2004-06-08 Sebastien Pouliot <sebastien@ximian.com>
[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 GenericConstraints {
22                 bool HasConstructor { get; }
23                 bool IsReferenceType { get; }
24                 bool IsValueType { get; }
25                 bool HasClassConstraint { get; }
26                 Type ClassConstraint { get; }
27                 Type[] InterfaceConstraints { get; }
28         }
29
30         public interface ParameterData {
31                 Type ParameterType (int pos);
32                 GenericConstraints GenericConstraints (int pos);
33                 bool HasArrayParameter { get; }
34                 int  Count { get; }
35                 string ParameterName (int pos);
36                 string ParameterDesc (int pos);
37                 Parameter.Modifier ParameterModifier (int pos);
38         }
39
40         public class ReflectionParameters : ParameterData {
41                 ParameterInfo [] pi;
42                 bool last_arg_is_params = false;
43                 ParameterData gpd;
44
45                 public ReflectionParameters (MethodBase method)
46                 {
47                         object [] attrs;
48
49                         this.pi = method.GetParameters ();
50                         int count = pi.Length-1;
51
52                         if (count < 0)
53                                 return;
54
55                         if (method.Mono_IsInflatedMethod) {
56                                 MethodInfo generic = method.GetGenericMethodDefinition ();
57                                 gpd = Invocation.GetParameterData (generic);
58
59                                 last_arg_is_params = gpd.HasArrayParameter;
60                                 return;
61                         }
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 bool HasArrayParameter {
74                         get { return last_arg_is_params; }
75                 }
76
77                 public Type ParameterType (int pos)
78                 {
79                         if (last_arg_is_params && pos >= pi.Length - 1)
80                                 return pi [pi.Length - 1].ParameterType;
81                         else {
82                                 Type t = pi [pos].ParameterType;
83
84                                 return t;
85                         }
86                 }
87
88                 public GenericConstraints GenericConstraints (int pos)
89                 {
90                         if (gpd != null)
91                                 return gpd.GenericConstraints (pos);
92
93                         Type t = ParameterType (pos);
94                         if (!t.IsGenericParameter)
95                                 return null;
96
97                         return ReflectionConstraints.Create (t);
98                 }
99
100                 public string ParameterName (int pos)
101                 {
102                         if (last_arg_is_params && pos >= pi.Length - 1)
103                                 return pi [pi.Length - 1].Name;
104                         else 
105                                 return pi [pos].Name;
106                 }
107
108                 public string ParameterDesc (int pos)
109                 {
110                         StringBuilder sb = new StringBuilder ();
111
112                         if (pi [pos].IsIn)
113                                 sb.Append ("in ");
114
115                         Type partype = ParameterType (pos);
116                         if (partype.IsByRef){
117                                 partype = TypeManager.GetElementType (partype);
118                                 if (pi [pos].IsOut)
119                                         sb.Append ("out ");
120                                 else
121                                         sb.Append ("ref ");
122                         } 
123
124                         if (pos >= pi.Length - 1 && last_arg_is_params)
125                                 sb.Append ("params ");
126                         
127                         sb.Append (TypeManager.CSharpName (partype));
128
129                         return sb.ToString ();
130                         
131                 }
132
133                 public Parameter.Modifier ParameterModifier (int pos)
134                 {
135                         int len = pi.Length;
136
137                         if (pos >= len - 1)
138                                 if (last_arg_is_params)
139                                         return Parameter.Modifier.PARAMS;
140                         
141                         Type t = pi [pos].ParameterType;
142                         if (t.IsByRef){
143                                 if ((pi [pos].Attributes & ParameterAttributes.Out) != 0)
144                                         return Parameter.Modifier.ISBYREF | Parameter.Modifier.OUT;
145                                 else
146                                         return Parameter.Modifier.ISBYREF | Parameter.Modifier.REF;
147                         }
148                         
149                         return Parameter.Modifier.NONE;
150                 }
151
152                 public int Count {
153                         get {
154                                 return pi.Length;
155                         }
156                 }
157
158                 protected class ReflectionConstraints : GenericConstraints
159                 {
160                         bool has_ctor;
161                         bool is_reference_type;
162                         bool is_value_type;
163                         Type class_constraint;
164                         Type[] iface_constraints;
165
166                         protected ReflectionConstraints (bool has_ctor, Type class_constr,
167                                                          Type[] iface_constrs)
168                         {
169                                 this.has_ctor = has_ctor;
170                                 this.class_constraint = class_constr;
171                                 this.iface_constraints = iface_constrs;
172
173                                 if (class_constraint != null) {
174                                         if (class_constraint == TypeManager.object_type)
175                                                 is_reference_type = true;
176                                         else if (class_constraint == TypeManager.value_type)
177                                                 is_value_type = true;
178                                 }
179                         }
180
181                         public static GenericConstraints Create (Type t)
182                         {
183                                 Type class_constr = null;
184                                 Type[] iface_constrs = t.GetInterfaces ();
185                                 if (iface_constrs ==  null)
186                                         iface_constrs = Type.EmptyTypes;
187                                 if (t.BaseType != TypeManager.object_type)
188                                         class_constr = t.BaseType;
189
190                                 return new ReflectionConstraints (
191                                         false, class_constr, iface_constrs);
192                         }
193
194                         public bool HasConstructor {
195                                 get { return has_ctor; }
196                         }
197
198                         public bool HasClassConstraint {
199                                 get { return class_constraint != null; }
200                         }
201
202                         public bool IsReferenceType {
203                                 get { return is_reference_type; }
204                         }
205
206                         public bool IsValueType {
207                                 get { return is_value_type; }
208                         }
209
210                         public Type ClassConstraint {
211                                 get { return class_constraint; }
212                         }
213
214                         public Type[] InterfaceConstraints {
215                                 get { return iface_constraints; }
216                         }
217                 }
218         }
219
220         public class InternalParameters : ParameterData {
221                 Type [] param_types;
222
223                 public readonly Parameters Parameters;
224                 
225                 public InternalParameters (Type [] param_types, Parameters parameters)
226                 {
227                         this.param_types = param_types;
228                         this.Parameters = parameters;
229                 }
230
231                 public InternalParameters (DeclSpace ds, Parameters parameters)
232                         : this (parameters.GetParameterInfo (ds), parameters)
233                 {
234                 }
235
236                 public int Count {
237                         get {
238                                 if (param_types == null)
239                                         return 0;
240
241                                 return param_types.Length;
242                         }
243                 }
244
245                 public bool HasArrayParameter {
246                         get { return Parameters.ArrayParameter != null; }
247                 }
248
249                 Parameter GetParameter (int pos)
250                 {
251                         Parameter [] fixed_pars = Parameters.FixedParameters;
252                         if (fixed_pars != null){
253                                 int len = fixed_pars.Length;
254                                 if (pos < len)
255                                         return Parameters.FixedParameters [pos];
256                         }
257
258                         return Parameters.ArrayParameter;
259                 }
260
261                 public Type ParameterType (int pos)
262                 {
263                         if (param_types == null)
264                                 return null;
265
266                         return GetParameter (pos).ExternalType ();
267                 }
268
269                 public GenericConstraints GenericConstraints (int pos)
270                 {
271                         if (param_types == null)
272                                 return null;
273
274                         return GetParameter (pos).GenericConstraints;
275                 }
276
277                 public string ParameterName (int pos)
278                 {
279                         return GetParameter (pos).Name;
280                 }
281
282                 public string ParameterDesc (int pos)
283                 {
284                         string tmp = String.Empty;
285                         Parameter p = GetParameter (pos);
286
287                         //
288                         // We need to and for REF/OUT, because if either is set the
289                         // extra flag ISBYREF will be set as well
290                         //
291                         if ((p.ModFlags & Parameter.Modifier.REF) != 0)
292                                 tmp = "ref ";
293                         else if ((p.ModFlags & Parameter.Modifier.OUT) != 0)
294                                 tmp = "out ";
295                         else if (p.ModFlags == Parameter.Modifier.PARAMS)
296                                 tmp = "params ";
297
298                         Type t = ParameterType (pos);
299
300                         return tmp + TypeManager.CSharpName (t);
301                 }
302
303                 public Parameter.Modifier ParameterModifier (int pos)
304                 {
305                         Parameter.Modifier mod = GetParameter (pos).ModFlags;
306
307                         if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0)
308                                 mod |= Parameter.Modifier.ISBYREF;
309
310                         return mod;
311                 }
312                 
313         }
314
315         class PtrHashtable : Hashtable {
316                 sealed class PtrComparer : IComparer {
317                         private PtrComparer () {}
318                         
319                         public static PtrComparer Instance = new PtrComparer ();
320                         
321                         public int Compare (object x, object y)
322                         {
323                                 if (x == y)
324                                         return 0;
325                                 else
326                                         return 1;
327                         }
328                 }
329                 
330                 public PtrHashtable ()
331                 {
332                         comparer = PtrComparer.Instance;
333                 }
334         }
335
336         /*
337          * Hashtable whose keys are character arrays with the same length
338          */
339         class CharArrayHashtable : Hashtable {
340                 sealed class ArrComparer : IComparer {
341                         private int len;
342
343                         public ArrComparer (int len) {
344                                 this.len = len;
345                         }
346
347                         public int Compare (object x, object y)
348                         {
349                                 char[] a = (char[])x;
350                                 char[] b = (char[])y;
351
352                                 for (int i = 0; i < len; ++i)
353                                         if (a [i] != b [i])
354                                                 return 1;
355                                 return 0;
356                         }
357                 }
358
359                 private int len;
360
361                 protected override int GetHash (Object key)
362                 {
363                         char[] arr = (char[])key;
364                         int h = 0;
365
366                         for (int i = 0; i < len; ++i)
367                                 h = (h << 5) - h + arr [i];
368
369                         return h;
370                 }
371
372                 public CharArrayHashtable (int len)
373                 {
374                         this.len = len;
375                         comparer = new ArrComparer (len);
376                 }
377         }                       
378
379         //
380         // Compares member infos based on their name and
381         // also allows one argument to be a string
382         //
383         class MemberInfoCompare : IComparer {
384
385                 public int Compare (object a, object b)
386                 {
387                         if (a == null || b == null){
388                                 Console.WriteLine ("Invalid information passed");
389                                 throw new Exception ();
390                         }
391                         
392                         if (a is string)
393                                 return String.Compare ((string) a, ((MemberInfo)b).Name, false, CultureInfo.InvariantCulture);
394
395                         if (b is string)
396                                 return String.Compare (((MemberInfo)a).Name, (string) b, false, CultureInfo.InvariantCulture);
397
398                         return String.Compare (((MemberInfo)a).Name, ((MemberInfo)b).Name, false, CultureInfo.InvariantCulture);
399                 }
400         }
401
402         struct Pair {
403                 public object First;
404                 public object Second;
405                 
406                 public Pair (object f, object s)
407                 {
408                         First = f;
409                         Second = s;
410                 }
411         }
412
413         /// <summary>
414         ///   This is a wrapper around StreamReader which is seekable.
415         /// </summary>
416         public class SeekableStreamReader
417         {
418                 public SeekableStreamReader (StreamReader reader)
419                 {
420                         this.reader = reader;
421                         this.buffer = new char [DefaultCacheSize];
422                         
423                         // Compute the preamble size
424                         
425                         // Let the StreamWriter autodetect the encoder
426                         reader.Peek ();
427                         
428                         reader.BaseStream.Position = 0;
429                         Encoding enc = reader.CurrentEncoding;
430                         // First of all, get at least a char
431                         
432                         byte[] auxb = new byte [50];
433                         int num_bytes = 0;
434                         int num_chars = 0;
435                         int br = 0;
436                         do {
437                                 br = reader.BaseStream.Read (auxb, num_bytes, auxb.Length - num_bytes);
438                                 num_bytes += br;
439                                 num_chars = enc.GetCharCount (auxb, 0, num_bytes);
440                         }
441                         while (num_chars == 0 && br > 0);
442                         
443                         if (num_chars != 0)
444                         {
445                                 // Now, check which bytes at the beginning have no effect in the
446                                 // char count
447                                 
448                                 int p = 0;
449                                 while (enc.GetCharCount (auxb, p, num_bytes-p) >= num_chars)
450                                         p++;
451                                 
452                                 preamble_size = p - 1;
453                                 reader.BaseStream.Position = 0;
454                                 reader.DiscardBufferedData ();
455                                 
456                                 buffer_start = preamble_size;
457                         }
458                 }
459
460                 public SeekableStreamReader (Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks)
461                         : this (new StreamReader (stream, encoding, detect_encoding_from_bytemarks))
462                 { }
463
464                 StreamReader reader;
465
466                 private const int DefaultCacheSize = 1024;
467
468                 char[] buffer;
469                 int buffer_start;       // in bytes
470                 int buffer_size;        // in bytes
471                 int char_count;         // count buffer[] valid characters
472                 int pos;                // index into buffer[]
473                 int preamble_size;
474
475                 /// <remarks>
476                 ///   The difference to the StreamReader's BaseStream.Position is that this one is reliable; ie. it
477                 //    always reports the correct position and if it's modified, it also takes care of the buffered data.
478                 /// </remarks>
479                 public int Position {
480                         get {
481                                 return buffer_start + reader.CurrentEncoding.GetByteCount (buffer, 0, pos);
482                         }
483
484                         set {
485                                 // This one is easy: we're modifying the position within our current
486                                 // buffer.
487                                 if ((value >= buffer_start) && (value < buffer_start + buffer_size)) {
488                                         int byte_offset = value - buffer_start;
489                                         pos = byte_offset;
490                                         // encoded characters can take more than 1 byte length
491                                         while (reader.CurrentEncoding.GetByteCount (buffer, 0, pos) > byte_offset)
492                                                 pos--;
493                                         
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 }