2008-06-25 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / 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 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
9 // Copyright 2003-2008 Novell, Inc
10 //
11
12 using System;
13 using System.IO;
14 using System.Text;
15 using System.Reflection;
16 using System.Collections;
17 using System.Reflection.Emit;
18 using System.Globalization;
19
20 namespace Mono.CSharp {
21
22         public interface ParameterData {
23                 Type ParameterType (int pos);
24                 Type [] Types { get; }
25                 int  Count { get; }
26                 Type ExtensionMethodType { get; }
27                 bool HasParams { get; }
28                 string ParameterName (int pos);
29                 string ParameterDesc (int pos);
30
31                 Parameter.Modifier ParameterModifier (int pos);
32                 string GetSignatureForError ();
33
34 #if MS_COMPATIBLE
35                 ParameterData InflateTypes (Type[] genArguments, Type[] argTypes);
36 #endif
37         }
38
39         public class ReflectionParameters : ParameterData {
40                 ParameterInfo [] pi;
41                 Type [] types;
42                 int params_idx = int.MaxValue;
43                 bool is_varargs;
44                 bool is_extension;
45                 ParameterData gpd;
46
47                 public ReflectionParameters (MethodBase mb)
48                 {
49                         ParameterInfo [] pi = mb.GetParameters ();
50                         is_varargs = (mb.CallingConvention & CallingConventions.VarArgs) != 0;
51
52                         this.pi = pi;
53                         int count = pi.Length;
54
55                         if (count == 0) {
56                                 types = Type.EmptyTypes;
57                                 return;
58                         }
59
60                         types = new Type [count];
61                         for (int i = 0; i < count; i++)
62                                 types [i] = TypeManager.TypeToCoreType (pi [i].ParameterType);
63
64                         // TODO: This (if) should be done one level higher to correctly use
65                         // out caching facilities.
66                         MethodBase generic = TypeManager.DropGenericMethodArguments (mb);
67                         if (generic != mb) {
68                                 gpd = TypeManager.GetParameterData (generic);
69                                 if (gpd.HasParams) {
70                                         for (int i = gpd.Count; i != 0; --i) {
71                                                 if ((gpd.ParameterModifier (i-1) & Parameter.Modifier.PARAMS) != 0) {
72                                                         this.params_idx = i-1;
73                                                         break;
74                                                 }
75                                         }
76                                 }
77                                 return;
78                         }
79
80                         //
81                         // So far, the params attribute can be used in C# for the last
82                         // and next to last method parameters.
83                         // If some other language can place it anywhere we will
84                         // have to analyze all parameters and not just last 2.
85                         //
86                         --count;
87                         for (int i = count; i >= 0 && i > count - 2; --i) {
88                                 if (!pi [i].ParameterType.IsArray)
89                                         continue;
90
91                                 if (pi [i].IsDefined (TypeManager.param_array_type, false)) {
92                                         params_idx = i;
93                                         break;
94                                 }
95                         }
96
97                         if (TypeManager.extension_attribute_type != null && mb.IsStatic &&
98                                 (mb.DeclaringType.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute &&
99                                 mb.IsDefined (TypeManager.extension_attribute_type, false))
100                                 is_extension = true;
101                 }
102
103                 public override bool Equals (object obj)
104                 {
105                         ReflectionParameters rp = obj as ReflectionParameters;
106                         if (rp == null)
107                                 return false;
108
109                         if (Count != rp.Count)
110                                 return false;
111
112                         for (int i = 0; i < Count; ++i) {
113                         if (!types [i].Equals (rp.types [i]))
114                                 return false;
115                         }
116                         return true;
117                 }
118
119                 public override int GetHashCode ()
120                 {
121                         return base.GetHashCode ();
122                 }
123
124                 public string GetSignatureForError ()
125                 {
126                         StringBuilder sb = new StringBuilder ("(");
127                         for (int i = 0; i < pi.Length; ++i) {
128                                 if (i != 0)
129                                         sb.Append (", ");
130                                 sb.Append (ParameterDesc (i));
131                         }
132                         if (is_varargs) {
133                                 if (pi.Length > 0)
134                                         sb.Append (", ");
135                                 sb.Append ("__arglist");
136                         }
137                         sb.Append (')');
138                         return sb.ToString ();
139                 }
140
141 #if MS_COMPATIBLE
142                 public ParameterData InflateTypes (Type[] genArguments, Type[] argTypes)
143                 {
144                         ReflectionParameters p = (ReflectionParameters)MemberwiseClone ();
145
146                         for (int i = 0; i < types.Length; ++i) {
147                                 if (types[i].IsGenericParameter) {
148                                         for (int ii = 0; ii < genArguments.Length; ++ii) {
149                                                 if (types[i] != genArguments[ii])
150                                                         continue;
151
152                                                 p.types[i] = argTypes[ii];
153                                                 break;
154                                         }
155                                         continue;
156                                 }
157                                 
158                                 if (types[i].IsGenericType) {
159                                         Type[] gen_arguments_open = types[i].GetGenericTypeDefinition ().GetGenericArguments ();
160                                         Type[] gen_arguments = types[i].GetGenericArguments ();
161                                         for (int ii = 0; ii < gen_arguments_open.Length; ++ii) {
162                                                 if (gen_arguments [ii].IsGenericParameter) {
163                                                         for (int iii = 0; iii < genArguments.Length; ++iii) {
164                                                                 if (gen_arguments [ii] != genArguments [iii])
165                                                                         continue;
166
167                                                                 gen_arguments_open [ii] = argTypes [iii];
168                                                                 break;
169                                                         }
170                                                 } else {
171                                                         gen_arguments_open [ii] = gen_arguments [ii];
172                                                 }
173                                         }
174
175                                         p.types[i] = types[i].GetGenericTypeDefinition ().MakeGenericType (gen_arguments_open);
176                                 }
177                         }
178                         return p;
179                 }
180 #endif
181
182                 public Type ParameterType (int pos)
183                 {
184                         if (is_varargs && pos >= pi.Length)
185                                 return TypeManager.runtime_argument_handle_type;
186
187                         return types [pos];
188                 }
189
190                 public string ParameterName (int pos)
191                 {
192                         if (gpd != null)
193                                 return gpd.ParameterName (pos);
194
195                         if (is_varargs && pos >= pi.Length)
196                                 return "__arglist";
197
198                         return pi [pos].Name;
199                 }
200
201                 public string ParameterDesc (int pos)
202                 {
203                         if (is_varargs && pos >= pi.Length)
204                                 return "";
205
206                         StringBuilder sb = new StringBuilder ();
207
208                         if (pi [pos].IsIn)
209                                 sb.Append ("in ");
210
211                         Type partype = ParameterType (pos);
212                         if (partype.IsByRef){
213                                 partype = TypeManager.GetElementType (partype);
214                                 if (pi [pos].IsOut)
215                                         sb.Append ("out ");
216                                 else
217                                         sb.Append ("ref ");
218                         }
219
220                         if (params_idx == pos)
221                                 sb.Append ("params ");
222
223                         if (pos == 0 && ExtensionMethodType != null)
224                                 sb.Append ("this ");
225
226                         sb.Append (TypeManager.CSharpName (partype).Replace ("&", ""));
227
228                         return sb.ToString ();
229                 }
230
231                 public Parameter.Modifier ParameterModifier (int pos)
232                 {
233                         if (pos >= params_idx)
234                                 return Parameter.Modifier.PARAMS;
235                         if (is_varargs && pos >= pi.Length)
236                                 return Parameter.Modifier.ARGLIST;
237
238                         if (gpd != null)
239                                 return gpd.ParameterModifier (pos);
240
241                         Type t = types [pos];
242                         if (t.IsByRef){
243                                 if ((pi [pos].Attributes & (ParameterAttributes.Out|ParameterAttributes.In)) == ParameterAttributes.Out)
244                                         return Parameter.Modifier.OUT;
245                                 else
246                                         return Parameter.Modifier.REF;
247                         }
248
249                         return Parameter.Modifier.NONE;
250                 }
251
252                 public int Count {
253                         get { return is_varargs ? pi.Length + 1 : pi.Length; }
254                 }
255
256                 public Type ExtensionMethodType {
257                         get {
258                                 if (!is_extension)
259                                         return null;
260
261                                 return types [0];
262                         }
263                 }
264
265                 public bool HasParams {
266                         get { return params_idx != int.MaxValue; }
267                 }
268
269                 public Type[] Types {
270                         get { return types; }
271                 }
272         }
273
274 #if GMCS_SOURCE
275         public class ReflectionConstraints : GenericConstraints
276         {
277                 GenericParameterAttributes attrs;
278                 Type base_type;
279                 Type class_constraint;
280                 Type[] iface_constraints;
281                 string name;
282
283                 public static GenericConstraints GetConstraints (Type t)
284                 {
285                         Type [] constraints = t.GetGenericParameterConstraints ();
286                         GenericParameterAttributes attrs = t.GenericParameterAttributes;
287                         if (constraints.Length == 0 && attrs == GenericParameterAttributes.None)
288                                 return null;
289                         return new ReflectionConstraints (t.Name, constraints, attrs);
290                 }
291
292                 private ReflectionConstraints (string name, Type [] constraints, GenericParameterAttributes attrs)
293                 {
294                         this.name = name;
295                         this.attrs = attrs;
296
297                         if ((constraints.Length > 0) && !constraints [0].IsInterface) {
298                                 class_constraint = constraints [0];
299                                 iface_constraints = new Type [constraints.Length - 1];
300                                 Array.Copy (constraints, 1, iface_constraints, 0, constraints.Length - 1);
301                         } else
302                                 iface_constraints = constraints;
303
304                         if (HasValueTypeConstraint)
305                                 base_type = TypeManager.value_type;
306                         else if (class_constraint != null)
307                                 base_type = class_constraint;
308                         else
309                                 base_type = TypeManager.object_type;
310                 }
311
312                 public override string TypeParameter {
313                         get { return name; }
314                 }
315
316                 public override GenericParameterAttributes Attributes {
317                         get { return attrs; }
318                 }
319
320                 public override Type ClassConstraint {
321                         get { return class_constraint; }
322                 }
323
324                 public override Type EffectiveBaseClass {
325                         get { return base_type; }
326                 }
327
328                 public override Type[] InterfaceConstraints {
329                         get { return iface_constraints; }
330                 }
331         }
332 #endif
333
334         class PtrHashtable : Hashtable {
335                 sealed class PtrComparer : IComparer {
336                         private PtrComparer () {}
337
338                         public static PtrComparer Instance = new PtrComparer ();
339
340                         public int Compare (object x, object y)
341                         {
342                                 if (x == y)
343                                         return 0;
344                                 else
345                                         return 1;
346                         }
347                 }
348
349                 public PtrHashtable ()
350                 {
351                         comparer = PtrComparer.Instance;
352                 }
353
354 #if MS_COMPATIBLE
355                 //
356                 // Workaround System.InvalidOperationException for enums
357                 //
358                 protected override int GetHash (object key)
359                 {
360                         TypeBuilder tb = key as TypeBuilder;
361                         if (tb != null && tb.BaseType == TypeManager.enum_type)
362                                 key = tb.BaseType;
363
364                         return base.GetHash (key);
365                 }
366 #endif
367         }
368
369         /*
370          * Hashtable whose keys are character arrays with the same length
371          */
372         class CharArrayHashtable : Hashtable {
373                 sealed class ArrComparer : IComparer {
374                         private int len;
375
376                         public ArrComparer (int len) {
377                                 this.len = len;
378                         }
379
380                         public int Compare (object x, object y)
381                         {
382                                 char[] a = (char[])x;
383                                 char[] b = (char[])y;
384
385                                 for (int i = 0; i < len; ++i)
386                                         if (a [i] != b [i])
387                                                 return 1;
388                                 return 0;
389                         }
390                 }
391
392                 private int len;
393
394                 protected override int GetHash (Object key)
395                 {
396                         char[] arr = (char[])key;
397                         int h = 0;
398
399                         for (int i = 0; i < len; ++i)
400                                 h = (h << 5) - h + arr [i];
401
402                         return h;
403                 }
404
405                 public CharArrayHashtable (int len)
406                 {
407                         this.len = len;
408                         comparer = new ArrComparer (len);
409                 }
410         }
411
412         struct Pair {
413                 public object First;
414                 public object Second;
415
416                 public Pair (object f, object s)
417                 {
418                         First = f;
419                         Second = s;
420                 }
421         }
422
423         public class Accessors {
424                 public Accessor get_or_add;
425                 public Accessor set_or_remove;
426
427                 // was 'set' declared before 'get'?  was 'remove' declared before 'add'?
428                 public bool declared_in_reverse;
429
430                 public Accessors (Accessor get_or_add, Accessor set_or_remove)
431                 {
432                         this.get_or_add = get_or_add;
433                         this.set_or_remove = set_or_remove;
434                 }
435         }
436
437         /// <summary>
438         ///   This is a wrapper around StreamReader which is seekable backwards
439         ///   within a window of around 2048 chars.
440         /// </summary>
441         public class SeekableStreamReader
442         {
443                 const int AverageReadLength = 1024;
444                 TextReader reader;
445                 Stream stream;
446                 Encoding encoding;
447
448                 char[] buffer;
449                 int buffer_start;       // in chars
450                 int char_count;         // count buffer[] valid characters
451                 int pos;                // index into buffer[]
452
453                 public SeekableStreamReader (Stream stream, Encoding encoding)
454                 {
455                         this.stream = stream;
456                         this.encoding = encoding;
457                         
458                         this.reader = new StreamReader (stream, encoding, true);
459                         this.buffer = new char [AverageReadLength * 3];
460
461                         // Let the StreamWriter autodetect the encoder
462                         reader.Peek ();
463                 }
464
465                 /// <remarks>
466                 ///   This value corresponds to the current position in a stream of characters.
467                 ///   The StreamReader hides its manipulation of the underlying byte stream and all
468                 ///   character set/decoding issues.  Thus, we cannot use this position to guess at
469                 ///   the corresponding position in the underlying byte stream even though there is
470                 ///   a correlation between them.
471                 /// </remarks>
472                 public int Position {
473                         get { return buffer_start + pos; }
474
475                         set {
476                                 if (value > buffer_start + char_count)
477                                         throw new InternalErrorException ("can't seek that far forward: " + (pos - value));
478                                 
479                                 if (value < buffer_start){
480                                         // Reinitialize.
481                                         stream.Position = 0;
482                                         reader = new StreamReader (stream, encoding, true);
483                                         buffer_start = 0;
484                                         char_count = 0;
485                                         pos = 0;
486                                         Peek ();
487
488                                         while (value > buffer_start + char_count){
489                                                 pos = char_count+1;
490                                                 Peek ();
491                                         }
492                                         pos = value - buffer_start;
493                                 }
494
495                                 pos = value - buffer_start;
496                         }
497                 }
498
499                 private bool ReadBuffer ()
500                 {
501                         int slack = buffer.Length - char_count;
502                         if (slack <= AverageReadLength / 2) {
503                                 // shift the buffer to make room for AverageReadLength number of characters
504                                 int shift = AverageReadLength - slack;
505                                 Array.Copy (buffer, shift, buffer, 0, char_count - shift);
506                                 pos -= shift;
507                                 char_count -= shift;
508                                 buffer_start += shift;
509                                 slack += shift;         // slack == AverageReadLength
510                         }
511
512                         int chars_read = reader.Read (buffer, char_count, slack);
513                         char_count += chars_read;
514
515                         return pos < char_count;
516                 }
517
518                 public int Peek ()
519                 {
520                         if ((pos >= char_count) && !ReadBuffer ())
521                                 return -1;
522
523                         return buffer [pos];
524                 }
525
526                 public int Read ()
527                 {
528                         if ((pos >= char_count) && !ReadBuffer ())
529                                 return -1;
530
531                         return buffer [pos++];
532                 }
533         }
534
535         public class DoubleHash {
536                 const int DEFAULT_INITIAL_BUCKETS = 100;
537
538                 public DoubleHash () : this (DEFAULT_INITIAL_BUCKETS) {}
539
540                 public DoubleHash (int size)
541                 {
542                         count = size;
543                         buckets = new Entry [size];
544                 }
545
546                 int count;
547                 Entry [] buckets;
548                 int size = 0;
549
550                 class Entry {
551                         public object key1;
552                         public object key2;
553                         public int hash;
554                         public object value;
555                         public Entry next;
556
557                         public Entry (object key1, object key2, int hash, object value, Entry next)
558                         {
559                                 this.key1 = key1;
560                                 this.key2 = key2;
561                                 this.hash = hash;
562                                 this.next = next;
563                                 this.value = value;
564                         }
565                 }
566
567                 public bool Lookup (object a, object b, out object res)
568                 {
569                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
570
571                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
572                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b)) {
573                                         res = e.value;
574                                         return true;
575                                 }
576                         }
577                         res = null;
578                         return false;
579                 }
580
581                 public void Insert (object a, object b, object value)
582                 {
583                         // Is it an existing one?
584
585                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
586
587                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
588                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b))
589                                         e.value = value;
590                         }
591
592                         int bucket = h % count;
593                         buckets [bucket] = new Entry (a, b, h, value, buckets [bucket]);
594
595                         // Grow whenever we double in size
596                         if (size++ == count) {
597                                 count <<= 1;
598                                 count ++;
599
600                                 Entry [] newBuckets = new Entry [count];
601                                 foreach (Entry root in buckets) {
602                                         Entry e = root;
603                                         while (e != null) {
604                                                 int newLoc = e.hash % count;
605                                                 Entry n = e.next;
606                                                 e.next = newBuckets [newLoc];
607                                                 newBuckets [newLoc] = e;
608                                                 e = n;
609                                         }
610                                 }
611
612                                 buckets = newBuckets;
613                         }
614                 }
615         }
616
617         class PartialMethodDefinitionInfo : MethodInfo
618         {
619                 MethodOrOperator mc;
620                 MethodAttributes attrs;
621
622                 public PartialMethodDefinitionInfo (MethodOrOperator mc)
623                 {
624                         this.mc = mc;
625                         if ((mc.ModFlags & Modifiers.STATIC) != 0)
626                                 attrs = MethodAttributes.Static;
627                 }
628
629                 public override MethodInfo GetBaseDefinition ()
630                 {
631                         throw new NotImplementedException ();
632                 }
633
634                 public override ICustomAttributeProvider ReturnTypeCustomAttributes
635                 {
636                         get { throw new NotImplementedException (); }
637                 }
638
639                 public override MethodAttributes Attributes
640                 {
641                         get { return attrs; }
642                 }
643
644                 public override MethodImplAttributes GetMethodImplementationFlags ()
645                 {
646                         throw new NotImplementedException ();
647                 }
648
649                 public override ParameterInfo [] GetParameters ()
650                 {
651                         throw new NotImplementedException ();
652                 }
653
654                 public override object Invoke (object obj, BindingFlags invokeAttr, Binder binder, object [] parameters, CultureInfo culture)
655                 {
656                         throw new NotImplementedException ();
657                 }
658
659                 public override RuntimeMethodHandle MethodHandle
660                 {
661                         get { throw new NotImplementedException (); }
662                 }
663
664                 public override Type DeclaringType
665                 {
666                         get { return mc.Parent.TypeBuilder; }
667                 }
668
669                 public override object [] GetCustomAttributes (Type attributeType, bool inherit)
670                 {
671                         throw new NotImplementedException ();
672                 }
673
674                 public override object [] GetCustomAttributes (bool inherit)
675                 {
676                         throw new NotImplementedException ();
677                 }
678
679                 public override Type ReturnType {
680                         get {
681                                 return mc.MemberType;
682                         }
683                 }
684
685                 public override bool IsDefined (Type attributeType, bool inherit)
686                 {
687                         throw new NotImplementedException ();
688                 }
689
690                 public override string Name
691                 {
692                         get { return mc.Name; }
693                 }
694
695                 public override Type ReflectedType
696                 {
697                         get { throw new NotImplementedException (); }
698                 }
699         }
700
701 }