Skip to content

Commit dd4e681

Browse files
martinbuMartin Burtscher
and
Martin Burtscher
authored
Add support for object return values in Func callbacks (#2053)
If a Func defines a none value-type return type that does not implement IConvertible a InvalidCastException was thrown. This update skips Convert.ChangeType if the returned value is not IConvertible and just casts the result. Co-authored-by: Martin Burtscher <martin.burtscher@teslab.com>
1 parent 2563811 commit dd4e681

File tree

2 files changed

+77
-3
lines changed

2 files changed

+77
-3
lines changed

Diff for: Jint.Tests/Runtime/InteropTests.cs

+65-1
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,70 @@ public void ExtraParametersAreIgnored()
263263
");
264264
}
265265

266+
class Example()
267+
{
268+
public T ExchangeGenericViaFunc<T>(Func<T> objViaFunc)
269+
{
270+
return objViaFunc();
271+
}
272+
273+
public object ExchangeObjectViaFunc(Func<object> objViaFunc)
274+
{
275+
return objViaFunc();
276+
}
277+
278+
public int ExchangeValueViaFunc(Func<int> objViaFunc)
279+
{
280+
return objViaFunc();
281+
}
282+
}
283+
284+
[Fact]
285+
public void ExchangeGenericViaFunc()
286+
{
287+
_engine.SetValue("Example", new Example());
288+
289+
RunTest(@"
290+
const result = Example.ExchangeGenericViaFunc(() => {
291+
return {
292+
value: 42
293+
};
294+
});
295+
296+
assert(result.value === 42);
297+
");
298+
}
299+
300+
[Fact]
301+
public void ExchangeObjectViaFunc()
302+
{
303+
_engine.SetValue("Example", new Example());
304+
305+
RunTest(@"
306+
const result = Example.ExchangeObjectViaFunc(() => {
307+
return {
308+
value: 42
309+
};
310+
});
311+
312+
assert(result.value === 42);
313+
");
314+
}
315+
316+
[Fact]
317+
public void ExchangeValueViaFunc()
318+
{
319+
_engine.SetValue("Example", new Example());
320+
321+
RunTest(@"
322+
const result = Example.ExchangeValueViaFunc(() => {
323+
return 42;
324+
});
325+
326+
assert(result === 42);
327+
");
328+
}
329+
266330
private delegate string callParams(params object[] values);
267331

268332
private delegate string callArgumentAndParams(string firstParam, params object[] values);
@@ -2925,7 +2989,7 @@ public void ArrayPrototypeFindWithInteropList()
29252989
{
29262990
var engine = new Jint.Engine();
29272991
var list = new List<string> { "A", "B", "C" };
2928-
2992+
29292993
engine.SetValue("list", list);
29302994

29312995
Assert.Equal(1, engine.Evaluate("list.findIndex((x) => x === 'B')"));

Diff for: Jint/Runtime/Interop/DefaultTypeConverter.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public class DefaultTypeConverter : ITypeConverter
3434
private static readonly Type engineType = typeof(Engine);
3535
private static readonly Type typeType = typeof(Type);
3636

37-
private static readonly MethodInfo convertChangeType = typeof(Convert).GetMethod("ChangeType", new[] { objectType, typeType, typeof(IFormatProvider) })!;
37+
private static readonly MethodInfo changeTypeIfConvertible = typeof(DefaultTypeConverter).GetMethod(
38+
"ChangeTypeOnlyIfConvertible", BindingFlags.NonPublic | BindingFlags.Static)!;
3839
private static readonly MethodInfo jsValueFromObject = jsValueType.GetMethod(nameof(JsValue.FromObject))!;
3940
private static readonly MethodInfo jsValueToObject = jsValueType.GetMethod(nameof(JsValue.ToObject))!;
4041

@@ -323,7 +324,7 @@ private Delegate BuildDelegate(
323324
Expression.Convert(
324325
Expression.Call(
325326
null,
326-
convertChangeType,
327+
changeTypeIfConvertible,
327328
Expression.Call(callExpression, jsValueToObject),
328329
Expression.Constant(method.ReturnType),
329330
Expression.Constant(System.Globalization.CultureInfo.InvariantCulture, typeof(IFormatProvider))
@@ -339,6 +340,15 @@ private Delegate BuildDelegate(
339340
new ReadOnlyCollection<ParameterExpression>(parameters)).Compile();
340341
}
341342

343+
[return: NotNullIfNotNull(nameof(value))]
344+
private static object? ChangeTypeOnlyIfConvertible(object? value, Type conversionType, IFormatProvider? provider)
345+
{
346+
if (value == null || value is IConvertible)
347+
return System.Convert.ChangeType(value, conversionType, provider);
348+
349+
return value;
350+
}
351+
342352
private static bool TryCastWithOperators(object value, Type type, Type valueType, [NotNullWhen(true)] out object? converted)
343353
{
344354
var key = new TypeConversionKey(valueType, type);

0 commit comments

Comments
 (0)