**** Merged r49131 from MCS ****
[mono.git] / mcs / gmcs / support.cs
1 //
2 // support.cs: Support routines to work around the fact that System.Reflection.Emit
3 // can not introspect types that are being constructed
4 //
5 // Author:
6 //   Miguel de Icaza (miguel@ximian.com)
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10
11 using System;
12 using System.IO;
13 using System.Text;
14 using System.Reflection;
15 using System.Collections;
16 using System.Reflection.Emit;
17 using System.Globalization;
18
19 namespace Mono.CSharp {
20
21         public interface ParameterData {
22                 Type ParameterType (int pos);
23                 GenericConstraints GenericConstraints (int pos);
24                 int  Count { get; }
25                 bool HasParams { get; }
26                 string ParameterName (int pos);
27                 string ParameterDesc (int pos);
28                 Parameter.Modifier ParameterModifier (int pos);
29                 string GetSignatureForError ();
30         }
31
32         public class ReflectionParameters : ParameterData {
33                 ParameterInfo [] pi;
34                 Type[] type_params;
35                 bool last_arg_is_params = false;
36                 bool is_varargs = false;
37                 ParameterData gpd;
38
39                 public ReflectionParameters (MethodBase mb)
40                 {
41                         object [] attrs;
42
43                         ParameterInfo [] pi = mb.GetParameters ();
44                         is_varargs = (mb.CallingConvention & CallingConventions.VarArgs) != 0;
45
46                         this.pi = pi;
47                         int count = pi.Length-1;
48
49                         if (count < 0)
50                                 return;
51
52                         if (mb.Mono_IsInflatedMethod) {
53                                 MethodInfo generic = mb.GetGenericMethodDefinition ();
54                                 gpd = TypeManager.GetParameterData (generic);
55
56                                 last_arg_is_params = gpd.HasParams;
57                                 return;
58                         }
59
60                         if (mb.IsGenericMethodDefinition)
61                                 type_params = mb.GetGenericArguments ();
62
63                         attrs = pi [count].GetCustomAttributes (TypeManager.param_array_type, true);
64                         if (attrs == null)
65                                 return;
66
67                         if (attrs.Length == 0)
68                                 return;
69
70                         last_arg_is_params = true;
71                 }
72                 
73                 public string GetSignatureForError ()
74                 {
75                         StringBuilder sb = new StringBuilder ("(");
76                         for (int i = 0; i < pi.Length; ++i) {
77                                 if (i != 0)
78                                         sb.Append (", ");
79                                 sb.Append (ParameterDesc (i));
80                         }
81                         sb.Append (')');
82                         return sb.ToString ();
83                 }
84
85                 public Type ParameterType (int pos)
86                 {
87                         if (last_arg_is_params && pos >= pi.Length - 1)
88                                 return pi [pi.Length - 1].ParameterType;
89                         else if (is_varargs && pos >= pi.Length)
90                                 return TypeManager.runtime_argument_handle_type;
91                         else {
92                                 Type t = pi [pos].ParameterType;
93
94                                 return t;
95                         }
96                 }
97
98                 public GenericConstraints GenericConstraints (int pos)
99                 {
100                         if (gpd != null)
101                                 return gpd.GenericConstraints (pos);
102
103                         if (type_params == null)
104                                 return null;
105
106                         return new ReflectionConstraints (type_params [pos]);
107                 }
108
109                 public string ParameterName (int pos)
110                 {
111                         if (gpd != null)
112                                 return gpd.ParameterName (pos);
113
114                         if (last_arg_is_params && pos >= pi.Length - 1)
115                                 return pi [pi.Length - 1].Name;
116                         else if (is_varargs && pos >= pi.Length)
117                                 return "__arglist";
118                         else 
119                                 return pi [pos].Name;
120                 }
121
122                 public string ParameterDesc (int pos)
123                 {
124                         if (is_varargs && pos >= pi.Length)
125                                 return "";                      
126
127                         StringBuilder sb = new StringBuilder ();
128
129                         if (pi [pos].IsIn)
130                                 sb.Append ("in ");
131
132                         Type partype = ParameterType (pos);
133                         if (partype.IsByRef){
134                                 partype = TypeManager.GetElementType (partype);
135                                 if (pi [pos].IsOut)
136                                         sb.Append ("out ");
137                                 else
138                                         sb.Append ("ref ");
139                         } 
140
141                         if (pos >= pi.Length - 1 && last_arg_is_params)
142                                 sb.Append ("params ");
143                         
144                         sb.Append (TypeManager.CSharpName (partype).Replace ("&", ""));
145
146                         return sb.ToString ();
147                         
148                 }
149
150                 public Parameter.Modifier ParameterModifier (int pos)
151                 {
152                         if (last_arg_is_params && pos >= pi.Length - 1)
153                                         return Parameter.Modifier.PARAMS;
154                         else if (is_varargs && pos >= pi.Length)
155                                 return Parameter.Modifier.ARGLIST;
156                         
157                         if (gpd != null)
158                                 return gpd.ParameterModifier (pos);
159
160                         Type t = pi [pos].ParameterType;
161                         if (t.IsByRef){
162                                 if ((pi [pos].Attributes & (ParameterAttributes.Out|ParameterAttributes.In)) == ParameterAttributes.Out)
163                                         return Parameter.Modifier.ISBYREF | Parameter.Modifier.OUT;
164                                 else
165                                         return Parameter.Modifier.ISBYREF | Parameter.Modifier.REF;
166                         }
167                         
168                         return Parameter.Modifier.NONE;
169                 }
170
171                 public int Count {
172                         get {
173                                 return is_varargs ? pi.Length + 1 : pi.Length;
174                         }
175                 }
176
177                 public bool HasParams {
178                         get {
179                                 return this.last_arg_is_params;
180                         }
181                 }
182         }
183
184         public class InternalParameters : ParameterData {
185                 Type [] param_types;
186                 bool has_varargs;
187                 int count;
188
189                 public readonly Parameters Parameters;
190                 public readonly TypeParameter[] TypeParameters;
191                 
192                 public InternalParameters (Type [] param_types, Parameters parameters)
193                 {
194                         this.param_types = param_types;
195                         this.Parameters = parameters;
196
197                         has_varargs = parameters.HasArglist;
198
199                         if (param_types == null)
200                                 count = 0;
201                         else
202                                 count = param_types.Length;
203                 }
204
205                 public InternalParameters (Type [] param_types, Parameters parameters,
206                                            TypeParameter [] type_params)
207                         : this (param_types, parameters)
208                 {
209                         this.TypeParameters = type_params;
210                 }
211
212                 public int Count {
213                         get {
214                                 return has_varargs ? count + 1 : count;
215                         }
216                 }
217
218                 public bool HasParams {
219                         get {
220                                 return Parameters.ArrayParameter != null;
221                         }
222                 }
223
224                 Parameter GetParameter (int pos)
225                 {
226                         Parameter [] fixed_pars = Parameters.FixedParameters;
227                         if (fixed_pars != null){
228                                 int len = fixed_pars.Length;
229                                 if (pos < len)
230                                         return Parameters.FixedParameters [pos];
231                         }
232
233                         return Parameters.ArrayParameter;
234                 }
235
236                 public string GetSignatureForError ()
237                 {
238                         StringBuilder sb = new StringBuilder ("(");
239                         for (int i = 0; i < count; ++i) {
240                                 if (i != 0)
241                                         sb.Append (", ");
242                                 sb.Append (ParameterDesc (i));
243                         }
244                         sb.Append (')');
245                         return sb.ToString ();
246                 }
247
248                 public Type ParameterType (int pos)
249                 {
250                         if (has_varargs && pos >= count)
251                                 return TypeManager.runtime_argument_handle_type;
252
253                         if (param_types == null)
254                                 return null;
255
256                         return GetParameter (pos).ExternalType ();
257                 }
258
259                 public GenericConstraints GenericConstraints (int pos)
260                 {
261                         if (TypeParameters == null)
262                                 return null;
263
264                         return TypeParameters [pos].Constraints;
265                 }
266
267                 public string ParameterName (int pos)
268                 {
269                         if (has_varargs && pos >= count)
270                                 return "__arglist";
271
272                         return GetParameter (pos).Name;
273                 }
274
275                 public string ParameterDesc (int pos)
276                 {
277                         if (has_varargs && pos >= count)
278                                 return "__arglist";
279
280                         Type t = ParameterType (pos);
281                         return (ModifierDesc (pos) + " " + TypeManager.CSharpName (t).Replace ("&", "")).TrimStart ();
282                 }
283
284                 public string ModifierDesc (int pos)
285                 {
286                         Parameter p = GetParameter (pos);
287
288                         //
289                         // We need to and for REF/OUT, because if either is set the
290                         // extra flag ISBYREF will be set as well
291                         //
292                         if ((p.ModFlags & Parameter.Modifier.REF) != 0)
293                                 return "ref";
294                         if ((p.ModFlags & Parameter.Modifier.OUT) != 0)
295                                 return "out";
296                         if (p.ModFlags == Parameter.Modifier.PARAMS)
297                                 return "params";
298
299                         return "";
300                 }
301
302                 public Parameter.Modifier ParameterModifier (int pos)
303                 {
304                         if (has_varargs && pos >= count)
305                                 return Parameter.Modifier.ARGLIST;
306
307                         Parameter.Modifier mod = GetParameter (pos).ModFlags;
308
309                         if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0)
310                                 mod |= Parameter.Modifier.ISBYREF;
311
312                         return mod;
313                 }
314                 
315         }
316
317         public class ReflectionConstraints : GenericConstraints
318         {
319                 GenericParameterAttributes attrs;
320                 Type base_type;
321                 Type class_constraint;
322                 Type[] iface_constraints;
323
324                 public ReflectionConstraints (Type t)
325                 {
326                         Type[] constraints = t.GetGenericParameterConstraints ();
327                         if ((constraints.Length > 0) && !constraints [0].IsInterface) {
328                                 class_constraint = constraints [0];
329                                 iface_constraints = new Type [constraints.Length - 1];
330                                 Array.Copy (constraints, 1, iface_constraints, 0, constraints.Length - 1);
331                         } else
332                                 iface_constraints = constraints;
333                         attrs = t.GenericParameterAttributes;
334
335                         if (HasValueTypeConstraint)
336                                 base_type = TypeManager.value_type;
337                         else if (class_constraint != null)
338                                 base_type = class_constraint;
339                         else
340                                 base_type = TypeManager.object_type;
341                 }
342
343                 public override GenericParameterAttributes Attributes {
344                         get { return attrs; }
345                 }
346
347                 public override Type ClassConstraint {
348                         get { return class_constraint; }
349                 }
350
351                 public override Type EffectiveBaseClass {
352                         get { return base_type; }
353                 }
354
355                 public override Type[] InterfaceConstraints {
356                         get { return iface_constraints; }
357                 }
358         }
359
360         class PtrHashtable : Hashtable {
361                 sealed class PtrComparer : IComparer {
362                         private PtrComparer () {}
363                         
364                         public static PtrComparer Instance = new PtrComparer ();
365                         
366                         public int Compare (object x, object y)
367                         {
368                                 if (x == y)
369                                         return 0;
370                                 else
371                                         return 1;
372                         }
373                 }
374                 
375                 public PtrHashtable ()
376                 {
377                         comparer = PtrComparer.Instance;
378                 }
379         }
380
381         /*
382          * Hashtable whose keys are character arrays with the same length
383          */
384         class CharArrayHashtable : Hashtable {
385                 sealed class ArrComparer : IComparer {
386                         private int len;
387
388                         public ArrComparer (int len) {
389                                 this.len = len;
390                         }
391
392                         public int Compare (object x, object y)
393                         {
394                                 char[] a = (char[])x;
395                                 char[] b = (char[])y;
396
397                                 for (int i = 0; i < len; ++i)
398                                         if (a [i] != b [i])
399                                                 return 1;
400                                 return 0;
401                         }
402                 }
403
404                 private int len;
405
406                 protected override int GetHash (Object key)
407                 {
408                         char[] arr = (char[])key;
409                         int h = 0;
410
411                         for (int i = 0; i < len; ++i)
412                                 h = (h << 5) - h + arr [i];
413
414                         return h;
415                 }
416
417                 public CharArrayHashtable (int len)
418                 {
419                         this.len = len;
420                         comparer = new ArrComparer (len);
421                 }
422         }                       
423
424         struct Pair {
425                 public object First;
426                 public object Second;
427                 
428                 public Pair (object f, object s)
429                 {
430                         First = f;
431                         Second = s;
432                 }
433         }
434
435         /// <summary>
436         ///   This is a wrapper around StreamReader which is seekable.
437         /// </summary>
438         public class SeekableStreamReader
439         {
440                 public SeekableStreamReader (StreamReader reader)
441                 {
442                         this.reader = reader;
443                         this.buffer = new char [DefaultCacheSize];
444                         
445                         // Compute the preamble size
446                         
447                         // Let the StreamWriter autodetect the encoder
448                         reader.Peek ();
449                         
450                         preamble_size = (int) reader.BaseStream.Position;
451                 }
452
453                 public SeekableStreamReader (Stream stream, Encoding encoding)
454                         : this (new StreamReader (stream, encoding, true))
455                 { }
456
457                 StreamReader reader;
458
459                 private const int DefaultCacheSize = 1024;
460
461                 char[] buffer;
462                 int buffer_start;       // in bytes
463                 int buffer_size;        // in bytes
464                 int char_count;         // count buffer[] valid characters
465                 int pos;                // index into buffer[]
466                 int preamble_size;
467
468                 /// <remarks>
469                 ///   The difference to the StreamReader's BaseStream.Position is that this one is reliable; ie. it
470                 //    always reports the correct position and if it's modified, it also takes care of the buffered data.
471                 /// </remarks>
472                 public int Position {
473                         get {
474                                 return buffer_start + reader.CurrentEncoding.GetByteCount (buffer, 0, pos);
475                         }
476
477                         set {
478                                 // This one is easy: we're modifying the position within our current
479                                 // buffer.
480                                 if ((value >= buffer_start) && (value < buffer_start + buffer_size)) {
481                                         int byte_offset = value - buffer_start;
482
483                                         // If we have an uni-byte encoding, 'pos' will be the same as 
484                                         // 'byte_offset'.  If we have a multi-byte encoding, 'byte_offset'
485                                         // can be bigger than the desired value of 'pos', but not smaller.
486                                         // (IOW, the number of characters <= number of bytes)
487                                         //
488                                         // So, we start pos off at byte_offset, and fix it up.
489                                         pos = byte_offset;
490                                         if (pos > char_count)
491                                                 pos = char_count;
492                                         while (reader.CurrentEncoding.GetByteCount (buffer, 0, pos) > byte_offset)
493                                                 --pos;
494                                         return;
495                                 }
496
497                                 if (value == 0) // Skip preamble
498                                         value = preamble_size;
499
500                                 // Ok, now we need to seek.
501                                 reader.DiscardBufferedData ();
502                                 reader.BaseStream.Position = buffer_start = value;
503                                 char_count = buffer_size = pos = 0;
504                         }
505                 }
506
507                 private bool ReadBuffer ()
508                 {
509                         pos = 0;
510                         buffer_start += buffer_size;
511                         char_count = reader.Read (buffer, 0, buffer.Length);
512                         buffer_size = reader.CurrentEncoding.GetByteCount (buffer, 0, char_count);
513                         return buffer_size > 0;
514                 }
515
516                 public int Peek ()
517                 {
518                         if ((pos >= char_count) && !ReadBuffer ())
519                                 return -1;
520
521                         return buffer [pos];
522                 }
523
524                 public int Read ()
525                 {
526                         if ((pos >= char_count) && !ReadBuffer ())
527                                 return -1;
528
529                         return buffer [pos++];
530                 }
531         }
532
533         public class DoubleHash {
534                 const int DEFAULT_INITIAL_BUCKETS = 100;
535                 
536                 public DoubleHash () : this (DEFAULT_INITIAL_BUCKETS) {}
537                 
538                 public DoubleHash (int size)
539                 {
540                         count = size;
541                         buckets = new Entry [size];
542                 }
543                 
544                 int count;
545                 Entry [] buckets;
546                 int size = 0;
547                 
548                 class Entry {
549                         public object key1;
550                         public object key2;
551                         public int hash;
552                         public object value;
553                         public Entry next;
554         
555                         public Entry (object key1, object key2, int hash, object value, Entry next)
556                         {
557                                 this.key1 = key1;
558                                 this.key2 = key2;
559                                 this.hash = hash;
560                                 this.next = next;
561                                 this.value = value;
562                         }
563                 }
564
565                 public bool Lookup (object a, object b, out object res)
566                 {
567                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
568                         
569                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
570                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b)) {
571                                         res = e.value;
572                                         return true;
573                                 }
574                         }
575                         res = null;
576                         return false;
577                 }
578
579                 public void Insert (object a, object b, object value)
580                 {
581                         // Is it an existing one?
582                 
583                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
584                         
585                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
586                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b))
587                                         e.value = value;
588                         }
589                         
590                         int bucket = h % count;
591                         buckets [bucket] = new Entry (a, b, h, value, buckets [bucket]);
592                         
593                         // Grow whenever we double in size
594                         if (size++ == count) {
595                                 count <<= 1;
596                                 count ++;
597                                 
598                                 Entry [] newBuckets = new Entry [count];
599                                 foreach (Entry root in buckets) {
600                                         Entry e = root;
601                                         while (e != null) {
602                                                 int newLoc = e.hash % count;
603                                                 Entry n = e.next;
604                                                 e.next = newBuckets [newLoc];
605                                                 newBuckets [newLoc] = e;
606                                                 e = n;
607                                         }
608                                 }
609
610                                 buckets = newBuckets;
611                         }
612                 }
613         }
614 }