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