**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Reflection / Binder.cs
blob818acb502e5601af5f79a495f77d24a4aa5a6a90
1 // System.Reflection.Binder
2 //
3 // Authors:
4 // Sean MacIsaac (macisaac@ximian.com)
5 // Paolo Molaro (lupus@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (C) Ximian, Inc. 2001 - 2003
9 // (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Globalization;
35 using System.Runtime.InteropServices;
37 namespace System.Reflection
39 [Serializable]
40 [ClassInterface(ClassInterfaceType.AutoDual)]
41 public abstract class Binder
43 protected Binder () {}
45 public abstract FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture);
46 public abstract MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state);
47 public abstract object ChangeType (object value, Type type, CultureInfo culture);
48 public abstract void ReorderArgumentArray( ref object[] args, object state);
49 public abstract MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers);
50 public abstract PropertyInfo SelectProperty( BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers);
52 static Binder default_binder;
54 internal static Binder DefaultBinder {
55 get {
56 if (null == default_binder)
58 lock (typeof (Binder))
60 if (default_binder == null)
61 default_binder = new Default ();
63 return default_binder;
67 return default_binder;
71 internal static bool ConvertArgs (Binder binder, object[] args, ParameterInfo[] pinfo, CultureInfo culture) {
72 if (args == null) {
73 if ( pinfo.Length == 0)
74 return true;
75 else
76 throw new TargetParameterCountException ();
78 if (pinfo.Length != args.Length)
79 throw new TargetParameterCountException ();
80 for (int i = 0; i < args.Length; ++i) {
81 object v = binder.ChangeType (args [i], pinfo[i].ParameterType, culture);
82 if ((v == null) && (args [i] != null))
83 return false;
84 args [i] = v;
86 return true;
89 internal static int GetDerivedLevel (Type type)
91 Type searchType = type;
92 int level = 1;
94 while (searchType.BaseType != null)
96 level++;
97 searchType = searchType.BaseType;
100 return level;
103 internal static MethodBase FindMostDerivedMatch (MethodBase [] match)
105 int highLevel = 0;
106 int matchId = -1;
107 int count = match.Length;
109 for (int current = 0; current < count; current++)
111 int level = GetDerivedLevel (match[current].DeclaringType);
112 if (level == highLevel)
113 throw new AmbiguousMatchException ();
115 if (level > highLevel)
117 highLevel = level;
118 matchId = current;
122 return match[matchId];
125 internal sealed class Default : Binder {
126 public override FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture)
128 if (match == null)
129 throw new ArgumentNullException ("match");
130 foreach (FieldInfo f in match) {
131 if (check_type (value.GetType (), f.FieldType))
132 return f;
134 return null;
137 [MonoTODO]
138 public override MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state)
140 Type[] types;
141 if (args == null)
142 types = Type.EmptyTypes;
143 else {
144 types = new Type [args.Length];
145 for (int i = 0; i < args.Length; ++i) {
146 if (args [i] != null)
147 types [i] = args [i].GetType ();
150 MethodBase selected = SelectMethod (bindingAttr, match, types, modifiers);
151 state = null;
152 return selected;
155 static bool IsArrayAssignable (Type object_type, Type target_type)
157 if (object_type.IsArray && target_type.IsArray)
158 return IsArrayAssignable (object_type.GetElementType (), target_type.GetElementType ());
160 if (target_type.IsAssignableFrom (object_type))
161 return true;
163 return false;
166 public override object ChangeType (object value, Type type, CultureInfo culture)
168 if (value == null)
169 return null;
170 Type vtype = value.GetType ();
171 if (type.IsByRef)
172 type = type.GetElementType ();
173 if (vtype == type || type.IsAssignableFrom (vtype))
174 return value;
175 if (vtype.IsArray && type.IsArray){
176 if (IsArrayAssignable (vtype.GetElementType (), type.GetElementType ()))
177 return value;
180 if (check_type (vtype, type))
181 return Convert.ChangeType (value, type);
182 return null;
185 [MonoTODO]
186 public override void ReorderArgumentArray (ref object[] args, object state)
188 //do nothing until we support named arguments
189 //throw new NotImplementedException ();
192 private static bool check_type (Type from, Type to) {
193 if (from == to)
194 return true;
196 if (from == null)
197 return !to.IsValueType;
199 TypeCode fromt = Type.GetTypeCode (from);
200 TypeCode tot = Type.GetTypeCode (to);
202 if (to.IsByRef && !from.IsByRef)
203 return check_type (from, to.GetElementType ());
205 switch (fromt) {
206 case TypeCode.Char:
207 switch (tot) {
208 case TypeCode.UInt16:
209 case TypeCode.UInt32:
210 case TypeCode.Int32:
211 case TypeCode.UInt64:
212 case TypeCode.Int64:
213 case TypeCode.Single:
214 case TypeCode.Double:
215 return true;
217 return to == typeof (object);
218 case TypeCode.Byte:
219 switch (tot) {
220 case TypeCode.Char:
221 case TypeCode.UInt16:
222 case TypeCode.Int16:
223 case TypeCode.UInt32:
224 case TypeCode.Int32:
225 case TypeCode.UInt64:
226 case TypeCode.Int64:
227 case TypeCode.Single:
228 case TypeCode.Double:
229 return true;
231 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
232 case TypeCode.SByte:
233 switch (tot) {
234 case TypeCode.Int16:
235 case TypeCode.Int32:
236 case TypeCode.Int64:
237 case TypeCode.Single:
238 case TypeCode.Double:
239 return true;
241 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
242 case TypeCode.UInt16:
243 switch (tot) {
244 case TypeCode.UInt32:
245 case TypeCode.Int32:
246 case TypeCode.UInt64:
247 case TypeCode.Int64:
248 case TypeCode.Single:
249 case TypeCode.Double:
250 return true;
252 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
253 case TypeCode.Int16:
254 switch (tot) {
255 case TypeCode.Int32:
256 case TypeCode.Int64:
257 case TypeCode.Single:
258 case TypeCode.Double:
259 return true;
261 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
262 case TypeCode.UInt32:
263 switch (tot) {
264 case TypeCode.UInt64:
265 case TypeCode.Int64:
266 case TypeCode.Single:
267 case TypeCode.Double:
268 return true;
270 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
271 case TypeCode.Int32:
272 switch (tot) {
273 case TypeCode.Int64:
274 case TypeCode.Single:
275 case TypeCode.Double:
276 return true;
278 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
279 case TypeCode.UInt64:
280 case TypeCode.Int64:
281 switch (tot) {
282 case TypeCode.Single:
283 case TypeCode.Double:
284 return true;
286 return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
287 case TypeCode.Single:
288 return tot == TypeCode.Double || to == typeof (object);
289 default:
290 /* TODO: handle valuetype -> byref */
291 if (to == typeof (object) && from.IsValueType)
292 return true;
294 return to.IsAssignableFrom (from);
298 private static bool check_arguments (Type[] types, ParameterInfo[] args) {
299 for (int i = 0; i < types.Length; ++i) {
300 if (!check_type (types [i], args [i].ParameterType))
301 return false;
303 return true;
306 public override MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
308 MethodBase m;
309 int i, j;
311 if (match == null)
312 throw new ArgumentNullException ("match");
314 /* first look for an exact match... */
315 for (i = 0; i < match.Length; ++i) {
316 m = match [i];
317 ParameterInfo[] args = m.GetParameters ();
318 if (args.Length != types.Length)
319 continue;
320 for (j = 0; j < types.Length; ++j) {
321 if (types [j] != args [j].ParameterType)
322 break;
324 if (j == types.Length)
325 return m;
328 MethodBase result = null;
329 for (i = 0; i < match.Length; ++i) {
330 m = match [i];
331 ParameterInfo[] args = m.GetParameters ();
332 if (args.Length != types.Length)
333 continue;
334 if (!check_arguments (types, args))
335 continue;
337 if (result != null)
338 throw new AmbiguousMatchException ();
340 result = m;
343 return result;
346 public override PropertyInfo SelectProperty (BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
348 if (match == null || match.Length == 0)
349 throw new ArgumentException ("No properties provided", "match");
351 bool haveRet = (returnType != null);
352 int idxlen = (indexes != null) ? indexes.Length : 0;
353 PropertyInfo result = null;
354 int i;
355 int best_score = Int32.MaxValue - 1;
356 int fail_score = Int32.MaxValue;
357 int level = 0;
359 for (i = match.Length - 1; i >= 0; i--) {
360 PropertyInfo p = match [i];
361 ParameterInfo[] args = p.GetIndexParameters ();
362 if (idxlen > 0 && idxlen != args.Length)
363 continue;
365 if (haveRet && !check_type (p.PropertyType, returnType))
366 continue;
368 int score = Int32.MaxValue - 1;
369 if (idxlen > 0) {
370 score = check_arguments_with_score (indexes, args);
371 if (score == -1)
372 continue;
375 int new_level = GetDerivedLevel (p.DeclaringType);
376 if (result != null) {
377 if (best_score < score)
378 continue;
380 if (best_score == score) {
381 if (level == new_level) {
382 // Keep searching. May be there's something
383 // better for us.
384 fail_score = score;
385 continue;
388 if (level > new_level)
389 continue;
393 result = p;
394 best_score = score;
395 level = new_level;
398 if (fail_score <= best_score)
399 throw new AmbiguousMatchException ();
401 return result;
404 static int check_arguments_with_score (Type [] types, ParameterInfo [] args)
406 int worst = -1;
408 for (int i = 0; i < types.Length; ++i) {
409 int res = check_type_with_score (types [i], args [i].ParameterType);
410 if (res == -1)
411 return -1;
413 if (worst < res)
414 worst = res;
417 return worst;
420 // 0 -> same type or null and !valuetype
421 // 1 -> to == Enum
422 // 2 -> value type that don't lose data
423 // 3 -> to == IsAssignableFrom
424 // 4 -> to == object
425 static int check_type_with_score (Type from, Type to)
427 if (from == null)
428 return to.IsValueType ? -1 : 0;
430 if (from == to)
431 return 0;
433 if (to == typeof (object))
434 return 4;
436 TypeCode fromt = Type.GetTypeCode (from);
437 TypeCode tot = Type.GetTypeCode (to);
439 switch (fromt) {
440 case TypeCode.Char:
441 switch (tot) {
442 case TypeCode.UInt16:
443 return 0;
445 case TypeCode.UInt32:
446 case TypeCode.Int32:
447 case TypeCode.UInt64:
448 case TypeCode.Int64:
449 case TypeCode.Single:
450 case TypeCode.Double:
451 return 2;
453 return -1;
454 case TypeCode.Byte:
455 switch (tot) {
456 case TypeCode.Char:
457 case TypeCode.UInt16:
458 case TypeCode.Int16:
459 case TypeCode.UInt32:
460 case TypeCode.Int32:
461 case TypeCode.UInt64:
462 case TypeCode.Int64:
463 case TypeCode.Single:
464 case TypeCode.Double:
465 return 2;
467 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
468 case TypeCode.SByte:
469 switch (tot) {
470 case TypeCode.Int16:
471 case TypeCode.Int32:
472 case TypeCode.Int64:
473 case TypeCode.Single:
474 case TypeCode.Double:
475 return 2;
477 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
478 case TypeCode.UInt16:
479 switch (tot) {
480 case TypeCode.UInt32:
481 case TypeCode.Int32:
482 case TypeCode.UInt64:
483 case TypeCode.Int64:
484 case TypeCode.Single:
485 case TypeCode.Double:
486 return 2;
488 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
489 case TypeCode.Int16:
490 switch (tot) {
491 case TypeCode.Int32:
492 case TypeCode.Int64:
493 case TypeCode.Single:
494 case TypeCode.Double:
495 return 2;
497 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
498 case TypeCode.UInt32:
499 switch (tot) {
500 case TypeCode.UInt64:
501 case TypeCode.Int64:
502 case TypeCode.Single:
503 case TypeCode.Double:
504 return 2;
506 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
507 case TypeCode.Int32:
508 switch (tot) {
509 case TypeCode.Int64:
510 case TypeCode.Single:
511 case TypeCode.Double:
512 return 2;
514 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
515 case TypeCode.UInt64:
516 case TypeCode.Int64:
517 switch (tot) {
518 case TypeCode.Single:
519 case TypeCode.Double:
520 return 2;
522 return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
523 case TypeCode.Single:
524 return tot == TypeCode.Double ? 2 : -1;
525 default:
526 return (to.IsAssignableFrom (from)) ? 3 : -1;