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