2 // support.cs: Support routines to work around the fact that System.Reflection.Emit
3 // can not introspect types that are being constructed
6 // Miguel de Icaza (miguel@ximian.com)
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
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; }
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 ();
34 void InflateTypes (Type[] genArguments, Type[] argTypes);
38 public class ReflectionParameters : ParameterData {
46 public ReflectionParameters (MethodBase mb)
48 ParameterInfo [] pi = mb.GetParameters ();
49 is_varargs = (mb.CallingConvention & CallingConventions.VarArgs) != 0;
52 int count = pi.Length;
55 types = Type.EmptyTypes;
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);
67 gpd = TypeManager.GetParameterData (generic);
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;
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.
86 for (int i = count; i >= 0 && i > count - 2; --i) {
87 if (!pi [i].ParameterType.IsArray)
90 if (pi [i].IsDefined (TypeManager.param_array_type, false)) {
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))
102 public override bool Equals (object obj)
104 ReflectionParameters rp = obj as ReflectionParameters;
108 if (Count != rp.Count)
111 for (int i = 0; i < Count; ++i) {
112 if (!types [i].Equals (rp.types [i]))
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) {
129 sb.Append (ParameterDesc (i));
134 sb.Append ("__arglist");
137 return sb.ToString ();
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])
149 types[i] = argTypes[ii];
155 if (types[i].IsGenericType) {
156 Type[] gen_arguments_open = types[i].GetGenericTypeDefinition ().GetGenericArguments ();
157 Type[] gen_arguments = types[i].GetGenericArguments ();
158 for (int ii = 0; ii < gen_arguments_open.Length; ++ii) {
159 if (gen_arguments[ii].IsGenericParameter)
160 gen_arguments_open[ii] = argTypes[gen_arguments_open[ii].GenericParameterPosition];
162 gen_arguments_open[ii] = gen_arguments[ii];
165 types[i] = types[i].GetGenericTypeDefinition ().MakeGenericType (gen_arguments_open);
171 public Type ParameterType (int pos)
173 if (is_varargs && pos >= pi.Length)
174 return TypeManager.runtime_argument_handle_type;
179 public string ParameterName (int pos)
182 return gpd.ParameterName (pos);
184 if (is_varargs && pos >= pi.Length)
187 return pi [pos].Name;
190 public string ParameterDesc (int pos)
192 if (is_varargs && pos >= pi.Length)
195 StringBuilder sb = new StringBuilder ();
200 Type partype = ParameterType (pos);
201 if (partype.IsByRef){
202 partype = TypeManager.GetElementType (partype);
209 if (params_idx == pos)
210 sb.Append ("params ");
212 if (pos == 0 && ExtensionMethodType != null)
215 sb.Append (TypeManager.CSharpName (partype).Replace ("&", ""));
217 return sb.ToString ();
220 public Parameter.Modifier ParameterModifier (int pos)
222 if (pos == params_idx)
223 return Parameter.Modifier.PARAMS;
224 else if (is_varargs && pos >= pi.Length)
225 return Parameter.Modifier.ARGLIST;
228 return gpd.ParameterModifier (pos);
230 Type t = types [pos];
232 if ((pi [pos].Attributes & (ParameterAttributes.Out|ParameterAttributes.In)) == ParameterAttributes.Out)
233 return Parameter.Modifier.OUT;
235 return Parameter.Modifier.REF;
238 return Parameter.Modifier.NONE;
242 get { return is_varargs ? pi.Length + 1 : pi.Length; }
245 public Type ExtensionMethodType {
254 public bool HasParams {
255 get { return params_idx != -1; }
258 public Type[] Types {
259 get { return types; }
264 public class ReflectionConstraints : GenericConstraints
266 GenericParameterAttributes attrs;
268 Type class_constraint;
269 Type[] iface_constraints;
272 public static GenericConstraints GetConstraints (Type t)
274 Type [] constraints = t.GetGenericParameterConstraints ();
275 GenericParameterAttributes attrs = t.GenericParameterAttributes;
276 if (constraints.Length == 0 && attrs == GenericParameterAttributes.None)
278 return new ReflectionConstraints (t.Name, constraints, attrs);
281 private ReflectionConstraints (string name, Type [] constraints, GenericParameterAttributes attrs)
286 if ((constraints.Length > 0) && !constraints [0].IsInterface) {
287 class_constraint = constraints [0];
288 iface_constraints = new Type [constraints.Length - 1];
289 Array.Copy (constraints, 1, iface_constraints, 0, constraints.Length - 1);
291 iface_constraints = constraints;
293 if (HasValueTypeConstraint)
294 base_type = TypeManager.value_type;
295 else if (class_constraint != null)
296 base_type = class_constraint;
298 base_type = TypeManager.object_type;
301 public override string TypeParameter {
305 public override GenericParameterAttributes Attributes {
306 get { return attrs; }
309 public override Type ClassConstraint {
310 get { return class_constraint; }
313 public override Type EffectiveBaseClass {
314 get { return base_type; }
317 public override Type[] InterfaceConstraints {
318 get { return iface_constraints; }
323 class PtrHashtable : Hashtable {
324 sealed class PtrComparer : IComparer {
325 private PtrComparer () {}
327 public static PtrComparer Instance = new PtrComparer ();
329 public int Compare (object x, object y)
338 public PtrHashtable ()
340 comparer = PtrComparer.Instance;
345 * Hashtable whose keys are character arrays with the same length
347 class CharArrayHashtable : Hashtable {
348 sealed class ArrComparer : IComparer {
351 public ArrComparer (int len) {
355 public int Compare (object x, object y)
357 char[] a = (char[])x;
358 char[] b = (char[])y;
360 for (int i = 0; i < len; ++i)
369 protected override int GetHash (Object key)
371 char[] arr = (char[])key;
374 for (int i = 0; i < len; ++i)
375 h = (h << 5) - h + arr [i];
380 public CharArrayHashtable (int len)
383 comparer = new ArrComparer (len);
389 public object Second;
391 public Pair (object f, object s)
398 public class Accessors {
399 public Accessor get_or_add;
400 public Accessor set_or_remove;
402 // was 'set' declared before 'get'? was 'remove' declared before 'add'?
403 public bool declared_in_reverse;
405 public Accessors (Accessor get_or_add, Accessor set_or_remove)
407 this.get_or_add = get_or_add;
408 this.set_or_remove = set_or_remove;
413 /// This is a wrapper around StreamReader which is seekable backwards
414 /// within a window of around 2048 chars.
416 public class SeekableStreamReader
418 public SeekableStreamReader (TextReader reader)
420 this.reader = reader;
421 this.buffer = new char [AverageReadLength * 3];
423 // Let the StreamWriter autodetect the encoder
427 public SeekableStreamReader (Stream stream, Encoding encoding)
428 : this (new StreamReader (stream, encoding, true))
433 private const int AverageReadLength = 1024;
436 int buffer_start; // in chars
437 int char_count; // count buffer[] valid characters
438 int pos; // index into buffer[]
441 /// This value corresponds to the current position in a stream of characters.
442 /// The StreamReader hides its manipulation of the underlying byte stream and all
443 /// character set/decoding issues. Thus, we cannot use this position to guess at
444 /// the corresponding position in the underlying byte stream even though there is
445 /// a correlation between them.
447 public int Position {
448 get { return buffer_start + pos; }
451 if (value < buffer_start || value > buffer_start + char_count)
452 throw new InternalErrorException ("can't seek that far back: " + (pos - value));
453 pos = value - buffer_start;
457 private bool ReadBuffer ()
459 int slack = buffer.Length - char_count;
460 if (slack <= AverageReadLength / 2) {
461 // shift the buffer to make room for AverageReadLength number of characters
462 int shift = AverageReadLength - slack;
463 Array.Copy (buffer, shift, buffer, 0, char_count - shift);
466 buffer_start += shift;
467 slack += shift; // slack == AverageReadLength
470 int chars_read = reader.Read (buffer, char_count, slack);
471 char_count += chars_read;
473 return pos < char_count;
478 if ((pos >= char_count) && !ReadBuffer ())
486 if ((pos >= char_count) && !ReadBuffer ())
489 return buffer [pos++];
493 public class DoubleHash {
494 const int DEFAULT_INITIAL_BUCKETS = 100;
496 public DoubleHash () : this (DEFAULT_INITIAL_BUCKETS) {}
498 public DoubleHash (int size)
501 buckets = new Entry [size];
515 public Entry (object key1, object key2, int hash, object value, Entry next)
525 public bool Lookup (object a, object b, out object res)
527 int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
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)) {
539 public void Insert (object a, object b, object value)
541 // Is it an existing one?
543 int h = (a.GetHashCode () ^ b.GetHashCode ()) & 0x7FFFFFFF;
545 for (Entry e = buckets [h % count]; e != null; e = e.next) {
546 if (e.hash == h && e.key1.Equals (a) && e.key2.Equals (b))
550 int bucket = h % count;
551 buckets [bucket] = new Entry (a, b, h, value, buckets [bucket]);
553 // Grow whenever we double in size
554 if (size++ == count) {
558 Entry [] newBuckets = new Entry [count];
559 foreach (Entry root in buckets) {
562 int newLoc = e.hash % count;
564 e.next = newBuckets [newLoc];
565 newBuckets [newLoc] = e;
570 buckets = newBuckets;