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