Skip to content

Commit d971f8a

Browse files
committed
Fixed a error, that occurred during finding the suitable method overload, that receives numeric values and interfaces as parameters, of the host object
1 parent 5cdcfa5 commit d971f8a

File tree

8 files changed

+181
-52
lines changed

8 files changed

+181
-52
lines changed

src/MsieJavaScriptEngine/Helpers/ReflectionHelpers.cs

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#if NETSTANDARD1_3
22
using System;
3+
using System.Collections.Generic;
34
using System.Linq;
45
using System.Reflection;
56

@@ -85,9 +86,8 @@ public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] para
8586

8687
public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValues)
8788
{
88-
int argCount = argValues.Length;
89-
var methodCandidates = methods
90-
.Select(m => new
89+
MethodWithMetadata[] methodCandidates = methods
90+
.Select(m => new MethodWithMetadata
9191
{
9292
Method = m,
9393
ParameterTypes = m.GetParameters()
@@ -96,12 +96,14 @@ public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValu
9696
})
9797
.ToArray()
9898
;
99-
100-
var methodsWithSameArity = methodCandidates
99+
int argCount = argValues.Length;
100+
MethodWithMetadata[] sameArityMethods = methodCandidates
101101
.Where(m => m.ParameterTypes.Length == argCount)
102102
.ToArray()
103103
;
104-
if (methodsWithSameArity.Length == 0)
104+
105+
int sameArityMethodCount = sameArityMethods.Length;
106+
if (sameArityMethodCount == 0)
105107
{
106108
return null;
107109
}
@@ -110,40 +112,46 @@ public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValu
110112
.Select(a => a.GetType())
111113
.ToArray()
112114
;
113-
var weaklyCompatibleMethods = methodsWithSameArity
114-
.Where(m => CompareParameterTypes(argValues, argTypes, m.ParameterTypes, false))
115-
.ToArray()
116-
;
115+
var compatibleMethods = new List<MethodWithMetadata>();
117116

118-
int weaklyCompatibleMethodCount = weaklyCompatibleMethods.Length;
119-
if (weaklyCompatibleMethodCount > 0)
117+
for (int methodIndex = 0; methodIndex < sameArityMethodCount; methodIndex++)
120118
{
121-
if (weaklyCompatibleMethodCount == 1)
119+
MethodWithMetadata method = sameArityMethods[methodIndex];
120+
ushort compatibilityScore;
121+
122+
if (CompareParameterTypes(argValues, argTypes, method.ParameterTypes, out compatibilityScore))
122123
{
123-
return weaklyCompatibleMethods[0].Method;
124+
method.CompatibilityScore = compatibilityScore;
125+
compatibleMethods.Add(method);
124126
}
127+
}
125128

126-
var strictlyCompatibleMethods = weaklyCompatibleMethods
127-
.Where(m => CompareParameterTypes(argValues, argTypes, m.ParameterTypes, true))
128-
.ToArray()
129-
;
130-
if (strictlyCompatibleMethods.Length > 0)
129+
int compatibleMethodCount = compatibleMethods.Count;
130+
if (compatibleMethodCount > 0)
131+
{
132+
if (compatibleMethodCount == 1)
131133
{
132-
return strictlyCompatibleMethods[0].Method;
134+
return compatibleMethods[0].Method;
133135
}
134136

135-
return weaklyCompatibleMethods[0].Method;
137+
MethodWithMetadata bestFitMethod = compatibleMethods
138+
.OrderByDescending(m => m.CompatibilityScore)
139+
.First()
140+
;
141+
142+
return bestFitMethod.Method;
136143
}
137144

138145
return null;
139146
}
140147

141148
private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, Type[] parameterTypes,
142-
bool strictСompliance)
149+
out ushort compatibilityScore)
143150
{
144151
int argValueCount = argValues.Length;
145152
int argTypeCount = argTypes.Length;
146153
int parameterCount = parameterTypes.Length;
154+
compatibilityScore = 0;
147155

148156
if (argValueCount != argTypeCount || argTypeCount != parameterCount)
149157
{
@@ -156,32 +164,50 @@ private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, T
156164
Type argType = argTypes[argIndex];
157165
Type parameterType = parameterTypes[argIndex];
158166

159-
if (argType != parameterType)
167+
if (argType == parameterType)
160168
{
161-
if (!strictСompliance
162-
&& NumericHelpers.IsNumericType(argType) && NumericHelpers.IsNumericType(parameterType))
163-
{
164-
object convertedArgValue;
165-
166-
if (!TypeConverter.TryConvertToType(argValue, parameterType, out convertedArgValue))
167-
{
168-
return false;
169-
}
170-
171-
if (argValue != convertedArgValue)
172-
{
173-
return false;
174-
}
169+
compatibilityScore++;
170+
}
171+
else
172+
{
173+
// TODO: It is necessary to calculate the compatibility score based on length
174+
// of inheritance and interface implementation chains.
175+
object convertedArgValue;
175176

176-
continue;
177+
if (!TypeConverter.TryConvertToType(argValue, parameterType, out convertedArgValue))
178+
{
179+
return false;
177180
}
178181

179-
return false;
182+
continue;
180183
}
181184
}
182185

183186
return true;
184187
}
188+
189+
190+
private sealed class MethodWithMetadata
191+
{
192+
public MethodBase Method
193+
{
194+
get;
195+
set;
196+
}
197+
198+
public Type[] ParameterTypes
199+
{
200+
get;
201+
set;
202+
}
203+
204+
/// TODO: In future will need to change type to <code>double</code>
205+
public ushort CompatibilityScore
206+
{
207+
get;
208+
set;
209+
}
210+
}
185211
}
186212
}
187213
#endif

