mirror of
https://github.com/SSCLI/sscli_20021101
synced 2026-06-08 12:28:57 +00:00
9fa3874800
Moved the original file to the archive subfolder.
913 lines
39 KiB
C#
913 lines
39 KiB
C#
// ==++==
|
|
//
|
|
//
|
|
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// The use and distribution terms for this software are contained in the file
|
|
// named license.txt, which can be found in the root of this distribution.
|
|
// By using this software in any fashion, you are agreeing to be bound by the
|
|
// terms of this license.
|
|
//
|
|
// You must not remove this notice, or any other, from this software.
|
|
//
|
|
//
|
|
// ==--==
|
|
namespace Microsoft.JScript
|
|
{
|
|
|
|
using System;
|
|
using System.Reflection;
|
|
using System.Globalization;
|
|
using System.Collections;
|
|
|
|
internal sealed class JSBinder : Binder{
|
|
internal static readonly JSBinder ob = new JSBinder();
|
|
|
|
// both args and namedParameters cannot be null and args length must be bigger or equal to namedParameters.Length
|
|
internal static Object[] ArrangeNamedArguments(MethodBase method, Object[] args, string[] namedParameters){
|
|
ParameterInfo[] pars = method.GetParameters();
|
|
int formalLength = pars.Length;
|
|
if (formalLength == 0)
|
|
throw new JScriptException(JSError.MissingNameParameter);
|
|
Object[] newArgs = new Object[formalLength];
|
|
int argsLength = args.Length;
|
|
int namesLength = namedParameters.Length;
|
|
int numPositional = argsLength - namesLength;
|
|
ArrayObject.Copy(args, namesLength, newArgs, 0, numPositional);
|
|
for (int i = 0; i < namesLength; i++){
|
|
string name = namedParameters[i];
|
|
if (name == null || name.Equals(""))
|
|
throw new JScriptException(JSError.MustProvideNameForNamedParameter);
|
|
int j;
|
|
for (j = numPositional; j < formalLength; j++){
|
|
if (name.Equals(pars[j].Name))
|
|
if (newArgs[j] is Empty)
|
|
throw new JScriptException(JSError.DuplicateNamedParameter);
|
|
else{
|
|
newArgs[j] = args[i];
|
|
break;
|
|
}
|
|
}
|
|
if (j == formalLength)
|
|
throw new JScriptException(JSError.MissingNameParameter);
|
|
}
|
|
// go through the array and replace null/missing entries with the default value of the corresponding param (if any)
|
|
if (method is JSMethod) return newArgs; //JSMethods get special treatment elsewhere
|
|
for (int i = 0; i < formalLength; i++)
|
|
if (newArgs[i] == null || newArgs[i] == Missing.Value){
|
|
Object dv = pars[i].DefaultValue;
|
|
if (dv == System.Convert.DBNull) //No default value was specified
|
|
throw new ArgumentException(pars[i].Name);
|
|
newArgs[i] = dv;
|
|
}
|
|
return newArgs;
|
|
}
|
|
|
|
public override FieldInfo BindToField(BindingFlags bindAttr, FieldInfo[] match, Object value, CultureInfo locale){
|
|
if (value == null)
|
|
value = DBNull.Value;
|
|
int minval = Int32.MaxValue;
|
|
int dupCount = 0;
|
|
FieldInfo bestField = null;
|
|
Type vtype = value.GetType();
|
|
for (int i = 0, j = match.Length; i < j; i++){
|
|
FieldInfo field = match[i];
|
|
int distance = TypeDistance(field.FieldType, vtype);
|
|
if (distance < minval){
|
|
minval = distance;
|
|
bestField = field;
|
|
dupCount = 0;
|
|
}else if (distance == minval)
|
|
dupCount += 1;
|
|
}
|
|
if (dupCount > 0)
|
|
throw new AmbiguousMatchException();
|
|
return bestField;
|
|
}
|
|
|
|
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref Object[] args, ParameterModifier[] modifiers,
|
|
CultureInfo locale, String[] namedParameters, out Object state){
|
|
state = null;
|
|
return JSBinder.SelectMethodBase(match, ref args, modifiers, namedParameters);
|
|
}
|
|
|
|
public override Object ChangeType(Object value, Type target_type, CultureInfo locale){
|
|
return Convert.CoerceT(value, target_type);
|
|
}
|
|
|
|
internal static MemberInfo[] GetDefaultMembers(IReflect ir){
|
|
while (ir is ClassScope){
|
|
ClassScope csc = (ClassScope)ir;
|
|
csc.owner.IsExpando(); //Force the class to expose its expando property if it has not already done so
|
|
if (csc.itemProp != null) return new MemberInfo[]{csc.itemProp};
|
|
ir = csc.GetParent();
|
|
if (ir is WithObject) ir = (IReflect)((WithObject)ir).contained_object;
|
|
}
|
|
if (ir is Type) return JSBinder.GetDefaultMembers((Type)ir);
|
|
if (ir is JSObject) return (typeof(ScriptObject)).GetDefaultMembers();
|
|
return null;
|
|
}
|
|
|
|
internal static MemberInfo[] GetDefaultMembers(Type t){
|
|
while (t != Typeob.Object && t != null){
|
|
MemberInfo[] defaultMembers = t.GetDefaultMembers();
|
|
if (defaultMembers != null && defaultMembers.Length > 0)
|
|
return defaultMembers;
|
|
t = t.BaseType;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal static MethodInfo GetDefaultPropertyForArrayIndex(Type t, int index, Type elementType, bool getSetter){
|
|
try{
|
|
MemberInfo[] defaultMembers = JSBinder.GetDefaultMembers(t);
|
|
int n = 0;
|
|
if (defaultMembers == null || (n = defaultMembers.Length) == 0)
|
|
return null;
|
|
for (int i = 0; i < n; i++){
|
|
MemberInfo mem = defaultMembers[i];
|
|
MemberTypes mt = mem.MemberType;
|
|
MethodInfo meth = null;
|
|
switch (mt){
|
|
case MemberTypes.Property:
|
|
meth = ((PropertyInfo)mem).GetGetMethod();
|
|
break;
|
|
case MemberTypes.Method:
|
|
meth = (MethodInfo)mem;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (meth != null){
|
|
ParameterInfo[] pars = meth.GetParameters();
|
|
if (pars == null || pars.Length == 0){
|
|
//See if meth is a default method/property-getter that returns an Array or List
|
|
Type rt = meth.ReturnType;
|
|
if (typeof(Array).IsAssignableFrom(rt) || typeof(IList).IsAssignableFrom(rt))
|
|
return meth;
|
|
}else if (pars.Length == 1 && mt == MemberTypes.Property){
|
|
//See if meth is the getter of a suitable default indexed property
|
|
PropertyInfo prop = (PropertyInfo)mem;
|
|
if (elementType == null || prop.PropertyType.IsAssignableFrom(elementType))
|
|
try{
|
|
Convert.CoerceT(index, pars[0].ParameterType);
|
|
if (getSetter)
|
|
return prop.GetSetMethod();
|
|
else
|
|
return meth;
|
|
}catch(JScriptException){}
|
|
}
|
|
}
|
|
}
|
|
}catch(InvalidOperationException){}
|
|
return null;
|
|
}
|
|
|
|
internal static MemberInfo[] GetInterfaceMembers(String name, Type t){
|
|
BindingFlags flags = BindingFlags.Public|BindingFlags.Instance|BindingFlags.Static|BindingFlags.DeclaredOnly;
|
|
MemberInfo[] members = t.GetMember(name, flags);
|
|
Type[] baseInts = t.GetInterfaces();
|
|
if (baseInts == null || baseInts.Length == 0) return members;
|
|
ArrayList baseInterfaces = new ArrayList(baseInts);
|
|
MemberInfoList result = new MemberInfoList();
|
|
result.AddRange(members);
|
|
for (int i = 0; i < baseInterfaces.Count; i++){
|
|
Type bi = (Type)baseInterfaces[i];
|
|
members = bi.GetMember(name, flags);
|
|
if (members != null) result.AddRange(members);
|
|
foreach (Type bbi in bi.GetInterfaces()){
|
|
if (baseInterfaces.IndexOf(bbi) == -1)
|
|
baseInterfaces.Add(bbi);
|
|
}
|
|
}
|
|
return result.ToArray();
|
|
}
|
|
|
|
private static bool FormalParamTypeIsObject(ParameterInfo par){
|
|
ParameterDeclaration pd = par as ParameterDeclaration;
|
|
if (pd != null) return pd.ParameterIReflect == Typeob.Object;
|
|
return par.ParameterType == Typeob.Object;
|
|
}
|
|
|
|
public override void ReorderArgumentArray(ref Object[] args, Object state){
|
|
}
|
|
|
|
internal static MemberInfo Select(MemberInfo[] match, int matches, IReflect[] argIRs, MemberTypes memberType){
|
|
Debug.Assert(matches > 1);
|
|
int candidates = 0;
|
|
ParameterInfo[][] fparams = new ParameterInfo[matches][];
|
|
bool lookForPropertyGetters = memberType == MemberTypes.Method;
|
|
for (int i = 0; i < matches; i++){
|
|
MemberInfo mem = match[i];
|
|
if (mem is PropertyInfo && lookForPropertyGetters) mem = ((PropertyInfo)mem).GetGetMethod(true);
|
|
if (mem == null) continue;
|
|
if (mem.MemberType == memberType){
|
|
if (mem is PropertyInfo)
|
|
fparams[i] = ((PropertyInfo)mem).GetIndexParameters();
|
|
else
|
|
fparams[i] = ((MethodBase)mem).GetParameters();
|
|
candidates++;
|
|
}
|
|
}
|
|
int j = JSBinder.SelectBest(match, matches, argIRs, fparams, null, candidates, argIRs.Length);
|
|
if (j < 0) return null;
|
|
return match[j];
|
|
}
|
|
|
|
internal static MemberInfo Select(MemberInfo[] match, int matches, ref Object[] args, String[] namedParameters, MemberTypes memberType){
|
|
Debug.Assert(matches > 1);
|
|
bool hasNamedParams = false;
|
|
if (namedParameters != null && namedParameters.Length > 0)
|
|
if (args.Length >= namedParameters.Length)
|
|
hasNamedParams = true;
|
|
else
|
|
throw new JScriptException(JSError.MoreNamedParametersThanArguments);
|
|
int candidates = 0;
|
|
ParameterInfo[][] fparams = new ParameterInfo[matches][];
|
|
Object[][] aparams = new Object[matches][];
|
|
bool lookForPropertyGetters = memberType == MemberTypes.Method;
|
|
for (int i = 0; i < matches; i++){
|
|
MemberInfo mem = match[i];
|
|
if (lookForPropertyGetters && mem.MemberType == MemberTypes.Property)
|
|
mem = ((PropertyInfo)mem).GetGetMethod(true);
|
|
if (mem.MemberType == memberType){
|
|
if (memberType == MemberTypes.Property)
|
|
fparams[i] = ((PropertyInfo)mem).GetIndexParameters();
|
|
else
|
|
fparams[i] = ((MethodBase)mem).GetParameters();
|
|
if (hasNamedParams)
|
|
aparams[i] = JSBinder.ArrangeNamedArguments((MethodBase)mem, args, namedParameters);
|
|
else
|
|
aparams[i] = args;
|
|
candidates++;
|
|
}
|
|
}
|
|
int j = JSBinder.SelectBest(match, matches, null, fparams, aparams, candidates, args.Length);
|
|
if (j < 0) return null;
|
|
args = aparams[j];
|
|
MemberInfo result = match[j];
|
|
if (lookForPropertyGetters && result.MemberType == MemberTypes.Property)
|
|
result = ((PropertyInfo)result).GetGetMethod(true);
|
|
return result;
|
|
}
|
|
|
|
private static int SelectBest(MemberInfo[] match, int matches, IReflect[] argIRs, ParameterInfo[][] fparams, Object[][] aparams, int candidates, int parameters){
|
|
Debug.Assert(matches > 1);
|
|
if (candidates == 0) return -1;
|
|
if (candidates == 1)
|
|
for (int i = 0; i < matches; i++)
|
|
if (fparams[i] != null) return i;
|
|
bool[] eliminated = new bool[matches];
|
|
//Set up penalties to discourage selection of methods that have more formal parameters than there are actual parameters
|
|
int[] penalty = new int[matches];
|
|
for (int i = 0; i < matches; i++){
|
|
ParameterInfo[] fpars = fparams[i];
|
|
if (fpars != null){
|
|
int m = fpars.Length;
|
|
int actuals = (argIRs == null ? aparams[i].Length : argIRs.Length);
|
|
// Eliminate the candidates which have fewer formal parameters than actuals ( and for which
|
|
// the last formal parameter is not a param-array). We do not consider these in overload
|
|
// resolution.
|
|
if (actuals > m && (m == 0 || !fpars[m-1].IsDefined(Typeob.ParamArrayAttribute, false))){
|
|
fparams[i] = null;
|
|
candidates--;
|
|
Debug.Assert(candidates >= 0);
|
|
continue;
|
|
}
|
|
for (int j = parameters; j < m; j++){
|
|
ParameterInfo fpar = fpars[j];
|
|
if (j == m-1 && fpar.IsDefined(Typeob.ParamArrayAttribute, false))
|
|
break;
|
|
Object dv = fpar.DefaultValue;
|
|
if (dv is System.DBNull){
|
|
//No default value, set up a penalty
|
|
penalty[i] = 50;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int p = 0; candidates > 1; p++){
|
|
int candidatesWithFormalParametersStillToBeConsidered = 0;
|
|
//Eliminate any candidate that is worse match than any other candidate for this (possibly missing) actual parameter
|
|
int minval = Int32.MaxValue;
|
|
bool objectCanWin = false;
|
|
for (int i = 0; i < matches; i++){
|
|
ParameterInfo[] fpars = fparams[i];
|
|
if (fpars != null){ //match[i] is a candidate
|
|
IReflect aIR = Typeob.Missing;
|
|
if (argIRs == null){
|
|
if (aparams[i].Length > p){
|
|
Object apar = aparams[i][p];
|
|
if (apar == null) apar = DBNull.Value;
|
|
aIR = apar.GetType();
|
|
}
|
|
}else if (p < parameters)
|
|
aIR = argIRs[p];
|
|
int m = fpars.Length;
|
|
if (m-1 > p)
|
|
candidatesWithFormalParametersStillToBeConsidered++;
|
|
IReflect fIR = Typeob.Missing;
|
|
if (m > 0 && p >= m-1 && fpars[m-1].IsDefined(Typeob.ParamArrayAttribute, false) &&
|
|
!(aIR is TypedArray || aIR == Typeob.ArrayObject || (aIR is Type && ((Type)aIR).IsArray))){
|
|
ParameterInfo fpar = fpars[m-1];
|
|
if (fpar is ParameterDeclaration){
|
|
fIR = ((ParameterDeclaration)fpar).ParameterIReflect;
|
|
fIR = ((TypedArray)fIR).elementType;
|
|
}else
|
|
fIR = fpar.ParameterType.GetElementType();
|
|
if (p == m-1) penalty[i]++;
|
|
}else if (p < m){
|
|
ParameterInfo fpar = fpars[p];
|
|
fIR = fpar is ParameterDeclaration ? ((ParameterDeclaration)fpar).ParameterIReflect : fpar.ParameterType;
|
|
if (aIR == Typeob.Missing){
|
|
//No actual parameter was supplied, if the formal parameter has default value, make the match perfect
|
|
Object dv = fpar.DefaultValue;
|
|
if (!(dv is System.DBNull))
|
|
aIR = fIR;
|
|
}
|
|
}
|
|
int distance = TypeDistance(fIR, aIR) + penalty[i];
|
|
if (distance == minval){
|
|
if (p == m-1 && eliminated[i]){
|
|
candidates--;
|
|
fparams[i] = null;
|
|
}
|
|
objectCanWin = eliminated[i];
|
|
continue;
|
|
}
|
|
if (distance > minval){
|
|
if (objectCanWin && p < m && JSBinder.FormalParamTypeIsObject(fparams[i][p])){
|
|
minval = distance; //Make sure that a future, better match than Object can win.
|
|
continue;
|
|
}
|
|
if (p > m-1 && aIR == Typeob.Missing && fpars[m-1].IsDefined(Typeob.ParamArrayAttribute, false)) continue;
|
|
eliminated[i] = true;
|
|
continue;
|
|
}
|
|
//If we get here, we have a match that is strictly better than all previous matches.
|
|
//If there are no other remaining candidates, we're done.
|
|
if (candidates == 1 && !eliminated[i])
|
|
return i;
|
|
//Eliminate those other candidates from consideration.
|
|
objectCanWin = eliminated[i];
|
|
for (int j = 0; j < i; j++)
|
|
if (fparams[j] != null && !eliminated[j]){
|
|
bool noFormalForCurrParam = fparams[j].Length <= p;
|
|
if (noFormalForCurrParam && parameters <= p)
|
|
//Do not eliminate an overload if it does not have a formal for the current position
|
|
//and the call does not have an actual parameter for the current position either.
|
|
continue;
|
|
if (noFormalForCurrParam || !objectCanWin || !JSBinder.FormalParamTypeIsObject(fparams[j][p]))
|
|
eliminated[j] = true;
|
|
}
|
|
minval = distance;
|
|
}
|
|
}
|
|
if (p >= parameters-1 && candidatesWithFormalParametersStillToBeConsidered < 1)
|
|
//Looked at all actual parameters as well as all formal parameters, no further progress can be made
|
|
break;
|
|
}
|
|
int best = -1;
|
|
for (int j = 0; j < matches && candidates > 0; j++){
|
|
ParameterInfo[] fpars = fparams[j];
|
|
if (fpars != null){
|
|
if (eliminated[j]){
|
|
candidates--; fparams[j] = null; continue;
|
|
}
|
|
int pc = fpars.Length;
|
|
if (best == -1){ //Choose the first remaining candidate as "best"
|
|
best = j; continue;
|
|
}
|
|
if (Class.ParametersMatch(fpars, fparams[best])){
|
|
MemberInfo bstm = match[best];
|
|
JSWrappedMethod jswm = match[best] as JSWrappedMethod;
|
|
if (jswm != null) bstm = jswm.method;
|
|
if (bstm is JSFieldMethod || bstm is JSConstructor || bstm is JSProperty){
|
|
//The first match is always the most derived, go with it
|
|
candidates--; fparams[j] = null; continue;
|
|
}
|
|
Type bestMemT = match[best].DeclaringType;
|
|
Type memT = match[j].DeclaringType;
|
|
if (bestMemT != memT){
|
|
if (memT.IsAssignableFrom(bestMemT)){
|
|
candidates--; fparams[j] = null; continue;
|
|
}else if (bestMemT.IsAssignableFrom(memT)){
|
|
fparams[best] = null; best = j; candidates--; continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (candidates != 1)
|
|
throw new AmbiguousMatchException();
|
|
return best;
|
|
}
|
|
|
|
|
|
internal static ConstructorInfo SelectConstructor(MemberInfo[] match, ref Object[] args, String[] namedParameters){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 0) return null;
|
|
if (n == 1){
|
|
Type t = match[0] as Type;
|
|
if (t != null) {
|
|
match = t.GetConstructors();
|
|
n = match.Length;
|
|
}
|
|
}
|
|
if (n == 1) return match[0] as ConstructorInfo;
|
|
return (ConstructorInfo)JSBinder.Select(match, n, ref args, namedParameters, MemberTypes.Constructor);
|
|
}
|
|
|
|
internal static ConstructorInfo SelectConstructor(MemberInfo[] match, IReflect[] argIRs){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 1){
|
|
Object val = match[0];
|
|
if (val is JSGlobalField) val = ((JSGlobalField)val).GetValue(null);
|
|
Type t = val as Type;
|
|
if (t != null){
|
|
match = t.GetConstructors();
|
|
}
|
|
n = match.Length;
|
|
}
|
|
if (n == 0) return null;
|
|
if (n == 1) return match[0] as ConstructorInfo;
|
|
return (ConstructorInfo)JSBinder.Select(match, n, argIRs, MemberTypes.Constructor);
|
|
}
|
|
|
|
internal static MethodInfo SelectMethod(MemberInfo[] match, ref Object[] args, String[] namedParameters){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 0) return null;
|
|
MemberInfo result = n == 1 ? match[0] : JSBinder.Select(match, n, ref args, namedParameters, MemberTypes.Method);
|
|
if (result != null && result.MemberType == MemberTypes.Property)
|
|
result = ((PropertyInfo)result).GetGetMethod(true);
|
|
return result as MethodInfo;
|
|
}
|
|
|
|
internal static MethodInfo SelectMethod(MemberInfo[] match, IReflect[] argIRs){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 0) return null;
|
|
MemberInfo result = n == 1 ? match[0] : JSBinder.Select(match, n, argIRs, MemberTypes.Method);
|
|
if (result != null && result.MemberType == MemberTypes.Property)
|
|
return ((PropertyInfo)result).GetGetMethod(true);
|
|
return result as MethodInfo;
|
|
}
|
|
|
|
public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 0) return null;
|
|
if (n == 1) return match[0];
|
|
if (match[0].MemberType == MemberTypes.Constructor)
|
|
return (ConstructorInfo)JSBinder.Select(match, n, types, MemberTypes.Constructor);
|
|
else
|
|
return (MethodInfo)JSBinder.Select(match, n, types, MemberTypes.Method);
|
|
}
|
|
|
|
private static MethodBase SelectMethodBase(MethodBase[] match, ref Object[] args, ParameterModifier[] modifiers, String[] namedParameters){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 0) return null;
|
|
if (n == 1) return match[0];
|
|
MethodBase result = (MethodBase)JSBinder.Select(match, n, ref args, namedParameters, MemberTypes.Method);
|
|
if (result == null)
|
|
result = (MethodBase)JSBinder.Select(match, n, ref args, namedParameters, MemberTypes.Constructor);
|
|
return result;
|
|
}
|
|
|
|
internal static MethodInfo SelectOperator(MethodInfo op1, MethodInfo op2, Type t1, Type t2){
|
|
ParameterInfo[] params1 = null;
|
|
if (op1 == null || (op1.Attributes & MethodAttributes.SpecialName) == 0 || (params1 = op1.GetParameters()).Length != 2)
|
|
op1 = null;
|
|
ParameterInfo[] params2 = null;
|
|
if (op2 == null || (op2.Attributes & MethodAttributes.SpecialName) == 0 || (params2 = op2.GetParameters()).Length != 2)
|
|
op2 = null;
|
|
if (op1 == null) return op2;
|
|
if (op2 == null) return op1;
|
|
int op1cost = TypeDistance(params1[0].ParameterType, t1) + TypeDistance(params1[1].ParameterType, t2);
|
|
int op2cost = TypeDistance(params2[0].ParameterType, t1) + TypeDistance(params2[1].ParameterType, t2);
|
|
if (op1cost <= op2cost) return op1;
|
|
return op2;
|
|
}
|
|
|
|
internal static PropertyInfo SelectProperty(MemberInfo[] match, Object[] args){
|
|
if (match == null) return null;
|
|
int matches = match.Length;
|
|
if (matches == 0) return null;
|
|
if (matches == 1) return match[0] as PropertyInfo;
|
|
int candidates = 0;
|
|
PropertyInfo result = null;
|
|
ParameterInfo[][] fparams = new ParameterInfo[matches][];
|
|
Object[][] aparams = new Object[matches][];
|
|
for (int i = 0; i < matches; i++){
|
|
MemberInfo mem = match[i];
|
|
if (mem.MemberType == MemberTypes.Property){
|
|
MethodInfo getter = (result = (PropertyInfo)mem).GetGetMethod(true);
|
|
if (getter == null)
|
|
fparams[i] = result.GetIndexParameters();
|
|
else
|
|
fparams[i] = getter.GetParameters();
|
|
aparams[i] = args;
|
|
candidates++;
|
|
}
|
|
}
|
|
if (candidates <= 1) return result;
|
|
int j = JSBinder.SelectBest(match, matches, null, fparams, aparams, candidates, args.Length);
|
|
if (j < 0) return null;
|
|
return (PropertyInfo)match[j];
|
|
}
|
|
|
|
public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type rtype, Type[] types, ParameterModifier[] modifiers){
|
|
if (match == null) return null;
|
|
int matches = match.Length;
|
|
if (matches == 0) return null;
|
|
if (matches == 1) return match[0];
|
|
int candidates = 0;
|
|
PropertyInfo result = null;
|
|
int minval = Int32.MaxValue;
|
|
ParameterInfo[][] fparams = new ParameterInfo[matches][];
|
|
for (int i = 0; i < matches; i++){
|
|
result = match[i];
|
|
if (rtype != null){
|
|
int distance = TypeDistance(result.PropertyType, rtype);
|
|
if (distance > minval) //Do not even consider this property, there is another one with a better match to rtype
|
|
continue;
|
|
if (distance < minval) //This property is strictly a better than all previous one w.r.t. rtype. Remove all of those from consideration
|
|
for (int j = 0; j < i; j++)
|
|
if (fparams[j] != null){
|
|
fparams[j] = null;
|
|
candidates--;
|
|
}
|
|
}
|
|
fparams[i] = result.GetIndexParameters();
|
|
candidates++;
|
|
}
|
|
if (candidates <= 1) return result;
|
|
int k = JSBinder.SelectBest(match, matches, types, fparams, null, candidates, types.Length);
|
|
if (k < 0) return null;
|
|
return match[k];
|
|
}
|
|
|
|
internal static PropertyInfo SelectProperty(MemberInfo[] match, IReflect[] argIRs){
|
|
if (match == null) return null;
|
|
int n = match.Length;
|
|
if (n == 0) return null;
|
|
if (n == 1) return match[0] as PropertyInfo;
|
|
return (PropertyInfo)JSBinder.Select(match, n, argIRs, MemberTypes.Property);
|
|
}
|
|
|
|
//Returns a value that quantifies the cost/badness of converting a value of type actual to type formal.
|
|
//Used to select the overload that is the closest match to the actual parameter.
|
|
|
|
private static int TypeDistance(IReflect formal, IReflect actual){
|
|
if (formal is TypedArray){
|
|
if (actual is TypedArray){
|
|
TypedArray f = (TypedArray)formal;
|
|
TypedArray a = (TypedArray)actual;
|
|
if (f.rank == a.rank)
|
|
return TypeDistance(f.elementType, a.elementType) == 0 ? 0 : 100;
|
|
}else if (actual is Type){
|
|
TypedArray f = (TypedArray)formal;
|
|
Type a = (Type)actual;
|
|
if (a.IsArray && f.rank == a.GetArrayRank())
|
|
return TypeDistance(f.elementType, a.GetElementType()) == 0 ? 0 : 100;
|
|
else if (a == Typeob.Array || a == Typeob.ArrayObject)
|
|
return 30;
|
|
}
|
|
return 100;
|
|
}
|
|
if (actual is TypedArray){
|
|
if (formal is Type){
|
|
Type f = (Type)formal;
|
|
TypedArray a = (TypedArray)actual;
|
|
if (f.IsArray && f.GetArrayRank() == a.rank)
|
|
return TypeDistance(f.GetElementType(), a.elementType) == 0 ? 0 : 100;
|
|
else if (f == Typeob.Array)
|
|
return 30;
|
|
else if (f == Typeob.Object)
|
|
return 50;
|
|
}
|
|
return 100;
|
|
}
|
|
if (formal is ClassScope){
|
|
if (actual is ClassScope)
|
|
return ((ClassScope)actual).IsSameOrDerivedFrom((ClassScope)formal) ? 0 : 100;
|
|
else
|
|
return 100;
|
|
}
|
|
if (actual is ClassScope){
|
|
if (formal is Type)
|
|
return ((ClassScope)actual).IsPromotableTo((Type)formal) ? 0 : 100;
|
|
else
|
|
return 100;
|
|
}
|
|
return TypeDistance(Convert.ToType(formal), Convert.ToType(actual));
|
|
}
|
|
|
|
private static int TypeDistance(Type formal, Type actual){
|
|
TypeCode atc = Type.GetTypeCode(actual);
|
|
TypeCode ftc = Type.GetTypeCode(formal);
|
|
if (actual.IsEnum) atc = TypeCode.Object;
|
|
if (formal.IsEnum) ftc = TypeCode.Object;
|
|
switch(atc){
|
|
case TypeCode.DBNull:
|
|
switch(ftc){
|
|
default: return formal == Typeob.Object ? 0 : 1;
|
|
}
|
|
|
|
case TypeCode.Boolean:
|
|
switch(ftc){
|
|
case TypeCode.Boolean: return 0;
|
|
case TypeCode.Object: return formal == Typeob.Object ? 1 : 100;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Char:
|
|
switch(ftc){
|
|
case TypeCode.Char: return 0;
|
|
case TypeCode.UInt16: return 1;
|
|
case TypeCode.UInt32: return 2;
|
|
case TypeCode.Int32: return 3;
|
|
case TypeCode.UInt64: return 4;
|
|
case TypeCode.Int64: return 5;
|
|
case TypeCode.Single: return 6;
|
|
case TypeCode.Double: return 7;
|
|
case TypeCode.Decimal: return 8;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 9);
|
|
case TypeCode.String: return 10;
|
|
case TypeCode.Int16: return 11;
|
|
case TypeCode.Byte: return 12;
|
|
case TypeCode.SByte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.SByte:
|
|
switch(ftc){
|
|
case TypeCode.SByte: return 0;
|
|
case TypeCode.Int16: return 1;
|
|
case TypeCode.Int32: return 2;
|
|
case TypeCode.Int64: return 3;
|
|
case TypeCode.Single: return 4;
|
|
case TypeCode.Double: return 5;
|
|
case TypeCode.Decimal: return 6;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 7);
|
|
case TypeCode.String: return 8;
|
|
case TypeCode.Byte: return 9;
|
|
case TypeCode.UInt16: return 10;
|
|
case TypeCode.UInt32: return 12;
|
|
case TypeCode.UInt64: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Byte:
|
|
switch(ftc){
|
|
case TypeCode.Byte: return 0;
|
|
case TypeCode.UInt16: return 1;
|
|
case TypeCode.Int16: return 3;
|
|
case TypeCode.UInt32: return 4;
|
|
case TypeCode.Int32: return 5;
|
|
case TypeCode.UInt64: return 6;
|
|
case TypeCode.Int64: return 7;
|
|
case TypeCode.Single: return 8;
|
|
case TypeCode.Double: return 9;
|
|
case TypeCode.Decimal: return 10;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 11);
|
|
case TypeCode.String: return 12;
|
|
case TypeCode.SByte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Int16:
|
|
switch(ftc){
|
|
case TypeCode.Int16: return 0;
|
|
case TypeCode.Int32: return 1;
|
|
case TypeCode.Int64: return 2;
|
|
case TypeCode.Single: return 3;
|
|
case TypeCode.Double: return 4;
|
|
case TypeCode.Decimal: return 5;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 6);
|
|
case TypeCode.String: return 7;
|
|
case TypeCode.UInt16: return 8;
|
|
case TypeCode.UInt32: return 10;
|
|
case TypeCode.UInt64: return 11;
|
|
case TypeCode.SByte: return 12;
|
|
case TypeCode.Byte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.UInt16:
|
|
switch(ftc){
|
|
case TypeCode.UInt16: return 0;
|
|
case TypeCode.UInt32: return 1;
|
|
case TypeCode.UInt64: return 2;
|
|
case TypeCode.Int32: return 4;
|
|
case TypeCode.Int64: return 5;
|
|
case TypeCode.Single: return 6;
|
|
case TypeCode.Double: return 7;
|
|
case TypeCode.Decimal: return 8;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 9);
|
|
case TypeCode.String: return 10;
|
|
case TypeCode.Int16: return 11;
|
|
case TypeCode.Byte: return 12;
|
|
case TypeCode.SByte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Int32:
|
|
switch(ftc){
|
|
case TypeCode.Int32: return 0;
|
|
case TypeCode.Int64: return 1;
|
|
case TypeCode.Double: return 2;
|
|
case TypeCode.Decimal: return 3;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 4);
|
|
case TypeCode.String: return 5;
|
|
case TypeCode.UInt64: return 6;
|
|
case TypeCode.UInt32: return 7;
|
|
case TypeCode.Single: return 8;
|
|
case TypeCode.Int16: return 9;
|
|
case TypeCode.UInt16: return 10;
|
|
case TypeCode.SByte: return 12;
|
|
case TypeCode.Byte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.UInt32:
|
|
switch(ftc){
|
|
case TypeCode.UInt32: return 0;
|
|
case TypeCode.UInt64: return 1;
|
|
case TypeCode.Int64: return 2;
|
|
case TypeCode.Double: return 3;
|
|
case TypeCode.Decimal: return 4;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 5);
|
|
case TypeCode.String: return 6;
|
|
case TypeCode.Int32: return 7;
|
|
case TypeCode.Single: return 8;
|
|
case TypeCode.UInt16: return 9;
|
|
case TypeCode.Int16: return 11;
|
|
case TypeCode.Byte: return 12;
|
|
case TypeCode.SByte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Int64:
|
|
switch(ftc){
|
|
case TypeCode.Int64: return 0;
|
|
case TypeCode.Decimal: return 1;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 2);
|
|
case TypeCode.String: return 3;
|
|
case TypeCode.Double: return 4;
|
|
case TypeCode.Single: return 5;
|
|
case TypeCode.Int32: return 6;
|
|
case TypeCode.Int16: return 7;
|
|
case TypeCode.SByte: return 8;
|
|
case TypeCode.UInt64: return 9;
|
|
case TypeCode.UInt32: return 10;
|
|
case TypeCode.UInt16: return 11;
|
|
case TypeCode.Byte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.UInt64:
|
|
switch(ftc){
|
|
case TypeCode.UInt64: return 0;
|
|
case TypeCode.Decimal: return 1;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 2);
|
|
case TypeCode.String: return 3;
|
|
case TypeCode.Int64: return 4;
|
|
case TypeCode.Double: return 5;
|
|
case TypeCode.Single: return 6;
|
|
case TypeCode.UInt32: return 7;
|
|
case TypeCode.UInt16: return 8;
|
|
case TypeCode.Byte: return 10;
|
|
case TypeCode.Int32: return 11;
|
|
case TypeCode.Int16: return 12;
|
|
case TypeCode.SByte: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Single:
|
|
switch(ftc){
|
|
case TypeCode.Single: return 0;
|
|
case TypeCode.Double: return 1;
|
|
case TypeCode.Decimal: return 2;
|
|
case TypeCode.Int64: return 3;
|
|
case TypeCode.UInt64: return 4;
|
|
case TypeCode.Int32: return 5;
|
|
case TypeCode.UInt32: return 6;
|
|
case TypeCode.Int16: return 7;
|
|
case TypeCode.UInt16: return 8;
|
|
case TypeCode.SByte: return 10;
|
|
case TypeCode.Byte: return 11;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 12);
|
|
case TypeCode.String: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Double:
|
|
switch(ftc){
|
|
case TypeCode.Double: return 0;
|
|
case TypeCode.Decimal: return 1;
|
|
case TypeCode.Single: return 2;
|
|
case TypeCode.Int64: return 3;
|
|
case TypeCode.UInt64: return 4;
|
|
case TypeCode.Int32: return 5;
|
|
case TypeCode.UInt32: return 6;
|
|
case TypeCode.Int16: return 7;
|
|
case TypeCode.UInt16: return 8;
|
|
case TypeCode.SByte: return 10;
|
|
case TypeCode.Byte: return 11;
|
|
case TypeCode.Object: return TypeDistance(formal, actual, 12);
|
|
case TypeCode.String: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Decimal:
|
|
switch(ftc){
|
|
case TypeCode.Decimal: return 0;
|
|
case TypeCode.Double: return 1;
|
|
case TypeCode.Single: return 2;
|
|
case TypeCode.Int64: return 3;
|
|
case TypeCode.UInt64: return 4;
|
|
case TypeCode.Int32: return 5;
|
|
case TypeCode.UInt32: return 6;
|
|
case TypeCode.Int16: return 7;
|
|
case TypeCode.UInt16: return 8;
|
|
case TypeCode.SByte: return 10;
|
|
case TypeCode.Byte: return 11;
|
|
case TypeCode.Object: return formal == Typeob.Object ? 12 : 100;
|
|
case TypeCode.String: return 13;
|
|
case TypeCode.Boolean: return 14;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.DateTime:
|
|
switch(ftc){
|
|
case TypeCode.DateTime: return 0;
|
|
case TypeCode.Object: return formal == Typeob.Object ? 1 : 100;
|
|
case TypeCode.String: return 3;
|
|
case TypeCode.Double: return 4;
|
|
case TypeCode.Decimal: return 5;
|
|
case TypeCode.UInt64: return 6;
|
|
case TypeCode.Int64: return 7;
|
|
case TypeCode.UInt32: return 8;
|
|
case TypeCode.Int32: return 9;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.String:
|
|
switch(ftc){
|
|
case TypeCode.String: return 0;
|
|
case TypeCode.Object: return formal == Typeob.Object ? 1 : 100;
|
|
case TypeCode.Char: return 2;
|
|
default: return 100;
|
|
}
|
|
|
|
case TypeCode.Object:
|
|
if (formal == actual) return 0;
|
|
if (formal == Typeob.Missing) return 200;
|
|
if (!(formal.IsAssignableFrom(actual))){
|
|
if (Typeob.Array.IsAssignableFrom(formal) && (actual == Typeob.Array || Typeob.ArrayObject.IsAssignableFrom(actual)))
|
|
return 10;
|
|
else if (ftc == TypeCode.String)
|
|
return 20;
|
|
else if (actual == Typeob.ScriptFunction && typeof(Delegate).IsAssignableFrom(formal))
|
|
return 19;
|
|
else
|
|
return 100;
|
|
}
|
|
Type[] interfaces = actual.GetInterfaces();
|
|
int i;
|
|
int n = interfaces.Length;
|
|
for (i = 0; i < n; i++)
|
|
if (formal == interfaces[i]) return i+1;
|
|
for (i = 0; actual != Typeob.Object && actual != null; i++){
|
|
if (formal == actual) return i+n+1;
|
|
actual = actual.BaseType;
|
|
}
|
|
return i+n+1;
|
|
}
|
|
return 0; //should never get here
|
|
}
|
|
|
|
private static int TypeDistance(Type formal, Type actual, int distFromObject){
|
|
if (formal == Typeob.Object) return distFromObject;
|
|
if (formal.IsEnum) return TypeDistance(Enum.GetUnderlyingType(formal), actual)+10;
|
|
return 100;
|
|
}
|
|
}
|
|
}
|