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