2005-07-14 Sureshkumar T <tsureshkumar@novell.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 // (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                         reader.BaseStream.Position = 0;
363                         Encoding enc = reader.CurrentEncoding;
364                         // First of all, get at least a char
365                         
366                         byte[] auxb = new byte [50];
367                         int num_bytes = 0;
368                         int num_chars = 0;
369                         int br = 0;
370                         do {
371                                 br = reader.BaseStream.Read (auxb, num_bytes, auxb.Length - num_bytes);
372                                 num_bytes += br;
373                                 num_chars = enc.GetCharCount (auxb, 0, num_bytes);
374                         }
375                         while (num_chars == 0 && br > 0);
376                         
377                         if (num_chars != 0)
378                         {
379                                 // Now, check which bytes at the beginning have no effect in the
380                                 // char count
381                                 
382                                 int p = 0;
383                                 while (enc.GetCharCount (auxb, p, num_bytes-p) >= num_chars)
384                                         p++;
385                                 
386                                 preamble_size = p - 1;
387                                 reader.BaseStream.Position = 0;
388                                 reader.DiscardBufferedData ();
389                                 
390                                 buffer_start = preamble_size;
391                         }
392                 }
393
394                 public SeekableStreamReader (Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks)
395                         : this (new StreamReader (stream, encoding, detect_encoding_from_bytemarks))
396                 { }
397
398                 StreamReader reader;
399
400                 private const int DefaultCacheSize = 1024;
401
402                 char[] buffer;
403                 int buffer_start;       // in bytes
404                 int buffer_size;        // in bytes
405                 int char_count;         // count buffer[] valid characters
406                 int pos;                // index into buffer[]
407                 int preamble_size;
408
409                 /// <remarks>
410                 ///   The difference to the StreamReader's BaseStream.Position is that this one is reliable; ie. it
411                 //    always reports the correct position and if it's modified, it also takes care of the buffered data.
412                 /// </remarks>
413                 public int Position {
414                         get {
415                                 return buffer_start + reader.CurrentEncoding.GetByteCount (buffer, 0, pos);
416                         }
417
418                         set {
419                                 // This one is easy: we're modifying the position within our current
420                                 // buffer.
421                                 if ((value >= buffer_start) && (value < buffer_start + buffer_size)) {
422                                         int byte_offset = value - buffer_start;
423
424                                         // pos is an index into a char
425                                         // buffer so it might be
426                                         // greater than the buffer
427                                         // length now, if the buffer
428                                         // contains multibyte chars
429                                         pos = byte_offset;
430                                         
431                                         // encoded characters can take
432                                         // more than 1 byte length.
433                                         while ((pos > buffer.Length) ||
434                                                reader.CurrentEncoding.GetByteCount (buffer, 0, pos) > byte_offset) {
435                                                 pos--;
436                                         }
437                                         
438                                         return;
439                                 }
440                                 
441                                 if (value == 0) // Skip preamble
442                                         value = preamble_size;
443
444                                 // Ok, now we need to seek.
445                                 reader.DiscardBufferedData ();
446                                 reader.BaseStream.Position = buffer_start = value;
447                                 char_count = buffer_size = pos = 0;
448                         }
449                 }
450
451                 private bool ReadBuffer ()
452                 {
453                         pos = 0;
454                         buffer_start += buffer_size;
455                         char_count = reader.Read (buffer, 0, buffer.Length);
456                         buffer_size = reader.CurrentEncoding.GetByteCount (buffer, 0, char_count);
457                         return buffer_size > 0;
458                 }
459
460                 public int Peek ()
461                 {
462                         if ((pos >= char_count) && !ReadBuffer ())
463                                 return -1;
464
465                         return buffer [pos];
466                 }
467
468                 public int Read ()
469                 {
470                         if ((pos >= char_count) && !ReadBuffer ())
471                                 return -1;
472
473                         return buffer [pos++];
474                 }
475         }
476
477         public class DoubleHash {
478                 const int DEFAULT_INITIAL_BUCKETS = 100;
479                 
480                 public DoubleHash () : this (DEFAULT_INITIAL_BUCKETS) {}
481                 
482                 public DoubleHash (int size)
483                 {
484                         count = size;
485                         buckets = new Entry [size];
486                 }
487                 
488                 int count;
489                 Entry [] buckets;
490                 int size = 0;
491                 
492                 class Entry {
493                         public object key1;
494                         public object key2;
495                         public int hash;
496                         public object value;
497                         public Entry next;
498         
499                         public Entry (object key1, object key2, int hash, object value, Entry next)
500                         {
501                                 this.key1 = key1;
502                                 this.key2 = key2;
503                                 this.hash = hash;
504                                 this.next = next;
505                                 this.value = value;
506                         }
507                 }
508
509                 public bool Lookup (object a, object b, out object res)
510                 {
511                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
512                         
513                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
514                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b)) {
515                                         res = e.value;
516                                         return true;
517                                 }
518                         }
519                         res = null;
520                         return false;
521                 }
522
523                 public void Insert (object a, object b, object value)
524                 {
525                         // Is it an existing one?
526                 
527                         int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
528                         
529                         for (Entry e = buckets [h % count]; e != null; e = e.next) {
530                                 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b))
531                                         e.value = value;
532                         }
533                         
534                         int bucket = h % count;
535                         buckets [bucket] = new Entry (a, b, h, value, buckets [bucket]);
536                         
537                         // Grow whenever we double in size
538                         if (size++ == count) {
539                                 count <<= 1;
540                                 count ++;
541                                 
542                                 Entry [] newBuckets = new Entry [count];
543                                 foreach (Entry root in buckets) {
544                                         Entry e = root;
545                                         while (e != null) {
546                                                 int newLoc = e.hash % count;
547                                                 Entry n = e.next;
548                                                 e.next = newBuckets [newLoc];
549                                                 newBuckets [newLoc] = e;
550                                                 e = n;
551                                         }
552                                 }
553
554                                 buckets = newBuckets;
555                         }
556                 }
557         }
558 }