diff --git a/Jurassic/Compiler/Binders/BinderMethod.cs b/Jurassic/Compiler/Binders/BinderMethod.cs index 8ce0e8f3..c6f52479 100644 --- a/Jurassic/Compiler/Binders/BinderMethod.cs +++ b/Jurassic/Compiler/Binders/BinderMethod.cs @@ -187,7 +187,7 @@ private Type ParamArrayElementType /// Gets an array of method parameters. /// /// An array of ParameterInfo instances describing the method parameters. - protected virtual ParameterInfo[] GetParameters() + internal virtual ParameterInfo[] GetParameters() { return this.Method.GetParameters(); } diff --git a/Jurassic/Compiler/Binders/BinderUtilities.cs b/Jurassic/Compiler/Binders/BinderUtilities.cs index f2eac035..59697abe 100644 --- a/Jurassic/Compiler/Binders/BinderUtilities.cs +++ b/Jurassic/Compiler/Binders/BinderUtilities.cs @@ -33,7 +33,8 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn const int disqualification = 65536; for (int i = 0; i < methods.Length; i++) { - foreach (var argument in methods[i].GetArguments(arguments.Length)) + IEnumerable binderArguments = methods[i].GetArguments(arguments.Length); + foreach (var argument in binderArguments) { // Get the input parameter. object input; @@ -55,8 +56,10 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn // Get the type of the output parameter. Type outputType = argument.Type; + TypeCode typeCode = Type.GetTypeCode(outputType); - switch (Type.GetTypeCode(outputType)) + + switch (typeCode) { case TypeCode.Boolean: if ((input is bool) == false) @@ -64,26 +67,37 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn break; case TypeCode.SByte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: case TypeCode.Single: case TypeCode.Decimal: + case TypeCode.Double: + Dictionary offsetDict = new Dictionary() { { TypeCode.SByte, 10 }, + { TypeCode.Byte, 9 }, + { TypeCode.UInt16, 8 }, + { TypeCode.UInt32, 7 }, + { TypeCode.UInt64, 6 }, + { TypeCode.Int16, 5 }, + { TypeCode.Int32, 4 }, + { TypeCode.Int64, 3 }, + { TypeCode.Single, 2 }, + { TypeCode.Decimal, 1 }, + { TypeCode.Double, 0 } + }; + + // To fix ambiguous methods error when there are method with numeric parameters + // double has maximal priority if (TypeUtilities.IsNumeric(input) == true) - demeritPoints[i] ++; + demeritPoints[i] += offsetDict[typeCode]; else demeritPoints[i] += disqualification; break; - case TypeCode.Double: - if (TypeUtilities.IsNumeric(input) == false) - demeritPoints[i] += disqualification; - break; - case TypeCode.Char: if (TypeUtilities.IsString(input) == true) demeritPoints[i]++; @@ -111,6 +125,12 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn { demeritPoints[i] += disqualification; } + else if (outputType != input.GetType()) + { + // To fix ambiguous when the parameter is of type object and there is another method + // with parameter which inherits object. + demeritPoints[i]++; + } break; @@ -118,22 +138,47 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn case TypeCode.DBNull: throw new NotSupportedException(string.Format("{0} is not a supported parameter type.", outputType)); } + // To fix ambiguous methods error when there are method with smilar parameters, for example int32[] and int32 + if (argument.IsParamArrayArgument) + { + demeritPoints[i] += 100; + } } } // Find the method(s) with the fewest number of demerit points. - int lowestScore = int.MaxValue; - var lowestIndices = new List(); - for (int i = 0; i < methods.Length; i++) + int lowestScore; + var lowestIndices = _LowestIndices(methods, demeritPoints, out lowestScore); + + // Try to get the method from the most close base type + if (lowestIndices.Count > 1) { - if (demeritPoints[i] < lowestScore) + for (int i = 0; i < demeritPoints.Length; i++) { - lowestScore = demeritPoints[i]; - lowestIndices.Clear(); + demeritPoints[i] = disqualification; } - if (demeritPoints[i] <= lowestScore) - lowestIndices.Add(i); + for (int i = 0; i < lowestIndices.Count; i++) + { + int index = lowestIndices[i]; + demeritPoints[index] = _CalcMethodDistance(_GetThisType(thisValue), methods[index].DeclaringType); + } + lowestIndices = _LowestIndices(methods, demeritPoints, out lowestScore); + } + + // Try to get the method with most close arguments count + if (lowestIndices.Count > 1) + { + for (int i = 0; i < demeritPoints.Length; i++) + { + demeritPoints[i] = disqualification; + } + for (int i = 0; i < lowestIndices.Count; i++) + { + int index = lowestIndices[i]; + demeritPoints[index] = _CalcArgumentsPoint(methods[index], arguments.Length); + } + lowestIndices = _LowestIndices(methods, demeritPoints, out lowestScore); } // Throw an error if the match is ambiguous. @@ -151,6 +196,66 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn return lowestIndices[0]; } + + + private static List _LowestIndices(BinderMethod[] methods, int[] demeritPoints, out int lowestScore) + { + lowestScore = int.MaxValue; + List lowestIndices = new List(); + for (int i = 0; i < methods.Length; i++) + { + if (demeritPoints[i] < lowestScore) + { + lowestScore = demeritPoints[i]; + lowestIndices.Clear(); + } + if (demeritPoints[i] <= lowestScore) + lowestIndices.Add(i); + } + + return lowestIndices; + } + + + private static Type _GetThisType(object thisValue) + { + object thisUnwrapped = thisValue; + if (thisUnwrapped is Jurassic.Library.ClrInstanceWrapper) + { + thisUnwrapped = ((Jurassic.Library.ClrInstanceWrapper)thisUnwrapped).WrappedInstance; + } + else if (thisUnwrapped is Jurassic.Library.ClrInstanceTypeWrapper) + { + thisUnwrapped = ((Jurassic.Library.ClrInstanceTypeWrapper)thisUnwrapped).WrappedType; + } + else if (thisUnwrapped is Jurassic.Library.ClrStaticTypeWrapper) + { + thisUnwrapped = ((Jurassic.Library.ClrStaticTypeWrapper)thisUnwrapped).WrappedType; + } + return thisUnwrapped.GetType(); + } + + + private static int _CalcMethodDistance(Type thisType, Type declaringType) + { + Type currentType = thisType; + + int result = 0; + while (currentType != null && currentType != declaringType) + { + result++; + currentType = currentType.BaseType; + } + + return result; + } + + + private static int _CalcArgumentsPoint(BinderMethod method, int argumentsCount) + { + int points = Math.Max(method.GetParameters().Length - argumentsCount, 0); + return points; + } } } diff --git a/Jurassic/Compiler/Binders/JSBinderMethod.cs b/Jurassic/Compiler/Binders/JSBinderMethod.cs index c196dc3e..bbfd2045 100644 --- a/Jurassic/Compiler/Binders/JSBinderMethod.cs +++ b/Jurassic/Compiler/Binders/JSBinderMethod.cs @@ -163,7 +163,7 @@ public int MaxParameterCount /// Gets an array of method parameters. /// /// An array of ParameterInfo instances describing the method parameters. - protected override ParameterInfo[] GetParameters() + internal override ParameterInfo[] GetParameters() { // Pull out the first and/or second parameters. var result = base.GetParameters(); diff --git a/Jurassic/Compiler/Binders/MethodBinder.cs b/Jurassic/Compiler/Binders/MethodBinder.cs index 5221a26e..378662ed 100644 --- a/Jurassic/Compiler/Binders/MethodBinder.cs +++ b/Jurassic/Compiler/Binders/MethodBinder.cs @@ -39,14 +39,14 @@ protected MethodBinder(BinderMethod targetMethod) /// Creates a new Binder instance. /// /// An enumerable list of methods to bind to. At least one - /// method must be provided. Every method must have the same name and declaring type. + /// method must be provided. Every method must have the same name. protected MethodBinder(IEnumerable targetMethods) { if (targetMethods == null) throw new ArgumentNullException(nameof(targetMethods)); // At least one method must be provided. - // Every method must have the same name and declaring type. + // Every method must have the same name foreach (var method in targetMethods) { if (this.Name == null) @@ -58,8 +58,13 @@ protected MethodBinder(IEnumerable targetMethods) { if (this.Name != method.Name) throw new ArgumentException(nameof(targetMethods)); - if (this.declaringType != method.DeclaringType) - throw new ArgumentException(nameof(targetMethods)); + + // This code is removed, because now methods with same name from + // the whole inheritance hierarchy are grouped together. + // Otherwise method from the base class is not accessible when there + // is a method with the same name in inherited class. + //if (this.declaringType != method.DeclaringType) + // throw new ArgumentException(nameof(targetMethods)); } this.functionLength = Math.Max(this.FunctionLength, method.RequiredParameterCount + method.OptionalParameterCount + (method.HasParamArray ? 1 : 0)); diff --git a/Jurassic/Library/ClrWrapper/ClrStaticTypeWrapper.cs b/Jurassic/Library/ClrWrapper/ClrStaticTypeWrapper.cs index 01e19dcf..bc6fd944 100644 --- a/Jurassic/Library/ClrWrapper/ClrStaticTypeWrapper.cs +++ b/Jurassic/Library/ClrWrapper/ClrStaticTypeWrapper.cs @@ -197,13 +197,24 @@ public override ObjectInstance ConstructLateBound(params object[] argumentValues /// BindingFlags.Instance to populate instance methods. internal static void PopulateMembers(ObjectInstance target, Type type, BindingFlags flags) { + List ownMethods = new List(); + foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.DeclaredOnly | flags)) + { + if (member.MemberType == MemberTypes.Method) + ownMethods.Add(member.Name); + } + // Register static methods as functions. var methodGroups = new Dictionary>(); - foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.DeclaredOnly | flags)) + foreach (var member in type.GetMembers(BindingFlags.Public | flags)) { switch (member.MemberType) { case MemberTypes.Method: + // Use base class method only when overriden + if (member.DeclaringType != type && !ownMethods.Contains(member.Name)) + continue; + MethodInfo method = (MethodInfo)member; List methodGroup; if (methodGroups.TryGetValue(method.Name, out methodGroup) == true) @@ -213,6 +224,9 @@ internal static void PopulateMembers(ObjectInstance target, Type type, BindingFl break; case MemberTypes.Property: + if (member.DeclaringType != type) // Skip base class properties + continue; + PropertyInfo property = (PropertyInfo)member; var getMethod = property.GetGetMethod(); ClrFunction getter = getMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(getMethod)); @@ -229,6 +243,9 @@ internal static void PopulateMembers(ObjectInstance target, Type type, BindingFl break; case MemberTypes.Field: + if (member.DeclaringType != type) // Skip base class fields + continue; + FieldInfo field = (FieldInfo)member; ClrFunction fieldGetter = new ClrFunction(target.Engine.Function.InstancePrototype, new FieldGetterBinder(field)); ClrFunction fieldSetter = new ClrFunction(target.Engine.Function.InstancePrototype, new FieldSetterBinder(field));