From 14d742a32cc7e9c9a9cb364dfddfb9c77274d749 Mon Sep 17 00:00:00 2001 From: objmalloc Date: Tue, 5 Jun 2018 17:12:11 +0300 Subject: [PATCH 1/5] Group methods with same name from the whole inheritance tree --- Jurassic/Compiler/Binders/MethodBinder.cs | 13 +++++++++---- .../ClrWrapper/ClrStaticTypeWrapper.cs | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) 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)); From 8e68d4a341d1845813c3c61130f22053c90d823b Mon Sep 17 00:00:00 2001 From: objmalloc Date: Wed, 7 Nov 2018 16:23:04 +0200 Subject: [PATCH 2/5] Prefer ambiguous method by smallest distance to base class where is defined --- Jurassic/Compiler/Binders/BinderUtilities.cs | 74 +++++++++++++++++--- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/Jurassic/Compiler/Binders/BinderUtilities.cs b/Jurassic/Compiler/Binders/BinderUtilities.cs index f2eac035..d5ea9a2b 100644 --- a/Jurassic/Compiler/Binders/BinderUtilities.cs +++ b/Jurassic/Compiler/Binders/BinderUtilities.cs @@ -123,17 +123,22 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn } // 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); } // Throw an error if the match is ambiguous. @@ -151,6 +156,59 @@ 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; + } } } From 2805229d0483604e0217c163ce3fc06da8ca76a1 Mon Sep 17 00:00:00 2001 From: objmalloc Date: Thu, 8 Nov 2018 18:42:54 +0200 Subject: [PATCH 3/5] Resolve ambiguous methods with object and inherited from object parameters --- Jurassic/Compiler/Binders/BinderUtilities.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Jurassic/Compiler/Binders/BinderUtilities.cs b/Jurassic/Compiler/Binders/BinderUtilities.cs index d5ea9a2b..413d28c4 100644 --- a/Jurassic/Compiler/Binders/BinderUtilities.cs +++ b/Jurassic/Compiler/Binders/BinderUtilities.cs @@ -111,6 +111,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; From f005e48785ad2ea8f3b3bb6bdf15d3c16eb0cbcd Mon Sep 17 00:00:00 2001 From: objmalloc Date: Wed, 28 Nov 2018 12:05:34 +0200 Subject: [PATCH 4/5] Resolve ambiguous methods by arguments count. --- Jurassic/Compiler/Binders/BinderMethod.cs | 2 +- Jurassic/Compiler/Binders/BinderUtilities.cs | 25 +++++++++++++++++++- Jurassic/Compiler/Binders/JSBinderMethod.cs | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) 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 413d28c4..57a0b5aa 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; @@ -147,6 +148,21 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn 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. if (lowestIndices.Count > 1) { @@ -215,6 +231,13 @@ private static int _CalcMethodDistance(Type thisType, Type declaringType) 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(); From c77fbd9942057f403a92e5649aceb4e304a63c1a Mon Sep 17 00:00:00 2001 From: objmalloc Date: Tue, 22 Jan 2019 11:35:39 +0200 Subject: [PATCH 5/5] Resolve methods with numeric parameters --- Jurassic/Compiler/Binders/BinderUtilities.cs | 38 ++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Jurassic/Compiler/Binders/BinderUtilities.cs b/Jurassic/Compiler/Binders/BinderUtilities.cs index 57a0b5aa..59697abe 100644 --- a/Jurassic/Compiler/Binders/BinderUtilities.cs +++ b/Jurassic/Compiler/Binders/BinderUtilities.cs @@ -56,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) @@ -65,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]++; @@ -125,6 +138,11 @@ 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; + } } }