5 // Marco Ridoni (marco.ridoni@virgilio.it)
6 // Satya Sudha K (ksathyasudha@novell.com)
8 // (C) 2003 Marco Ridoni
12 // Copyright (c) 2002-2003 Mainsoft Corporation.
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Reflection;
37 using System.Globalization;
38 using Microsoft.VisualBasic;
39 using System.ComponentModel;
42 namespace Microsoft.VisualBasic.CompilerServices
44 [StandardModule, EditorBrowsableAttribute(EditorBrowsableState.Never)]
45 public class VBBinder : Binder
47 public VBBinder() : base()
51 public VBBinder(bool [] CopyBack) : base()
53 byRefFlags = CopyBack;
56 private class BinderState
59 public bool[] byRefFlags;
63 public enum ConversionType {
70 public override FieldInfo BindToField(
71 BindingFlags bindingAttr,
80 private bool[] byRefFlags;
81 private Type objectType;
82 private string bindToName;
84 public override MethodBase BindToMethod(
85 BindingFlags bindingAttr,
88 ParameterModifier[] modifiers,
94 // Store the arguments to the method in a state object.
95 BinderState binderState = new BinderState();
96 object[] arguments = new Object[args.Length];
97 args.CopyTo(arguments, 0);
98 binderState.args = arguments;
101 MethodBase mbase = null;
102 Type applicable_type = null;
104 if(match == null || match.Length == 0)
105 throw new ArgumentNullException();
109 if (match [0].Name == "Invoke") {
114 ArrayList candidates = new ArrayList ();
115 // Get the list of all suitable methods
116 for (int index = 0; index < match.Length; index ++) {
117 if (IsApplicable (match [index], args))
118 candidates.Add (match [index]);
121 MemberInfo[] tempMatchList = GetMostDerivedMembers (candidates);
122 MethodBase[] filteredMatchList = new MethodBase [tempMatchList.Length];
123 for (int index = 0; index < tempMatchList.Length; index ++)
124 filteredMatchList [index] = (MethodBase) tempMatchList [index];
126 ConversionType bestMatch = ConversionType.None;
127 int numWideningConversions = 0, numNarrowingConversions = 0;
128 ArrayList narrowingConv = new ArrayList ();
129 ArrayList wideningConv = new ArrayList ();
130 for(int x = 0; x < filteredMatchList.Length; x++)
132 ParameterInfo[] parameters = filteredMatchList [x].GetParameters();
133 ConversionType ctype = GetConversionType (parameters, args);
134 if (ctype == ConversionType.None)
136 if (ctype == ConversionType.Widening)
137 wideningConv.Add (filteredMatchList[x]);
138 if (ctype == ConversionType.Narrowing)
139 narrowingConv.Add (filteredMatchList[x]);
140 if (bestMatch == ConversionType.None || ctype < bestMatch) {
142 if (ctype == ConversionType.Narrowing)
143 numNarrowingConversions ++;
144 if (ctype == ConversionType.Widening)
145 numWideningConversions ++;
146 mbase = filteredMatchList [x];
147 } else if (bestMatch == ctype) {
148 if (bestMatch == ConversionType.Widening || bestMatch == ConversionType.Exact) {
149 // Got a widening conversion before also.
150 // Find the best among the two
151 int closestMatch = GetClosestMatch (mbase, filteredMatchList [x], args.Length);
152 if (closestMatch == -1) {
153 numWideningConversions ++;
155 else if (closestMatch == 1)
156 mbase = filteredMatchList [x];
158 numNarrowingConversions ++;
163 if (bestMatch == ConversionType.Narrowing && numNarrowingConversions > 1) {
164 //TODO : print the methods too
165 throw new AmbiguousMatchException ("No overloaded '" + this.objectType + "." + this.bindToName + "' can be called without a narrowing conversion");
167 if ((bestMatch == ConversionType.Widening || bestMatch == ConversionType.Exact) && numWideningConversions > 1) {
168 //TODO : print the methods too
169 throw new AmbiguousMatchException ("No overloaded '" + this.objectType + "." + this.bindToName + "' can be called without a widening conversion");
176 ParameterInfo[] pars = mbase.GetParameters ();
177 if (pars.Length == 0)
179 int numFixedParams = CountStandardParams (pars);
181 if (UsesParamArray (pars)) {
183 int paramArrayIndex = pars.GetUpperBound (0);
184 Type paramArrayType = pars [paramArrayIndex].ParameterType;
185 Array paramArgs = Array.CreateInstance (paramArrayType.GetElementType (), args.Length - paramArrayIndex);
186 bool isArgArray = false;
187 if (pars.GetUpperBound (0) + 1 == args.Length) {
188 if (args [pars.GetUpperBound (0)].GetType().IsArray) {
195 for (int y = paramArrayIndex; y < args.Length; y ++) {
196 Type dest_type = paramArrayType;
197 if (!args [y].GetType ().IsArray) {
198 dest_type = paramArrayType.GetElementType ();
200 if((args [y] = ObjectType.CTypeHelper (args[y], dest_type)) != null) {
201 paramArgs.SetValue (args [y], index);
207 object[] newArgs = new object [pars.Length];
208 Array.Copy (args, newArgs, paramArrayIndex);
209 newArgs [newArgs.GetUpperBound (0)] = paramArgs;
214 object[] newArguments = new object [pars.Length];
215 args.CopyTo (newArguments, 0);
216 int numExpectedArgs = pars.Length;
217 if (UsesParamArray (pars))
219 for(int y = 0; y < numExpectedArgs; y++)
221 if (y < args.Length && (args [y] is Missing || args[y] == null))
222 newArguments [y] = pars [y].DefaultValue;
223 else if (y >= args.Length)
224 newArguments [y] = pars [y].DefaultValue;
226 newArguments [y] = args [y];
229 for(int y = 0; y < numExpectedArgs; y++) {
230 if (newArguments [y] == null)
233 if((newArguments [y] = ObjectType.CTypeHelper (newArguments[y], pars[y].ParameterType)) == null)
237 ((BinderState) state).args = newArguments;
239 if (byRefFlags == null || pars.Length == 0) {
244 for (int index = 0; index < byRefFlags.Length; index ++) {
246 if (index >= pars.Length)
247 paramIndex = pars.GetUpperBound (0);
248 ParameterInfo p = pars [paramIndex];
249 if (p.ParameterType.IsByRef) {
250 if (byRefFlags [index] != false)
251 byRefFlags [index] = true;
253 byRefFlags [index] = false;
259 private int GetClosestMatch (MethodBase bestMatch, MethodBase candidate, int argCount) {
260 // flag to indicate which one has been better so far
261 // -1 : none is better than other
262 // 0 : bestMatch has been better so far
263 // 1 : candidate is better than bestMatch
265 ParameterInfo[] bestMatchParams = bestMatch.GetParameters ();
266 ParameterInfo[] candidateParams = candidate.GetParameters ();
267 int numParams = Math.Min (bestMatchParams.Length, candidateParams.Length);
268 int paramArrayIndex1 = -1, paramArrayIndex2 = -1;
269 if (UsesParamArray (bestMatchParams)) {
270 paramArrayIndex1 = (bestMatchParams.Length > 0) ? (bestMatchParams.Length - 1) : -1;
273 if (UsesParamArray (candidateParams)) {
274 paramArrayIndex2 = (candidateParams.Length > 0) ? (candidateParams.Length - 1) : -1;
277 for (int i = 0; i < argCount; i ++) {
278 int index1 = i, index2 = i;
279 Type bestMatchParamsType = null;
280 Type candParamType = null;
281 if (i >= paramArrayIndex1 && paramArrayIndex1 != -1) {
282 index1 = paramArrayIndex1;
283 bestMatchParamsType = bestMatchParams [index1].ParameterType.GetElementType ();
285 bestMatchParamsType = bestMatchParams [index1].ParameterType;
286 if (i >= paramArrayIndex2 && paramArrayIndex2 != -1) {
287 index2 = paramArrayIndex2;
288 candParamType = candidateParams [index2].ParameterType.GetElementType ();
290 candParamType = candidateParams [index2].ParameterType;
293 if (bestMatchParamsType == candParamType)
296 if (ObjectType.IsWideningConversion (bestMatchParamsType, candParamType)) {
297 // ith param of candidate is wider than that of bestMatch
298 if (isBetter == -2) {
301 } else if (isBetter != 0) {
307 } else if (ObjectType.IsWideningConversion (candParamType, bestMatchParamsType)) {
308 // ith param of bestMatch is wider than that of candidate
309 if (isBetter == -2) {
312 } else if (isBetter != 1) {
320 if (isBetter == -2) {
321 // corresponding parameters of both methods have same types
322 // the method having max no of fixed parameters is better
323 if (paramArrayIndex1 == -1 && paramArrayIndex2 != -1)
325 if (paramArrayIndex1 != -1 && paramArrayIndex2 == -1)
327 return ((paramArrayIndex1 < paramArrayIndex2) ? 1 : 0);
333 internal static bool UsesParamArray (ParameterInfo [] pars) {
334 if (pars == null || pars.Length == 0)
336 ParameterInfo lastParam = pars [pars.GetUpperBound (0)];
337 object[] attrs = lastParam.GetCustomAttributes (typeof (System.ParamArrayAttribute), false);
338 if (attrs == null || attrs.Length == 0)
344 Gets the number of parameters that are neither optional not ParamArray
346 internal static int CountStandardParams (ParameterInfo [] pars) {
347 if (pars.Length == 0)
349 int count = pars.Length;
350 for (int index = 0; index < pars.Length; index ++) {
351 ParameterInfo param = pars [index];
352 if (param.IsOptional)
354 if (index + 1 == pars.Length) {
355 object[] attrs = param.GetCustomAttributes (typeof (System.ParamArrayAttribute), false);
356 if (attrs != null && attrs.Length > 0)
363 private ConversionType GetConversionType (ParameterInfo[] parameters, object[] args) {
364 int numParams = parameters.Length;
365 int numArgs = args.Length;
366 int paramArrayIndex = -1;
367 int numFixedParams = CountStandardParams (parameters);
369 if (numParams == 0) {
371 return ConversionType.Exact;
373 return ConversionType.None;
376 bool usesParamArray = UsesParamArray (parameters);
377 if (numFixedParams == 0 && numArgs == 0)
378 return ConversionType.Exact;
380 if (numArgs < numFixedParams)
381 return ConversionType.None;
383 ConversionType ctype = ConversionType.None;
384 bool isLastParam = false;
386 for (int index = 0; index < numArgs; index ++) {
387 if (index < numFixedParams)
389 else if (usesParamArray) {
390 paramIndex = parameters.GetUpperBound (0);
394 ConversionType currentCType = ConversionType.None;
396 if (args [index] != null)
397 type1 = args [index].GetType ();
398 Type type2 = parameters [paramIndex].ParameterType;
400 type2 = type2.GetElementType ();
401 if (usesParamArray && isLastParam) {
403 if (type1.GetElementType () == type2.GetElementType ())
404 currentCType = ConversionType.Exact;
405 else if (ObjectType.IsWideningConversion (type1, type2))
406 currentCType = ConversionType.Widening;
408 currentCType = ConversionType.Narrowing;
410 Type elementType = type2.GetElementType ();
411 if (type1 == elementType)
412 currentCType = ConversionType.Exact;
413 else if (ObjectType.IsWideningConversion (type1, elementType))
414 currentCType = ConversionType.Widening;
416 currentCType = ConversionType.Narrowing;
418 if (currentCType == ConversionType.Narrowing || ctype < currentCType)
419 ctype = currentCType;
422 if (type1 == type2) {
423 currentCType = ConversionType.Exact;
424 if (ctype < currentCType) {
425 ctype = ConversionType.Exact;
427 } else if (ObjectType.IsWideningConversion (type1, type2)) {
428 currentCType = ConversionType.Widening;
429 if (ctype < currentCType)
430 ctype = ConversionType.Widening;
432 ctype = ConversionType.Narrowing;
439 public override object ChangeType(
445 TypeCode src_type = Type.GetTypeCode (value.GetType());
446 TypeCode dest_type = Type.GetTypeCode (myChangeType);
449 case TypeCode.String:
453 return (StringType.FromByte ((byte)value));
454 case TypeCode.UInt16:
456 return (StringType.FromShort ((short)value));
457 case TypeCode.UInt32:
459 return (StringType.FromInteger ((int)value));
460 case TypeCode.UInt64:
462 return (StringType.FromLong ((long)value));
464 return (StringType.FromChar ((char)value));
465 case TypeCode.Single:
466 return (StringType.FromSingle ((float)value));
467 case TypeCode.Double:
468 return (StringType.FromDouble ((double)value));
469 case TypeCode.Boolean:
470 return (StringType.FromBoolean ((bool)value));
471 case TypeCode.Object:
472 return (StringType.FromObject (value));
477 case TypeCode.UInt32:
479 case TypeCode.String:
480 return (IntegerType.FromString ((string)value));
481 case TypeCode.Object:
482 return (IntegerType.FromObject (value));
487 case TypeCode.UInt16:
489 case TypeCode.String:
490 return (ShortType.FromString ((string)value));
491 case TypeCode.Object:
492 return (ShortType.FromObject (value));
495 case TypeCode.Object:
496 return ((Object) value);
501 public override void ReorderArgumentArray(
509 public override MethodBase SelectMethod(
510 BindingFlags bindingAttr,
513 ParameterModifier[] modifiers
519 public override PropertyInfo SelectProperty(
520 BindingFlags bindingAttr,
521 PropertyInfo[] match,
524 ParameterModifier[] modifiers
530 public Object InvokeMember (string name,
536 ParameterModifier[] modifiers,
538 string[] paramNames) {
540 this.objectType = objType;
541 this.bindToName = name;
542 if (name == null || name.Equals ("")) {
543 // Must be a default property
546 object[] attrArray = t.GetCustomAttributes (typeof (DefaultMemberAttribute), false);
547 if (attrArray != null && attrArray.Length != 0) {
548 name = ((DefaultMemberAttribute) attrArray[0]).MemberName;
551 // not found, search in the base type
556 if (name == null || name.Equals ("")) {
557 throw new MissingMemberException ("No default members defined for type '" + objType + "'");
560 MemberInfo[] memberinfo = GetMembers (objReflect, objType, name, flags);
562 if (memberinfo == null || memberinfo.Length == 0) {
563 throw new MissingMemberException ("No member '" + name + "' defined for type '" + objType + "'");
566 object objState = null;
567 object retVal = null;
568 if (memberinfo [0] is MethodBase) {
569 MethodBase[] methodbase = new MethodBase [memberinfo.Length];
570 for (int index = 0; index < memberinfo.Length; index ++)
571 methodbase [index] = (MethodBase) memberinfo [index];
572 MethodBase mbase = BindToMethod (flags, methodbase, ref args, modifiers, culture, paramNames, out objState);
574 throw new MissingMemberException ("No member '" + name + "' defined for type '" + objType + "' which takes the given set of arguments");
577 object [] newArgs = args;
578 if (objState != null)
579 newArgs = ((BinderState) objState).args;
581 MethodInfo mi = (MethodInfo) mbase;
582 retVal = mi.Invoke (target, newArgs);
583 Array.Copy (newArgs, args, args.Length);
586 if (objState != null && ((BinderState)objState).byRefFlags != null) {
587 this.byRefFlags = ((BinderState)objState).byRefFlags;
594 Determines whether a given method can be invoked with a given set of arguments
596 private bool IsApplicable (MethodBase mb, object [] args) {
597 ParameterInfo [] parameters = mb.GetParameters ();
598 int numFixedParams = CountStandardParams (parameters);
599 int numParams = parameters.Length;
602 argCount = args.Length;
604 if (numParams == numFixedParams) // No ParamArray or Optional params
605 if (argCount != numParams)
608 if (argCount < numFixedParams)
611 bool usesParamArray = UsesParamArray (parameters);
612 if (!usesParamArray && (argCount > numParams))
616 bool isLastParam = false;
617 for (int index = 0; index < argCount; index ++) {
619 Type paramType = null;
621 if (index < numFixedParams)
623 else if (usesParamArray) {
624 paramIndex = numParams - 1;
628 paramType = parameters [paramIndex].ParameterType;
629 if (args [index] != null) {
630 argType = args [index].GetType ();
631 if (usesParamArray && isLastParam) {
632 // ParamArray parameter of type 'T'. We can either have an
633 // argument which is an array of type T, or 'n' number of
634 // arguments that are of/convertible to type 'T'
635 if (!argType.IsArray) {
636 Type elementType = paramType.GetElementType ();
637 if (!ObjectType.ImplicitConversionExists (argType, elementType))
640 Type elementType = paramType.GetElementType ();
641 argType = argType.GetElementType ();
642 if (!elementType.IsAssignableFrom (argType))
646 if (paramType.IsByRef)
647 paramType = paramType.GetElementType ();
648 if (!ObjectType.ImplicitConversionExists (argType, paramType))
657 private static MemberInfo [] GetMostDerivedMembers (ArrayList memberinfo) {
659 int numElementsEliminated = 0;
660 for (i = 0; i < memberinfo.Count; i++) {
661 MemberInfo mi = (MemberInfo) memberinfo [i];
662 for (int j = i + 1; j < memberinfo.Count; j++) {
663 bool eliminateBaseMembers = false;
664 MemberInfo thisMember = (MemberInfo) memberinfo [j];
665 Type t1 = mi.DeclaringType;
666 Type t2 = thisMember.DeclaringType;
667 if (mi.MemberType == MemberTypes.Field)
668 eliminateBaseMembers = true;
669 if (mi.MemberType == MemberTypes.Method) {
670 MethodInfo methodinfo = (MethodInfo) mi;
671 if (methodinfo.IsVirtual)
672 eliminateBaseMembers = true;
674 if (mi.MemberType == MemberTypes.Property) {
675 PropertyInfo propertyinfo = (PropertyInfo) mi;
676 MethodInfo method = propertyinfo.GetGetMethod ();
677 if (method.IsVirtual)
678 eliminateBaseMembers = true;
680 if (eliminateBaseMembers) {
681 if (t1.IsSubclassOf (t2)) {
682 memberinfo [j] = null;
683 numElementsEliminated ++;
684 } else if (t2.IsSubclassOf (t1)) {
685 memberinfo [i] = null;
686 numElementsEliminated ++;
692 MemberInfo [] newMemberList = new MemberInfo [memberinfo.Count - numElementsEliminated];
694 for (int index = 0; index < memberinfo.Count; index ++) {
695 if (memberinfo [index] != null) {
696 newMemberList [newIndex ++] = (MemberInfo) memberinfo [index];
699 return newMemberList;
702 internal MemberInfo [] GetMembers (IReflect objReflect, Type objType, string name, BindingFlags invokeFlags)
704 MemberInfo [] mi = objReflect.GetMember (name, invokeFlags);
705 if (mi == null || mi.Length == 0)
708 for (int index = 0; index < mi.Length; index ++)
710 if (mi [index].MemberType == MemberTypes.Property) {
711 PropertyInfo propinfo = (PropertyInfo) mi [index];
712 if ((invokeFlags & BindingFlags.GetProperty) == BindingFlags.GetProperty)
713 mi [index] = propinfo.GetGetMethod ();
714 else if ((invokeFlags & BindingFlags.SetProperty) == BindingFlags.SetProperty)
715 mi [index] = propinfo.GetSetMethod ();