2007-04-19 Chris Toshok <toshok@ximian.com>
[mcs.git] / mcs / support.cs
blob3fdd91b530648165dfee3a3ec98fd91098c693d3
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 //
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;
19 namespace Mono.CSharp {
21 public interface ParameterData {
22 Type ParameterType (int pos);
23 Type [] Types { get; }
24 int Count { get; }
25 Type ExtensionMethodType { get; }
26 bool HasParams { get; }
27 string ParameterName (int pos);
28 string ParameterDesc (int pos);
30 Parameter.Modifier ParameterModifier (int pos);
31 string GetSignatureForError ();
33 #if MS_COMPATIBLE
34 void InflateTypes (Type[] genArguments, Type[] argTypes);
35 #endif
38 public class ReflectionParameters : ParameterData {
39 ParameterInfo [] pi;
40 Type [] types;
41 int params_idx = -1;
42 bool is_varargs;
43 bool is_extension;
44 ParameterData gpd;
46 public ReflectionParameters (MethodBase mb)
48 ParameterInfo [] pi = mb.GetParameters ();
49 is_varargs = (mb.CallingConvention & CallingConventions.VarArgs) != 0;
51 this.pi = pi;
52 int count = pi.Length;
54 if (count == 0) {
55 types = Type.EmptyTypes;
56 return;
59 types = new Type [count];
60 for (int i = 0; i < count; i++)
61 types [i] = pi [i].ParameterType;
63 // TODO: This (if) should be done one level higher to correctly use
64 // out caching facilities.
65 MethodBase generic = TypeManager.DropGenericMethodArguments (mb);
66 if (generic != mb) {
67 gpd = TypeManager.GetParameterData (generic);
68 if (gpd.HasParams) {
69 for (int i = gpd.Count; i != 0; --i) {
70 if ((gpd.ParameterModifier (i-1) & Parameter.Modifier.PARAMS) != 0) {
71 this.params_idx = i-1;
72 break;
76 return;
80 // So far, the params attribute can be used in C# for the last
81 // and next to last method parameters.
82 // If some other language can place it anywhere we will
83 // have to analyze all parameters and not just last 2.
85 --count;
86 for (int i = count; i >= 0 && i > count - 2; --i) {
87 if (!pi [i].ParameterType.IsArray)
88 continue;
90 if (pi [i].IsDefined (TypeManager.param_array_type, false)) {
91 params_idx = i;
92 return;
96 if (TypeManager.extension_attribute_type != null && mb.IsStatic &&
97 (mb.DeclaringType.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute &&
98 mb.IsDefined (TypeManager.extension_attribute_type, false))
99 is_extension = true;
102 public override bool Equals (object obj)
104 ReflectionParameters rp = obj as ReflectionParameters;
105 if (rp == null)
106 return false;
108 if (Count != rp.Count)
109 return false;
111 for (int i = 0; i < Count; ++i) {
112 if (!types [i].Equals (rp.types [i]))
113 return false;
115 return true;
118 public override int GetHashCode ()
120 return base.GetHashCode ();
123 public string GetSignatureForError ()
125 StringBuilder sb = new StringBuilder ("(");
126 for (int i = 0; i < pi.Length; ++i) {
127 if (i != 0)
128 sb.Append (", ");
129 sb.Append (ParameterDesc (i));
131 if (is_varargs) {
132 if (pi.Length > 0)
133 sb.Append (", ");
134 sb.Append ("__arglist");
136 sb.Append (')');
137 return sb.ToString ();
140 #if MS_COMPATIBLE
141 public void InflateTypes (Type[] genArguments, Type[] argTypes)
143 for (int i = 0; i < types.Length; ++i) {
144 if (types[i].IsGenericParameter) {
145 for (int ii = 0; ii < genArguments.Length; ++ii) {
146 if (types[i] != genArguments[ii])
147 continue;
149 types[i] = argTypes[ii];
150 break;
152 continue;
155 if (types[i].IsGenericType) {
156 Type[] gen_arguments = types[i].GetGenericTypeDefinition ().GetGenericArguments ();
157 for (int ii = 0; ii < gen_arguments.Length; ++ii) {
158 gen_arguments[ii] = argTypes[gen_arguments[ii].GenericParameterPosition];
161 types[i] = types[i].GetGenericTypeDefinition ().MakeGenericType (gen_arguments);
165 #endif
167 public Type ParameterType (int pos)
169 if (is_varargs && pos >= pi.Length)
170 return TypeManager.runtime_argument_handle_type;
172 return types [pos];
175 public string ParameterName (int pos)
177 if (gpd != null)
178 return gpd.ParameterName (pos);
180 if (is_varargs && pos >= pi.Length)
181 return "__arglist";
183 return pi [pos].Name;
186 public string ParameterDesc (int pos)
188 if (is_varargs && pos >= pi.Length)
189 return "";
191 StringBuilder sb = new StringBuilder ();
193 if (pi [pos].IsIn)
194 sb.Append ("in ");
196 Type partype = ParameterType (pos);
197 if (partype.IsByRef){
198 partype = TypeManager.GetElementType (partype);
199 if (pi [pos].IsOut)
200 sb.Append ("out ");
201 else
202 sb.Append ("ref ");
205 if (params_idx == pos)
206 sb.Append ("params ");
208 if (ExtensionMethodType != null)
209 sb.Append ("this ");
211 sb.Append (TypeManager.CSharpName (partype).Replace ("&", ""));
213 return sb.ToString ();
216 public Parameter.Modifier ParameterModifier (int pos)
218 if (pos == params_idx)
219 return Parameter.Modifier.PARAMS;
220 else if (is_varargs && pos >= pi.Length)
221 return Parameter.Modifier.ARGLIST;
223 if (gpd != null)
224 return gpd.ParameterModifier (pos);
226 Type t = types [pos];
227 if (t.IsByRef){
228 if ((pi [pos].Attributes & (ParameterAttributes.Out|ParameterAttributes.In)) == ParameterAttributes.Out)
229 return Parameter.Modifier.OUT;
230 else
231 return Parameter.Modifier.REF;
234 return Parameter.Modifier.NONE;
237 public int Count {
238 get { return is_varargs ? pi.Length + 1 : pi.Length; }
241 public Type ExtensionMethodType {
242 get {
243 if (!is_extension)
244 return null;
246 return types [0];
250 public bool HasParams {
251 get { return params_idx != -1; }
254 public Type[] Types {
255 get { return types; }
259 #if GMCS_SOURCE
260 public class ReflectionConstraints : GenericConstraints
262 GenericParameterAttributes attrs;
263 Type base_type;
264 Type class_constraint;
265 Type[] iface_constraints;
266 string name;
268 public static GenericConstraints GetConstraints (Type t)
270 Type [] constraints = t.GetGenericParameterConstraints ();
271 GenericParameterAttributes attrs = t.GenericParameterAttributes;
272 if (constraints.Length == 0 && attrs == GenericParameterAttributes.None)
273 return null;
274 return new ReflectionConstraints (t.Name, constraints, attrs);
277 private ReflectionConstraints (string name, Type [] constraints, GenericParameterAttributes attrs)
279 this.name = name;
280 this.attrs = attrs;
282 if ((constraints.Length > 0) && !constraints [0].IsInterface) {
283 class_constraint = constraints [0];
284 iface_constraints = new Type [constraints.Length - 1];
285 Array.Copy (constraints, 1, iface_constraints, 0, constraints.Length - 1);
286 } else
287 iface_constraints = constraints;
289 if (HasValueTypeConstraint)
290 base_type = TypeManager.value_type;
291 else if (class_constraint != null)
292 base_type = class_constraint;
293 else
294 base_type = TypeManager.object_type;
297 public override string TypeParameter {
298 get { return name; }
301 public override GenericParameterAttributes Attributes {
302 get { return attrs; }
305 public override Type ClassConstraint {
306 get { return class_constraint; }
309 public override Type EffectiveBaseClass {
310 get { return base_type; }
313 public override Type[] InterfaceConstraints {
314 get { return iface_constraints; }
317 #endif
319 class PtrHashtable : Hashtable {
320 sealed class PtrComparer : IComparer {
321 private PtrComparer () {}
323 public static PtrComparer Instance = new PtrComparer ();
325 public int Compare (object x, object y)
327 if (x == y)
328 return 0;
329 else
330 return 1;
334 public PtrHashtable ()
336 comparer = PtrComparer.Instance;
341 * Hashtable whose keys are character arrays with the same length
343 class CharArrayHashtable : Hashtable {
344 sealed class ArrComparer : IComparer {
345 private int len;
347 public ArrComparer (int len) {
348 this.len = len;
351 public int Compare (object x, object y)
353 char[] a = (char[])x;
354 char[] b = (char[])y;
356 for (int i = 0; i < len; ++i)
357 if (a [i] != b [i])
358 return 1;
359 return 0;
363 private int len;
365 protected override int GetHash (Object key)
367 char[] arr = (char[])key;
368 int h = 0;
370 for (int i = 0; i < len; ++i)
371 h = (h << 5) - h + arr [i];
373 return h;
376 public CharArrayHashtable (int len)
378 this.len = len;
379 comparer = new ArrComparer (len);
383 struct Pair {
384 public object First;
385 public object Second;
387 public Pair (object f, object s)
389 First = f;
390 Second = s;
394 public class Accessors {
395 public Accessor get_or_add;
396 public Accessor set_or_remove;
398 // was 'set' declared before 'get'? was 'remove' declared before 'add'?
399 public bool declared_in_reverse;
401 public Accessors (Accessor get_or_add, Accessor set_or_remove)
403 this.get_or_add = get_or_add;
404 this.set_or_remove = set_or_remove;
408 /// <summary>
409 /// This is a wrapper around StreamReader which is seekable backwards
410 /// within a window of around 2048 chars.
411 /// </summary>
412 public class SeekableStreamReader
414 public SeekableStreamReader (TextReader reader)
416 this.reader = reader;
417 this.buffer = new char [AverageReadLength * 3];
419 // Let the StreamWriter autodetect the encoder
420 reader.Peek ();
423 public SeekableStreamReader (Stream stream, Encoding encoding)
424 : this (new StreamReader (stream, encoding, true))
427 TextReader reader;
429 private const int AverageReadLength = 1024;
431 char[] buffer;
432 int buffer_start; // in chars
433 int char_count; // count buffer[] valid characters
434 int pos; // index into buffer[]
436 /// <remarks>
437 /// This value corresponds to the current position in a stream of characters.
438 /// The StreamReader hides its manipulation of the underlying byte stream and all
439 /// character set/decoding issues. Thus, we cannot use this position to guess at
440 /// the corresponding position in the underlying byte stream even though there is
441 /// a correlation between them.
442 /// </remarks>
443 public int Position {
444 get { return buffer_start + pos; }
446 set {
447 if (value < buffer_start || value > buffer_start + char_count)
448 throw new InternalErrorException ("can't seek that far back: " + (pos - value));
449 pos = value - buffer_start;
453 private bool ReadBuffer ()
455 int slack = buffer.Length - char_count;
456 if (slack <= AverageReadLength / 2) {
457 // shift the buffer to make room for AverageReadLength number of characters
458 int shift = AverageReadLength - slack;
459 Array.Copy (buffer, shift, buffer, 0, char_count - shift);
460 pos -= shift;
461 char_count -= shift;
462 buffer_start += shift;
463 slack += shift; // slack == AverageReadLength
466 int chars_read = reader.Read (buffer, char_count, slack);
467 char_count += chars_read;
469 return pos < char_count;
472 public int Peek ()
474 if ((pos >= char_count) && !ReadBuffer ())
475 return -1;
477 return buffer [pos];
480 public int Read ()
482 if ((pos >= char_count) && !ReadBuffer ())
483 return -1;
485 return buffer [pos++];
489 public class DoubleHash {
490 const int DEFAULT_INITIAL_BUCKETS = 100;
492 public DoubleHash () : this (DEFAULT_INITIAL_BUCKETS) {}
494 public DoubleHash (int size)
496 count = size;
497 buckets = new Entry [size];
500 int count;
501 Entry [] buckets;
502 int size = 0;
504 class Entry {
505 public object key1;
506 public object key2;
507 public int hash;
508 public object value;
509 public Entry next;
511 public Entry (object key1, object key2, int hash, object value, Entry next)
513 this.key1 = key1;
514 this.key2 = key2;
515 this.hash = hash;
516 this.next = next;
517 this.value = value;
521 public bool Lookup (object a, object b, out object res)
523 int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
525 for (Entry e = buckets [h % count]; e != null; e = e.next) {
526 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b)) {
527 res = e.value;
528 return true;
531 res = null;
532 return false;
535 public void Insert (object a, object b, object value)
537 // Is it an existing one?
539 int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
541 for (Entry e = buckets [h % count]; e != null; e = e.next) {
542 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b))
543 e.value = value;
546 int bucket = h % count;
547 buckets [bucket] = new Entry (a, b, h, value, buckets [bucket]);
549 // Grow whenever we double in size
550 if (size++ == count) {
551 count <<= 1;
552 count ++;
554 Entry [] newBuckets = new Entry [count];
555 foreach (Entry root in buckets) {
556 Entry e = root;
557 while (e != null) {
558 int newLoc = e.hash % count;
559 Entry n = e.next;
560 e.next = newBuckets [newLoc];
561 newBuckets [newLoc] = e;
562 e = n;
566 buckets = newBuckets;