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