* parser.cs (Parser.ConsumeWhitespace): Add bounds check.
[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|ParameterAttributes.In)) == ParameterAttributes.Out)
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                         Type t = ParameterType (pos);
211                         return ModifierDesc (pos) + " " + TypeManager.CSharpName (t);
212                 }
213
214                 public string ModifierDesc (int pos)
215                 {
216                         Parameter p = GetParameter (pos);
217
218                         //
219                         // We need to and for REF/OUT, because if either is set the
220                         // extra flag ISBYREF will be set as well
221                         //
222                         if ((p.ModFlags & Parameter.Modifier.REF) != 0)
223                                 return "ref";
224                         if ((p.ModFlags & Parameter.Modifier.OUT) != 0)
225                                 return "out";
226                         if (p.ModFlags == Parameter.Modifier.PARAMS)
227                                 return "params";
228
229                         return "";
230                 }
231
232                 public Parameter.Modifier ParameterModifier (int pos)
233                 {
234                         if (has_varargs && pos >= count)
235                                 return Parameter.Modifier.ARGLIST;
236
237                         Parameter.Modifier mod = GetParameter (pos).ModFlags;
238
239                         if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0)
240                                 mod |= Parameter.Modifier.ISBYREF;
241
242                         return mod;
243                 }
244                 
245         }
246
247         class PtrHashtable : Hashtable {
248                 sealed class PtrComparer : IComparer {
249                         private PtrComparer () {}
250                         
251                         public static PtrComparer Instance = new PtrComparer ();
252                         
253                         public int Compare (object x, object y)
254                         {
255                                 if (x == y)
256                                         return 0;
257                                 else
258                                         return 1;
259                         }
260                 }
261                 
262                 public PtrHashtable ()
263                 {
264                         comparer = PtrComparer.Instance;
265                 }
266         }
267
268         /*
269          * Hashtable whose keys are character arrays with the same length
270          */
271         class CharArrayHashtable : Hashtable {
272                 sealed class ArrComparer : IComparer {
273                         private int len;
274
275                         public ArrComparer (int len) {
276                                 this.len = len;
277                         }
278
279                         public int Compare (object x, object y)
280                         {
281                                 char[] a = (char[])x;
282                                 char[] b = (char[])y;
283
284                                 for (int i = 0; i < len; ++i)
285                                         if (a [i] != b [i])
286                                                 return 1;
287                                 return 0;
288                         }
289                 }
290
291                 private int len;
292
293                 protected override int GetHash (Object key)
294                 {
295                         char[] arr = (char[])key;
296                         int h = 0;
297
298                         for (int i = 0; i < len; ++i)
299                                 h = (h << 5) - h + arr [i];
300
301                         return h;
302                 }
303
304                 public CharArrayHashtable (int len)
305                 {
306                         this.len = len;
307                         comparer = new ArrComparer (len);
308                 }
309         }                       
310
311         struct Pair {
312                 public object First;
313                 public object Second;
314                 
315                 public Pair (object f, object s)
316                 {
317                         First = f;
318                         Second = s;
319                 }
320         }
321
322         /// <summary>
323         ///   This is a wrapper around StreamReader which is seekable.
324         /// </summary>
325         public class SeekableStreamReader
326         {
327                 public SeekableStreamReader (StreamReader reader)
328                 {
329                         this.reader = reader;
330                         this.buffer = new char [DefaultCacheSize];
331                         
332                         // Compute the preamble size
333                         
334                         // Let the StreamWriter autodetect the encoder
335                         reader.Peek ();
336                         
337                         reader.BaseStream.Position = 0;
338                         Encoding enc = reader.CurrentEncoding;
339                         // First of all, get at least a char
340                         
341                         byte[] auxb = new byte [50];
342                         int num_bytes = 0;
343                         int num_chars = 0;
344                         int br = 0;
345                         do {
346                                 br = reader.BaseStream.Read (auxb, num_bytes, auxb.Length - num_bytes);
347                                 num_bytes += br;
348                                 num_chars = enc.GetCharCount (auxb, 0, num_bytes);
349                         }
350                         while (num_chars == 0 && br > 0);
351                         
352                         if (num_chars != 0)
353                         {
354                                 // Now, check which bytes at the beginning have no effect in the
355                                 // char count
356                                 
357                                 int p = 0;
358                                 while (enc.GetCharCount (auxb, p, num_bytes-p) >= num_chars)
359                                         p++;
360                                 
361                                 preamble_size = p - 1;
362                                 reader.BaseStream.Position = 0;
363                                 reader.DiscardBufferedData ();
364                                 
365                                 buffer_start = preamble_size;
366                         }
367                 }
368
369                 public SeekableStreamReader (Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks)
370                         : this (new StreamReader (stream, encoding, detect_encoding_from_bytemarks))
371                 { }
372
373                 StreamReader reader;
374
375                 private const int DefaultCacheSize = 1024;
376
377                 char[] buffer;
378                 int buffer_start;       // in bytes
379                 int buffer_size;        // in bytes
380                 int char_count;         // count buffer[] valid characters
381                 int pos;                // index into buffer[]
382                 int preamble_size;
383
384                 /// <remarks>
385                 ///   The difference to the StreamReader's BaseStream.Position is that this one is reliable; ie. it
386                 //    always reports the correct position and if it's modified, it also takes care of the buffered data.
387                 /// </remarks>
388                 public int Position {
389                         get {
390                                 return buffer_start + reader.CurrentEncoding.GetByteCount (buffer, 0, pos);
391                         }
392
393                         set {
394                                 // This one is easy: we're modifying the position within our current
395                                 // buffer.
396                                 if ((value >= buffer_start) && (value < buffer_start + buffer_size)) {
397                                         int byte_offset = value - buffer_start;
398
399                                         // pos is an index into a char
400                                         // buffer so it might be
401                                         // greater than the buffer
402                                         // length now, if the buffer
403                                         // contains multibyte chars
404                                         pos = byte_offset;
405                                         
406                                         // encoded characters can take
407                                         // more than 1 byte length.
408                                         while ((pos > buffer.Length) ||
409                                                reader.CurrentEncoding.GetByteCount (buffer, 0, pos) > byte_offset) {
410                                                 pos--;
411                                         }
412                                         
413                                         return;
414                                 }
415                                 
416                                 if (value == 0) // Skip preamble
417                                         value = preamble_size;
418
419                                 // Ok, now we need to seek.
420                                 reader.DiscardBufferedData ();
421                                 reader.BaseStream.Position = buffer_start = value;
422                                 char_count = buffer_size = pos = 0;
423                         }
424                 }
425
426                 private bool ReadBuffer ()
427                 {
428                         pos = 0;
429                         buffer_start += buffer_size;
430                         char_count = reader.Read (buffer, 0, buffer.Length);
431                         buffer_size = reader.CurrentEncoding.GetByteCount (buffer, 0, char_count);
432                         return buffer_size > 0;
433                 }
434
435                 public int Peek ()
436                 {
437                         if ((pos >= char_count) && !ReadBuffer ())
438                                 return -1;
439
440                         return buffer [pos];
441                 }
442
443                 public int Read ()
444                 {
445                         if ((pos >= char_count) && !ReadBuffer ())
446                                 return -1;
447
448                         return buffer [pos++];
449                 }
450         }
451
452         public class DoubleHash {
453                 const int DEFAULT_INITIAL_BUCKETS = 100;
454                 
455                 public DoubleHash () : this (DEFAULT_INITIAL_BUCKETS) {}
456                 
457                 public DoubleHash (int size)
458                 {
459                         count = size;
460                         buckets = new Entry [size];
461                 }
462                 
463                 int count;
464                 Entry [] buckets;
465                 int size = 0;
466                 
467                 class Entry {
468                         public object key1;
469                         public object key2;
470                         public int hash;
471                         public object value;
472                         public Entry next;
473         
474                         public Entry (object key1, object key2, int hash, object value, Entry next)
475                         {
476                                 this.key1 = key1;
477                                 this.key2 = key2;
478                                 this.hash = hash;
479                                 this.next = next;
480                                 this.value = value;
481                         }
482                 }
483
484                 public bool Lookup (object a, object b, out object res)
485                 {
486                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
487                         
488                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
489                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b)) {
490                                         res = e.value;
491                                         return true;
492                                 }
493                         }
494                         res = null;
495                         return false;
496                 }
497
498                 public void Insert (object a, object b, object value)
499                 {
500                         // Is it an existing one?
501                 
502                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
503                         
504                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
505                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b))
506                                         e.value = value;
507                         }
508                         
509                         int bucket = h % count;
510                         buckets [bucket] = new Entry (a, b, h, value, buckets [bucket]);
511                         
512                         // Grow whenever we double in size
513                         if (size++ == count) {
514                                 count <<= 1;
515                                 count ++;
516                                 
517                                 Entry [] newBuckets = new Entry [count];
518                                 foreach (Entry root in buckets) {
519                                         Entry e = root;
520                                         while (e != null) {
521                                                 int newLoc = e.hash % count;
522                                                 Entry n = e.next;
523                                                 e.next = newBuckets [newLoc];
524                                                 newBuckets [newLoc] = e;
525                                                 e = n;
526                                         }
527                                 }
528
529                                 buckets = newBuckets;
530                         }
531                 }
532         }
533 }