test/MsieJavaScriptEngine.Test.Common.Net4/MsieJavaScriptEngine.Test.Common.Net40.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@
6161
<Compile Include="..\MsieJavaScriptEngine.Test.Common\InteropTestsBase.cs">
6262
<Link>InteropTestsBase.cs</Link>
6363
</Compile>
64+
<Compile Include="..\MsieJavaScriptEngine.Test.Common\Interop\Animals\AnimalTrainer.cs">
65+
<Link>Interop\Animals\AnimalTrainer.cs</Link>
66+
</Compile>
67+
<Compile Include="..\MsieJavaScriptEngine.Test.Common\Interop\Animals\Cat.cs">
68+
<Link>Interop\Animals\Cat.cs</Link>
69+
</Compile>
70+
<Compile Include="..\MsieJavaScriptEngine.Test.Common\Interop\Animals\Dog.cs">
71+
<Link>Interop\Animals\Dog.cs</Link>
72+
</Compile>
73+
<Compile Include="..\MsieJavaScriptEngine.Test.Common\Interop\Animals\IAnimal.cs">
74+
<Link>Interop\Animals\IAnimal.cs</Link>
75+
</Compile>
6476
<Compile Include="..\MsieJavaScriptEngine.Test.Common\Interop\Base64Encoder.cs">
6577
<Link>Interop\Base64Encoder.cs</Link>
6678
</Compile>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace MsieJavaScriptEngine.Test.Common.Interop.Animals
2+
{
3+
public sealed class AnimalTrainer
4+
{
5+
public string ExecuteVoiceCommand(IAnimal animal)
6+
{
7+
return animal.Cry();
8+
}
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace MsieJavaScriptEngine.Test.Common.Interop.Animals
2+
{
3+
public sealed class Cat : IAnimal
4+
{
5+
public string Cry()
6+
{
7+
return "Meow!";
8+
}
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace MsieJavaScriptEngine.Test.Common.Interop.Animals
2+
{
3+
public sealed class Dog : IAnimal
4+
{
5+
public string Cry()
6+
{
7+
return "Woof!";
8+
}
9+
}
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace MsieJavaScriptEngine.Test.Common.Interop.Animals
2+
{
3+
public interface IAnimal
4+
{
5+
string Cry();
6+
}
7+
}

test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static Date Today
1616
get
1717
{
1818
DateTime currentDateTime = DateTime.Today;
19-
Date currentDate = new Date(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day);
19+
var currentDate = new Date(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day);
2020

2121
return currentDate;
2222
}
@@ -43,5 +43,13 @@ public int GetDayOfYear()
4343
(Month > 2 && IsLeapYear(Year) ? 1 : 0)
4444
;
4545
}
46+
47+
public Date AddDays(double value)
48+
{
49+
var dateTime = new DateTime(Year, Month, Day);
50+
DateTime newDateTime = dateTime.AddDays(value);
51+
52+
return new Date(newDateTime.Year, newDateTime.Month, newDateTime.Day);
53+
}
4654
}
4755
}

test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using NUnit.Framework;
1010

1111
using MsieJavaScriptEngine.Test.Common.Interop;
12+
using MsieJavaScriptEngine.Test.Common.Interop.Animals;
1213
#if NETCOREAPP1_0
1314
using MsieJavaScriptEngine.Test.Common.Interop.Drawing;
1415
#endif
@@ -369,25 +370,32 @@ public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethodIsCorrect
369370
}
370371

371372
[Test]
372-
public virtual void EmbeddingOfInstanceOfCustomValueTypeWithMethodIsCorrect()
373+
public virtual void EmbeddingOfInstanceOfCustomValueTypeWithMethodsIsCorrect()
373374
{
374375
// Arrange
375376
var programmerDayDate = new Date(2015, 9, 13);
376377

377-
const string input = "programmerDay.GetDayOfYear()";
378-
const int targetOutput = 256;
378+
const string input1 = "programmerDay.GetDayOfYear()";
379+
const int targetOutput1 = 256;
380+
381+
const string input2 = @"var smileDay = programmerDay.AddDays(6);
382+
smileDay.GetDayOfYear();";
383+
const int targetOutput2 = 262;
379384

380385
// Act
381-
int output;
386+
int output1;
387+
int output2;
382388

383389
using (var jsEngine = CreateJsEngine())
384390
{
385391
jsEngine.EmbedHostObject("programmerDay", programmerDayDate);
386-
output = jsEngine.Evaluate<int>(input);
392+
output1 = jsEngine.Evaluate<int>(input1);
393+
output2 = jsEngine.Evaluate<int>(input2);
387394
}
388395

389396
// Assert
390-
Assert.AreEqual(targetOutput, output);
397+
Assert.AreEqual(targetOutput1, output1);
398+
Assert.AreEqual(targetOutput2, output2);
391399
}
392400

393401
[Test]
@@ -413,6 +421,38 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethodIsCorrect(
413421
Assert.AreEqual(targetOutput, output);
414422
}
415423

424+
[Test]
425+
public virtual void CallingOfMethodOfCustomReferenceTypeWithInterfaceParameterIsCorrect()
426+
{
427+
// Arrange
428+
var animalTrainer = new AnimalTrainer();
429+
var cat = new Cat();
430+
var dog = new Dog();
431+
432+
const string input1 = "animalTrainer.ExecuteVoiceCommand(cat)";
433+
const string targetOutput1 = "Meow!";
434+
435+
const string input2 = "animalTrainer.ExecuteVoiceCommand(dog)";
436+
const string targetOutput2 = "Woof!";
437+
438+
// Act
439+
string output1;
440+
string output2;
441+
442+
using (var jsEngine = CreateJsEngine())
443+
{
444+
jsEngine.EmbedHostObject("animalTrainer", animalTrainer);
445+
jsEngine.EmbedHostObject("cat", cat);
446+
jsEngine.EmbedHostObject("dog", dog);
447+
output1 = jsEngine.Evaluate<string>(input1);
448+
output2 = jsEngine.Evaluate<string>(input2);
449+
}
450+
451+
// Assert
452+
Assert.AreEqual(targetOutput1, output1);
453+
Assert.AreEqual(targetOutput2, output2);
454+
}
455+
416456
#endregion
417457

418458
#region Delegates
@@ -998,25 +1038,31 @@ public virtual void EmbeddingOfBuiltinValueTypeWithMethodIsCorrect()
9981038
}
9991039

