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