Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clr methods resolving #154

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Jurassic/Compiler/Binders/BinderMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private Type ParamArrayElementType
/// Gets an array of method parameters.
/// </summary>
/// <returns> An array of ParameterInfo instances describing the method parameters. </returns>
protected virtual ParameterInfo[] GetParameters()
internal virtual ParameterInfo[] GetParameters()
{
return this.Method.GetParameters();
}
Expand Down
143 changes: 124 additions & 19 deletions Jurassic/Compiler/Binders/BinderUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BinderArgument> binderArguments = methods[i].GetArguments(arguments.Length);
foreach (var argument in binderArguments)
{
// Get the input parameter.
object input;
Expand All @@ -55,35 +56,48 @@ 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)
demeritPoints[i] += disqualification;
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<TypeCode, int> offsetDict = new Dictionary<TypeCode, int>() { { 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]++;
Expand Down Expand Up @@ -111,29 +125,60 @@ 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;


case TypeCode.Empty:
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<int>();
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.
Expand All @@ -151,6 +196,66 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn

return lowestIndices[0];
}


private static List<int> _LowestIndices(BinderMethod[] methods, int[] demeritPoints, out int lowestScore)
{
lowestScore = int.MaxValue;
List<int> lowestIndices = new List<int>();
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;
}
}

}
2 changes: 1 addition & 1 deletion Jurassic/Compiler/Binders/JSBinderMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public int MaxParameterCount
/// Gets an array of method parameters.
/// </summary>
/// <returns> An array of ParameterInfo instances describing the method parameters. </returns>
protected override ParameterInfo[] GetParameters()
internal override ParameterInfo[] GetParameters()
{
// Pull out the first and/or second parameters.
var result = base.GetParameters();
Expand Down
13 changes: 9 additions & 4 deletions Jurassic/Compiler/Binders/MethodBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ protected MethodBinder(BinderMethod targetMethod)
/// Creates a new Binder instance.
/// </summary>
/// <param name="targetMethods"> 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. </param>
/// method must be provided. Every method must have the same name. </param>
protected MethodBinder(IEnumerable<BinderMethod> 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)
Expand All @@ -58,8 +58,13 @@ protected MethodBinder(IEnumerable<BinderMethod> 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));
Expand Down
19 changes: 18 additions & 1 deletion Jurassic/Library/ClrWrapper/ClrStaticTypeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,24 @@ public override ObjectInstance ConstructLateBound(params object[] argumentValues
/// <c>BindingFlags.Instance</c> to populate instance methods. </param>
internal static void PopulateMembers(ObjectInstance target, Type type, BindingFlags flags)
{
List<string> ownMethods = new List<string>();
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<string, List<MethodBase>>();
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<MethodBase> methodGroup;
if (methodGroups.TryGetValue(method.Name, out methodGroup) == true)
Expand All @@ -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));
Expand All @@ -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));
Expand Down