Merge pull request #798 from akoeplinger/fix-test
[mono.git] / mcs / class / corlib / System.Reflection / Binder.cs
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 //      Marek Safar (marek.safar@gmail.com)
8 //
9 // (C) Ximian, Inc. 2001 - 2003
10 // (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
11 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.Globalization;
34 using System.Runtime.InteropServices;
35 using System.Collections.Generic;
36
37 namespace System.Reflection
38 {
39         [ComVisible (true)]
40         [Serializable]
41         [ClassInterface(ClassInterfaceType.AutoDual)]
42         public abstract class Binder
43         {
44                 protected Binder () {}
45
46                 public abstract FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture);
47                 public abstract MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state);
48                 public abstract object ChangeType (object value, Type type, CultureInfo culture);
49                 public abstract void ReorderArgumentArray( ref object[] args, object state);
50                 public abstract MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers);
51                 public abstract PropertyInfo SelectProperty( BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers);
52
53                 static readonly Binder default_binder = new Default ();
54
55                 internal static Binder DefaultBinder {
56                         get {
57                                 return default_binder;
58                         }
59                 }
60                 
61                 internal void ConvertValues (object[] args, ParameterInfo[] pinfo, CultureInfo culture, bool exactMatch)
62                 {
63                         if (args == null) {
64                                 if (pinfo.Length == 0)
65                                         return;
66                                 
67                                 throw new TargetParameterCountException ();
68                         }
69
70                         if (pinfo.Length != args.Length)
71                                 throw new TargetParameterCountException ();
72                         
73                         for (int i = 0; i < args.Length; ++i) {
74                                 var arg = args [i];
75                                 var pi = pinfo [i];
76                                 if (arg == Type.Missing) {
77                                         args [i] = pi.DefaultValue;
78                                         continue;
79                                 }
80
81                                 args [i] = ConvertValue (arg, pi.ParameterType, culture, exactMatch);
82                         }
83                 }
84
85                 internal object ConvertValue (object value, Type type, CultureInfo culture, bool exactMatch)
86                 {
87                         bool failed = false;
88                         var res = TryConvertToType (value, type, ref failed);
89                         if (!failed)
90                                 return res;
91
92                         if (exactMatch || this == default_binder)
93                                 throw new ArgumentException ("Object type " + value.GetType() + " cannot be converted to target type: " + type.FullName);
94
95                         return ChangeType (value, type, culture);
96                 }
97
98                 object TryConvertToType (object value, Type type, ref bool failed)
99                 {
100                         if (type.IsInstanceOfType (value)) {
101                                 return value;
102                         }
103
104                         if (type.IsByRef) {
105                         var elementType = type.GetElementType ();
106                         if (value == null || elementType.IsInstanceOfType (value)) {
107                                         return value;
108                                 }
109                         }
110
111                         if (value == null)
112                                 return value;
113
114                         if (type.IsEnum) {
115                                 type = Enum.GetUnderlyingType (type);
116                                 if (type == value.GetType ())
117                                         return value;
118                         }
119
120                         if (type.IsPrimitive) {
121                                 var res = IsConvertibleToPrimitiveType (value, type);
122                                 if (res != null)
123                                         return res;
124                         } else if (type.IsPointer) {
125                                 var vtype = value.GetType ();
126                                 if (vtype == typeof (IntPtr) || vtype == typeof (UIntPtr))
127                                         return value;
128                         }
129
130                         failed = true;
131                         return null;
132                 }
133
134                 // Binder uses some incompatible conversion rules. For example
135                 // int value cannot be used with decimal parameter but in other
136                 // ways it's more flexible than normal convertor, for example
137                 // long value can be used with int based enum
138                 static object IsConvertibleToPrimitiveType (object value, Type targetType)              
139                 {
140                         var type = value.GetType ();
141                         if (type.IsEnum) {
142                                 type = Enum.GetUnderlyingType (type);
143                                 if (type == targetType)
144                                         return value;
145                         }
146
147                         var from = Type.GetTypeCode (type);
148                         var to = Type.GetTypeCode (targetType);
149
150                         switch (to) {
151                                 case TypeCode.Char:
152                                         switch (from) {
153                                                 case TypeCode.Byte:
154                                                         return (Char) (Byte) value;
155                                                 case TypeCode.UInt16:
156                                                         return value;
157                                         }
158                                         break;
159                                 case TypeCode.Int16:
160                                         switch (from) {
161                                                 case TypeCode.Byte:
162                                                         return (Int16) (Byte) value;
163                                                 case TypeCode.SByte:
164                                                         return (Int16) (SByte) value;                                           
165                                         }
166                                         break;
167                                 case TypeCode.UInt16:
168                                         switch (from) {
169                                                 case TypeCode.Byte:
170                                                         return (UInt16) (Byte) value;
171                                                 case TypeCode.Char:
172                                                         return value;
173                                         }
174                                         break;
175                                 case TypeCode.Int32:
176                                         switch (from) {
177                                                 case TypeCode.Byte:
178                                                         return (Int32) (Byte) value;
179                                                 case TypeCode.SByte:
180                                                         return (Int32) (SByte) value;
181                                                 case TypeCode.Char:
182                                                         return (Int32) (Char) value;
183                                                 case TypeCode.Int16:
184                                                         return (Int32) (Int16) value;
185                                                 case TypeCode.UInt16:
186                                                         return (Int32) (UInt16) value;
187                                         }
188                                         break;
189                                 case TypeCode.UInt32:
190                                         switch (from) {
191                                                 case TypeCode.Byte:
192                                                         return (UInt32) (Byte) value;
193                                                 case TypeCode.Char:
194                                                         return (UInt32) (Char) value;
195                                                 case TypeCode.UInt16:
196                                                         return (UInt32) (UInt16) value;
197                                         }
198                                         break;
199                                 case TypeCode.Int64:
200                                         switch (from) {
201                                                 case TypeCode.Byte:
202                                                         return (Int64) (Byte) value;
203                                                 case TypeCode.SByte:
204                                                         return (Int64) (SByte) value;                                                   
205                                                 case TypeCode.Int16:
206                                                         return (Int64) (Int16) value;
207                                                 case TypeCode.Char:
208                                                         return (Int64) (Char) value;
209                                                 case TypeCode.UInt16:
210                                                         return (Int64) (UInt16) value;
211                                                 case TypeCode.Int32:
212                                                         return (Int64) (Int32) value;
213                                                 case TypeCode.UInt32:
214                                                         return (Int64) (UInt32) value;
215                                         }
216                                         break;
217                                 case TypeCode.UInt64:
218                                         switch (from) {
219                                                 case TypeCode.Byte:
220                                                         return (UInt64) (Byte) value;
221                                                 case TypeCode.Char:
222                                                         return (UInt64) (Char) value;
223                                                 case TypeCode.UInt16:
224                                                         return (UInt64) (UInt16) value;
225                                                 case TypeCode.UInt32:
226                                                         return (UInt64) (UInt32) value;
227                                         }
228                                         break;
229                                 case TypeCode.Single:
230                                         switch (from) {
231                                                 case TypeCode.Byte:
232                                                         return (Single) (Byte) value;
233                                                 case TypeCode.SByte:
234                                                         return (Single) (SByte) value;
235                                                 case TypeCode.Int16:
236                                                         return (Single) (Int16) value;
237                                                 case TypeCode.Char:
238                                                         return (Single) (Char) value;
239                                                 case TypeCode.UInt16:
240                                                         return (Single) (UInt16) value;
241                                                 case TypeCode.Int32:
242                                                         return (Single) (Int32) value;
243                                                 case TypeCode.UInt32:
244                                                         return (Single) (UInt32) value;
245                                                 case TypeCode.Int64:
246                                                         return (Single) (Int64) value;
247                                                 case TypeCode.UInt64:
248                                                         return (Single) (UInt64) value;
249                                         }
250                                         break;
251                                 case TypeCode.Double:
252                                         switch (from) {
253                                                 case TypeCode.Byte:
254                                                         return (Double) (Byte) value;
255                                                 case TypeCode.SByte:
256                                                         return (Double) (SByte) value;
257                                                 case TypeCode.Char:
258                                                         return (Double) (Char) value;
259                                                 case TypeCode.Int16:
260                                                         return (Double) (Int16) value;
261                                                 case TypeCode.UInt16:
262                                                         return (Double) (UInt16) value;
263                                                 case TypeCode.Int32:
264                                                         return (Double) (Int32) value;
265                                                 case TypeCode.UInt32:
266                                                         return (Double) (UInt32) value;
267                                                 case TypeCode.Int64:
268                                                         return (Double) (Int64) value;
269                                                 case TypeCode.UInt64:
270                                                         return (Double) (UInt64) value;
271                                                 case TypeCode.Single:
272                                                         return (Double) (Single) value;
273                                         }
274                                         break;
275                         }
276
277                         // Everything else is rejected
278                         return null;
279                 }
280
281                 internal static int GetDerivedLevel (Type type) 
282                 {
283                         Type searchType = type;
284                         int level = 1;
285
286                         while (searchType.BaseType != null) 
287                         {
288                                 level++;
289                                 searchType = searchType.BaseType;
290                         }
291
292                         return level;
293                 }
294
295                 internal static MethodBase FindMostDerivedMatch (MethodBase [] match) 
296                 {
297                         int highLevel = 0;
298                         int matchId = -1;
299                         int count = match.Length;
300
301                         for (int current = 0; current < count; current++) 
302                         {
303                                 MethodBase m = match [current];
304                                 int level = GetDerivedLevel (m.DeclaringType);
305                                 if (level == highLevel)
306                                         throw new AmbiguousMatchException ();
307                                 // If the argument types differ we
308                                 // have an ambigous match, as well
309                                 if (matchId >= 0) {
310                                         ParameterInfo[] p1 = m.GetParametersInternal ();
311                                         ParameterInfo[] p2 = match [matchId].GetParametersInternal ();
312                                         bool equal = true;
313
314                                         if (p1.Length != p2.Length)
315                                                 equal = false;
316                                         else {
317                                                 int i;
318
319                                                 for (i = 0; i < p1.Length; ++i) {
320                                                         if (p1 [i].ParameterType != p2 [i].ParameterType) {
321                                                                 equal = false;
322                                                                 break;
323                                                         }
324                                                 }
325                                         }
326
327                                         if (!equal)
328                                                 throw new AmbiguousMatchException ();
329                                 }
330
331                                 if (level > highLevel) 
332                                 {
333                                         highLevel = level;
334                                         matchId = current;
335                                 }
336                         }
337
338                         return match[matchId];
339                 }
340
341                 internal sealed class Default : Binder {
342                         public override FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture) 
343                         {
344                                 if (match == null)
345                                         throw new ArgumentNullException ("match");
346                                 foreach (FieldInfo f in match) {
347                                         if (check_type (value.GetType (), f.FieldType))
348                                                 return f;
349                                 }
350                                 return null;
351                         }
352
353                         public override MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state)
354                         {
355                                 Type[] types;
356                                 if (args == null)
357                                         types = Type.EmptyTypes;
358                                 else {
359                                         types = new Type [args.Length];
360                                         for (int i = 0; i < args.Length; ++i) {
361                                                 if (args [i] != null)
362                                                         types [i] = args [i].GetType ();
363                                         }
364                                 }
365
366                                 MethodBase selected = null;
367                                 if (names != null) {
368                                         foreach (var m in match) {
369                                                 var parameters = m.GetParametersInternal ();
370                                                 int i;
371
372                                                 /*
373                                                  * Find the corresponding parameter for each parameter name,
374                                                  * reorder types/modifiers array during the search.
375                                                  */
376                                                 Type[] newTypes = new Type [types.Length];
377                                                 Array.FastCopy (types, 0, newTypes, 0, types.Length);
378
379                                                 ParameterModifier[] newModifiers = null;
380                                                 if (modifiers != null) {
381                                                         newModifiers = new ParameterModifier [modifiers.Length];
382                                                         Array.FastCopy (modifiers, 0, newModifiers, 0, modifiers.Length);
383                                                 }
384
385                                                 for (i = 0; i < names.Length; ++i) {
386                                                         /* Find the corresponding parameter */
387                                                         int nindex = -1;
388                                                         for (int j = 0; j < parameters.Length; ++j) {
389                                                                 if (parameters [j].Name == names [i]) {
390                                                                         nindex = j;
391                                                                         break;
392                                                                 }
393                                                         }
394                                                         if (nindex == -1)
395                                                                 break;
396                                                         if (i < newTypes.Length && nindex < types.Length)
397                                                                 newTypes [i] = types [nindex];
398                                                         if (modifiers != null && i < newModifiers.Length && nindex < modifiers.Length)
399                                                                 newModifiers [i] = modifiers [nindex];
400                                                 }
401                                                 if (i < names.Length)
402                                                         continue;
403
404                                                 selected = SelectMethod (bindingAttr, new MethodBase [] { m }, newTypes, newModifiers, true, ref args);
405                                                 if (selected != null)
406                                                         break;
407                                         }
408                                 } else {
409                                         selected = SelectMethod (bindingAttr, match, types, modifiers, true, ref args);
410                                 }
411
412                                 state = null;
413                                 if (selected != null && names != null)
414                                         ReorderParameters (names, ref args, selected);
415
416                                 if (selected != null) {
417                                         if (args == null)
418                                                 args = EmptyArray<object>.Value;
419         
420                                         AdjustArguments (selected, ref args);
421                                 }
422
423                                 return selected;
424                         }
425
426                         // probably belongs in ReorderArgumentArray
427                         static void AdjustArguments (MethodBase selected, ref object [] args)
428                         {
429                                 var parameters = selected.GetParametersInternal ();
430                                 var parameters_length = parameters.Length;
431                                 if (parameters_length == 0)
432                                         return;
433
434                                 var last_parameter = parameters [parameters.Length - 1];
435                                 Type last_parameter_type = last_parameter.ParameterType;
436                                 if (!Attribute.IsDefined (last_parameter, typeof (ParamArrayAttribute)))
437                                         return;
438
439                                 var args_length = args.Length;
440                                 var param_args_count = args_length + 1 - parameters_length;
441                                 var first_vararg_index = args_length - param_args_count;
442                                 if (first_vararg_index < args_length) {
443                                         var first_vararg = args [first_vararg_index];
444                                         if (first_vararg != null && first_vararg.GetType () == last_parameter_type)
445                                                 return;
446                                 }
447                                 
448                                 var params_args = Array.CreateInstance (last_parameter_type.GetElementType (), param_args_count);
449                                 for (int i = 0; i < param_args_count; i++)
450                                         params_args.SetValue (args [first_vararg_index + i], i);
451
452                                 var adjusted = new object [parameters_length];
453                                 Array.Copy (args, adjusted, parameters_length - 1);
454                                 
455                                 adjusted [adjusted.Length - 1] = params_args;
456                                 args = adjusted;
457                         }
458
459                         void ReorderParameters (string [] names, ref object [] args, MethodBase selected)
460                         {
461                                 object [] newArgs = new object [args.Length];
462                                 Array.Copy (args, newArgs, args.Length);
463                                 ParameterInfo [] plist = selected.GetParametersInternal ();
464                                 for (int n = 0; n < names.Length; n++)
465                                         for (int p = 0; p < plist.Length; p++) {
466                                                 if (names [n] == plist [p].Name) {
467                                                         newArgs [p] = args [n];
468                                                         break;
469                                                 }
470                                         }
471                                 Array.Copy (newArgs, args, args.Length);
472                         }
473                         
474                         public override object ChangeType (object value, Type type, CultureInfo culture)
475                         {
476                                 throw new NotSupportedException ();
477                         }
478
479                         [MonoTODO ("This method does not do anything in Mono")]
480                         public override void ReorderArgumentArray (ref object[] args, object state)
481                         {
482                                 //do nothing until we support named arguments
483                                 //throw new NotImplementedException ();
484                         }
485
486                         private static bool check_type (Type from, Type to) {
487                                 if (from == to)
488                                         return true;
489
490                                 if (from == null)
491                                         return true;
492
493                                 if (to.IsByRef != from.IsByRef)
494                                         return false;
495
496                                 if (to.IsInterface)
497                                         return to.IsAssignableFrom (from);
498
499                                 if (to.IsEnum) {
500                                         to = Enum.GetUnderlyingType (to);
501                                         if (from == to)
502                                                 return true;
503                                 }
504
505                                 if (to.IsGenericType && to.GetGenericTypeDefinition () == typeof (Nullable<>) && to.GetGenericArguments ()[0] == from)
506                                         return true;
507
508                                 TypeCode fromt = Type.GetTypeCode (from);
509                                 TypeCode tot = Type.GetTypeCode (to);
510
511                                 switch (fromt) {
512                                 case TypeCode.Char:
513                                         switch (tot) {
514                                         case TypeCode.UInt16:
515                                         case TypeCode.UInt32:
516                                         case TypeCode.Int32:
517                                         case TypeCode.UInt64:
518                                         case TypeCode.Int64:
519                                         case TypeCode.Single:
520                                         case TypeCode.Double:
521                                                 return true;
522                                         }
523                                         return to == typeof (object);
524                                 case TypeCode.Byte:
525                                         switch (tot) {
526                                         case TypeCode.Char:
527                                         case TypeCode.UInt16:
528                                         case TypeCode.Int16:
529                                         case TypeCode.UInt32:
530                                         case TypeCode.Int32:
531                                         case TypeCode.UInt64:
532                                         case TypeCode.Int64:
533                                         case TypeCode.Single:
534                                         case TypeCode.Double:
535                                                 return true;
536                                         }
537                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
538                                 case TypeCode.SByte:
539                                         switch (tot) {
540                                         case TypeCode.Int16:
541                                         case TypeCode.Int32:
542                                         case TypeCode.Int64:
543                                         case TypeCode.Single:
544                                         case TypeCode.Double:
545                                                 return true;
546                                         }
547                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
548                                 case TypeCode.UInt16:
549                                         switch (tot) {
550                                         case TypeCode.UInt32:
551                                         case TypeCode.Int32:
552                                         case TypeCode.UInt64:
553                                         case TypeCode.Int64:
554                                         case TypeCode.Single:
555                                         case TypeCode.Double:
556                                                 return true;
557                                         }
558                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
559                                 case TypeCode.Int16:
560                                         switch (tot) {
561                                         case TypeCode.Int32:
562                                         case TypeCode.Int64:
563                                         case TypeCode.Single:
564                                         case TypeCode.Double:
565                                                 return true;
566                                         }
567                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
568                                 case TypeCode.UInt32:
569                                         switch (tot) {
570                                         case TypeCode.UInt64:
571                                         case TypeCode.Int64:
572                                         case TypeCode.Single:
573                                         case TypeCode.Double:
574                                                 return true;
575                                         }
576                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
577                                 case TypeCode.Int32:
578                                         switch (tot) {
579                                         case TypeCode.Int64:
580                                         case TypeCode.Single:
581                                         case TypeCode.Double:
582                                                 return true;
583                                         }
584                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
585                                 case TypeCode.UInt64:
586                                 case TypeCode.Int64:
587                                         switch (tot) {
588                                         case TypeCode.Single:
589                                         case TypeCode.Double:
590                                                 return true;
591                                         }
592                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
593                                 case TypeCode.Single:
594                                         return tot == TypeCode.Double || to == typeof (object);
595                                 default:
596                                         /* TODO: handle valuetype -> byref */
597                                         if (to == typeof (object) && from.IsValueType)
598                                                 return true;
599                                         if (to.IsPointer && from == typeof (IntPtr))
600                                                 return true;
601
602                                         return to.IsAssignableFrom (from);
603                                 }
604                         }
605
606                         private static bool check_arguments (Type[] types, ParameterInfo[] args, bool allowByRefMatch) {
607                                 for (int i = 0; i < types.Length; ++i) {
608                                         bool match = check_type (types [i], args [i].ParameterType);
609                                         if (!match && allowByRefMatch) {
610                                                 Type param_type = args [i].ParameterType;
611                                                 if (param_type.IsByRef)
612                                                         match = check_type (types [i], param_type.GetElementType ());
613                                         }
614                                         if (!match)
615                                                 return false;
616                                 }
617                                 return true;
618                         }
619
620                         public override MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase [] match, Type [] types, ParameterModifier [] modifiers)
621                         {
622                                 object[] args = null;
623                                 return SelectMethod (bindingAttr, match, types, modifiers, false, ref args);
624                         }
625
626                         MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers, bool allowByRefMatch, ref object[] arguments)
627                         {
628                                 MethodBase m;
629                                 int i, j;
630
631                                 if (match == null)
632                                         throw new ArgumentNullException ("match");
633
634                                 /* first look for an exact match... */
635                                 MethodBase exact_match = null;
636                                 for (i = 0; i < match.Length; ++i) {
637                                         m = match [i];
638                                         if (m.GetParametersCount () != types.Length)
639                                                 continue;
640
641                                         ParameterInfo[] args = m.GetParametersInternal ();
642                                         for (j = 0; j < types.Length; ++j) {
643                                                 if (types [j] != args [j].ParameterType)
644                                                         break;
645                                         }
646                                         if (j == types.Length) {
647                                                 if (exact_match != null) {
648                                                         exact_match = null;
649                                                         break;
650                                                 } else {
651                                                         exact_match = m;
652                                                 }
653                                         }
654                                 }
655                                 if (exact_match != null)
656                                         return exact_match;
657
658                                 /* Try methods with ParamArray attribute */
659                                 if (arguments != null) {
660                                         for (i = 0; i < match.Length; ++i) {
661                                                 m = match [i];
662
663                                                 var count = m.GetParametersCount ();
664                                                 if (count == 0 || count > types.Length + 1)
665                                                         continue;
666
667                                                 var pi = m.GetParametersInternal ();
668                                                 if (!Attribute.IsDefined (pi [pi.Length - 1], typeof (ParamArrayAttribute)))
669                                                         continue;
670
671                                                 var elementType = pi [pi.Length - 1].ParameterType.GetElementType ();
672                                                 for (j = 0; j < types.Length; ++j) {
673                                                         if (j < (pi.Length - 1) && types [j] != pi [j].ParameterType)
674                                                                 break;
675                                                         
676                                                         if (j >= (pi.Length - 1) && types [j] != elementType) 
677                                                                 break;
678                                                 }
679
680                                                 if (j == types.Length)
681                                                         return m;
682                                         }
683                                 }
684
685                                 if ((bindingAttr & BindingFlags.ExactBinding) != 0)
686                                         return null;
687
688                                 MethodBase result = null;
689                                 ParameterInfo[] result_pi = null;
690                                 for (i = 0; i < match.Length; ++i) {
691                                         m = match [i];
692                                         var pi = m.GetParametersInternal ();
693                                         var full_pi = pi;
694                                         if (pi.Length != types.Length) {
695                                                 if ((bindingAttr & BindingFlags.OptionalParamBinding) == 0)
696                                                         continue;
697
698                                                 List<ParameterInfo> pi_reduced = null;
699                                                 for (var ii = pi.Length - 1; ii >= 0; --ii) {
700                                                         if ((pi [ii].Attributes & ParameterAttributes.HasDefault) == 0)
701                                                                 break;
702
703                                                         if (pi_reduced == null) {
704                                                                 pi_reduced = new List<ParameterInfo> (pi);
705                                                         }
706
707                                                         pi_reduced.RemoveAt (ii);
708                                                 }
709
710                                                 if (pi_reduced == null || pi_reduced.Count != types.Length)
711                                                         continue;
712
713                                                 pi = pi_reduced.ToArray ();
714                                         }
715
716                                         if (!check_arguments (types, pi, allowByRefMatch))
717                                                 continue;
718
719                                         if (result != null) {
720                                                 result = GetBetterMethod (result, m, types);
721                                                 if (result != m)
722                                                         continue;
723                                         }
724
725                                         result = m;
726                                         result_pi = full_pi;
727                                 }
728
729                                 if (result != null) {
730                                         i = arguments == null ? 0 : arguments.Length;
731                                         Array.Resize (ref arguments, result_pi.Length);
732                                         for (; i < arguments.Length; ++i)
733                                                 arguments [i] = result_pi [i].DefaultValue;
734
735                                         return result;
736                                 }
737
738                                 if (arguments == null || types.Length != arguments.Length)
739                                         return null;
740
741                                 // Xamarin-5278: try with parameters that are COM objects
742                                 // REVIEW: do we also need to implement best method match?
743                                 for (i = 0; i < match.Length; ++i) {
744                                         m = match [i];
745                                         ParameterInfo[] methodArgs = m.GetParametersInternal ();
746                                         if (methodArgs.Length != types.Length)
747                                                 continue;
748                                         for (j = 0; j < types.Length; ++j) {
749                                                 var requiredType = methodArgs [j].ParameterType;
750                                                 if (types [j] == requiredType)
751                                                         continue;
752 #if !MOBILE
753                                                 if (types [j] == typeof (__ComObject) && requiredType.IsInterface) {
754                                                         var iface = Marshal.GetComInterfaceForObject (arguments [j], requiredType);
755                                                         if (iface != IntPtr.Zero) {
756                                                                 // the COM object implements the desired interface
757                                                                 Marshal.Release (iface);
758                                                                 continue;
759                                                         }
760                                                 }
761 #endif
762                                                 break;
763                                         }
764
765                                         if (j == types.Length)
766                                                 return m;
767                                 }
768                                 return null;
769                         }
770
771                         MethodBase GetBetterMethod (MethodBase m1, MethodBase m2, Type [] types)
772                         {
773                                 ParameterInfo [] pl1 = m1.GetParametersInternal ();
774                                 ParameterInfo [] pl2 = m2.GetParametersInternal ();
775                                 int prev = 0;
776                                 for (int i = 0; i < pl1.Length; i++) {
777                                         int cmp = CompareCloserType (pl1 [i].ParameterType, pl2 [i].ParameterType);
778                                         if (cmp != 0 && prev != 0 && prev != cmp)
779                                                 throw new AmbiguousMatchException ();
780                                         if (cmp != 0)
781                                                 prev = cmp;
782                                 }
783                                 if (prev != 0)
784                                         return prev > 0 ? m2 : m1;
785
786                                 Type dt1 = m1.DeclaringType;
787                                 Type dt2 = m2.DeclaringType;
788                                 if (dt1 != dt2) {
789                                         if (dt1.IsSubclassOf(dt2))
790                                                 return m1;
791                                         if (dt2.IsSubclassOf(dt1))
792                                                 return m2;
793                                 }
794
795                                 bool va1 = (m1.CallingConvention & CallingConventions.VarArgs) != 0;
796                                 bool va2 = (m2.CallingConvention & CallingConventions.VarArgs) != 0;
797                                 if (va1 && !va2)
798                                         return m2;
799                                 if (va2 && !va1)
800                                         return m1;
801
802                                 throw new AmbiguousMatchException ();
803                         }
804
805                         int CompareCloserType (Type t1, Type t2)
806                         {
807                                 if (t1 == t2)
808                                         return 0;
809                                 if (t1.IsGenericParameter && !t2.IsGenericParameter)
810                                         return 1; // t2
811                                 if (!t1.IsGenericParameter && t2.IsGenericParameter)
812                                         return -1; // t1
813                                 if (t1.HasElementType && t2.HasElementType)
814                                         return CompareCloserType (
815                                                 t1.GetElementType (),
816                                                 t2.GetElementType ());
817
818                                 if (t1.IsSubclassOf (t2))
819                                         return -1; // t1
820                                 if (t2.IsSubclassOf (t1))
821                                         return 1; // t2
822
823                                 if (t1.IsInterface && Array.IndexOf (t2.GetInterfaces (), t1) >= 0)
824                                         return 1; // t2
825                                 if (t2.IsInterface && Array.IndexOf (t1.GetInterfaces (), t2) >= 0)
826                                         return -1; // t1
827
828                                 // What kind of cases could reach here?
829                                 return 0;
830                         }
831
832                         public override PropertyInfo SelectProperty (BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
833                         {
834                                 if (match == null || match.Length == 0)
835                                         throw new ArgumentException ("No properties provided", "match");
836
837                                 bool haveRet = (returnType != null);
838                                 int idxlen = (indexes != null) ? indexes.Length : -1;
839                                 PropertyInfo result = null;
840                                 int i;
841                                 int best_score = Int32.MaxValue - 1;
842                                 int fail_score = Int32.MaxValue;
843                                 int level = 0;
844                                 
845                                 for (i = match.Length - 1; i >= 0; i--) {
846                                         PropertyInfo p = match [i];
847                                         ParameterInfo[] args = p.GetIndexParameters ();
848                                         if (idxlen >= 0 && idxlen != args.Length)
849                                                 continue;
850
851                                         if (haveRet && p.PropertyType != returnType)
852                                                 continue;
853
854                                         int score = Int32.MaxValue - 1;
855                                         if (idxlen > 0) {
856                                                 score = check_arguments_with_score (indexes, args);
857                                                 if (score == -1)
858                                                         continue;
859                                         }
860
861                                         int new_level = GetDerivedLevel (p.DeclaringType);
862                                         if (result != null) {
863                                                 if (best_score < score)
864                                                         continue;
865
866                                                 if (best_score == score) {
867                                                         if (level == new_level) {
868                                                                 // Keep searching. May be there's something
869                                                                 // better for us.
870                                                                 fail_score = score;
871                                                                 continue;
872                                                         }
873
874                                                         if (level > new_level)
875                                                                 continue;
876                                                 }
877                                         }
878
879                                         result = p;
880                                         best_score = score;
881                                         level = new_level;
882                                 }
883
884                                 if (fail_score <= best_score)
885                                         throw new AmbiguousMatchException ();
886
887                                 return result;
888                         }
889
890                         static int check_arguments_with_score (Type [] types, ParameterInfo [] args)
891                         {
892                                 int worst = -1;
893
894                                 for (int i = 0; i < types.Length; ++i) {
895                                         int res = check_type_with_score (types [i], args [i].ParameterType);
896                                         if (res == -1)
897                                                 return -1;
898
899                                         if (worst < res)
900                                                 worst = res;
901                                 }
902
903                                 return worst;
904                         }
905
906                         // 0 -> same type or null and !valuetype
907                         // 1 -> to == Enum
908                         // 2 -> value type that don't lose data
909                         // 3 -> to == IsAssignableFrom
910                         // 4 -> to == object
911                         static int check_type_with_score (Type from, Type to)
912                         {
913                                 if (from == null)
914                                         return to.IsValueType ? -1 : 0;
915
916                                 if (from == to)
917                                         return 0;
918
919                                 if (to == typeof (object))
920                                         return 4;
921
922                                 TypeCode fromt = Type.GetTypeCode (from);
923                                 TypeCode tot = Type.GetTypeCode (to);
924
925                                 switch (fromt) {
926                                 case TypeCode.Char:
927                                         switch (tot) {
928                                         case TypeCode.UInt16:
929                                                 return 0;
930
931                                         case TypeCode.UInt32:
932                                         case TypeCode.Int32:
933                                         case TypeCode.UInt64:
934                                         case TypeCode.Int64:
935                                         case TypeCode.Single:
936                                         case TypeCode.Double:
937                                                 return 2;
938                                         }
939                                         return -1;
940                                 case TypeCode.Byte:
941                                         switch (tot) {
942                                         case TypeCode.Char:
943                                         case TypeCode.UInt16:
944                                         case TypeCode.Int16:
945                                         case TypeCode.UInt32:
946                                         case TypeCode.Int32:
947                                         case TypeCode.UInt64:
948                                         case TypeCode.Int64:
949                                         case TypeCode.Single:
950                                         case TypeCode.Double:
951                                                 return 2;
952                                         }
953                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
954                                 case TypeCode.SByte:
955                                         switch (tot) {
956                                         case TypeCode.Int16:
957                                         case TypeCode.Int32:
958                                         case TypeCode.Int64:
959                                         case TypeCode.Single:
960                                         case TypeCode.Double:
961                                                 return 2;
962                                         }
963                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
964                                 case TypeCode.UInt16:
965                                         switch (tot) {
966                                         case TypeCode.UInt32:
967                                         case TypeCode.Int32:
968                                         case TypeCode.UInt64:
969                                         case TypeCode.Int64:
970                                         case TypeCode.Single:
971                                         case TypeCode.Double:
972                                                 return 2;
973                                         }
974                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
975                                 case TypeCode.Int16:
976                                         switch (tot) {
977                                         case TypeCode.Int32:
978                                         case TypeCode.Int64:
979                                         case TypeCode.Single:
980                                         case TypeCode.Double:
981                                                 return 2;
982                                         }
983                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
984                                 case TypeCode.UInt32:
985                                         switch (tot) {
986                                         case TypeCode.UInt64:
987                                         case TypeCode.Int64:
988                                         case TypeCode.Single:
989                                         case TypeCode.Double:
990                                                 return 2;
991                                         }
992                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
993                                 case TypeCode.Int32:
994                                         switch (tot) {
995                                         case TypeCode.Int64:
996                                         case TypeCode.Single:
997                                         case TypeCode.Double:
998                                                 return 2;
999                                         }
1000                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
1001                                 case TypeCode.UInt64:
1002                                 case TypeCode.Int64:
1003                                         switch (tot) {
1004                                         case TypeCode.Single:
1005                                         case TypeCode.Double:
1006                                                 return 2;
1007                                         }
1008                                         return (from.IsEnum && to == typeof (Enum)) ? 1 : -1;
1009                                 case TypeCode.Single:
1010                                         return tot == TypeCode.Double ? 2 : -1;
1011                                 default:
1012                                         return (to.IsAssignableFrom (from)) ? 3 : -1;
1013                                 }
1014                         }
1015                 }
1016         }
1017 }
1018