10001040
[Test]
1001-
public virtual void EmbeddingOfBuiltinReferenceTypeWithMethodIsCorrect()
1041+
public virtual void EmbeddingOfBuiltinReferenceTypeWithMethodsIsCorrect()
10021042
{
10031043
// Arrange
10041044
Type mathType = typeof(Math);
10051045

1006-
const string input = "Math2.Max(5.37, 5.56)";
1007-
const double targetOutput = 5.56;
1046+
const string input1 = "Math2.Max(5.37, 5.56)";
1047+
const double targetOutput1 = 5.56;
1048+
1049+
const string input2 = "Math2.Log10(23)";
1050+
const double targetOutput2 = 1.36172783601759;
10081051

10091052
// Act
1010-
double output;
1053+
double output1;
1054+
double output2;
10111055

10121056
using (var jsEngine = CreateJsEngine())
10131057
{
10141058
jsEngine.EmbedHostType("Math2", mathType);
1015-
output = jsEngine.Evaluate<double>(input);
1059+
output1 = jsEngine.Evaluate<double>(input1);
1060+
output2 = Math.Round(jsEngine.Evaluate<double>(input2), 14);
10161061
}
10171062

10181063
// Assert
1019-
Assert.AreEqual(targetOutput, output);
1064+
Assert.AreEqual(targetOutput1, output1);
1065+
Assert.AreEqual(targetOutput2, output2);
10201066
}
10211067

10221068
[Test]

0 commit comments

Comments
 (0)