From 144591843cfa8c02aaf5a33bd75b4c0868bbb54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Fern=C3=A1ndez=20Corral?= Date: Sun, 2 Apr 2023 12:04:57 +0200 Subject: [PATCH] Support params arrays in delegate invocation (#1520) Co-authored-by: Marko Lahma --- Jint.Tests/Runtime/DelegateWrapperTests.cs | 33 +++++++++++++++++++ Jint/Runtime/Interop/DefaultTypeConverter.cs | 21 ++++++++++-- .../Interpreter/JintFunctionDefinition.cs | 4 +-- 3 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 Jint.Tests/Runtime/DelegateWrapperTests.cs diff --git a/Jint.Tests/Runtime/DelegateWrapperTests.cs b/Jint.Tests/Runtime/DelegateWrapperTests.cs new file mode 100644 index 0000000000..fa091e3a3e --- /dev/null +++ b/Jint.Tests/Runtime/DelegateWrapperTests.cs @@ -0,0 +1,33 @@ +using Jint.Runtime.Interop; + +namespace Jint.Tests.Runtime; + +public class DelegateWrapperTests +{ + [Fact] + public void ShouldSpreadParameters() + { + var engine = new Engine(); + engine.SetValue("registerCallback", new DelegateWrapper(engine, new RegisterCallbackDelegate(RegisterCallback))); + + engine.Execute(@" + var argsConcat = ''; + registerCallback((valTest, valOther, valNumber) => { + argsConcat+=typeof valTest; + argsConcat+=' ' + typeof valOther; + argsConcat+=' ' + typeof valNumber; + }, 'test', 'other', 1337); + "); + + Assert.True(engine.Evaluate("argsConcat == 'string string number'").AsBoolean()); + } + + private static void RegisterCallback(CallbackAction callback, params object[] arguments) + { + callback.Invoke(arguments); + } + + private delegate void RegisterCallbackDelegate(CallbackAction callback, params object[] arguments); + + private delegate void CallbackAction(params object[] arguments); +} diff --git a/Jint/Runtime/Interop/DefaultTypeConverter.cs b/Jint/Runtime/Interop/DefaultTypeConverter.cs index f4f412e814..0623874bc3 100644 --- a/Jint/Runtime/Interop/DefaultTypeConverter.cs +++ b/Jint/Runtime/Interop/DefaultTypeConverter.cs @@ -256,18 +256,33 @@ private Delegate BuildDelegate(Type type, Func func parameters[i] = Expression.Parameter(arguments[i].ParameterType, arguments[i].Name); } - var initializers = new MethodCallExpression[parameters.Length]; + var initializers = new List(parameters.Length); + for (var i = 0; i < parameters.Length; i++) { var param = parameters[i]; if (param.Type.IsValueType) { var boxing = Expression.Convert(param, objectType); - initializers[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), boxing); + initializers.Add(Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), boxing)); + } + else if (param.Type.IsArray && + arguments[i].GetCustomAttribute() is not null && + function.Target is FunctionInstance instance) + { + for (var j = 0; j < instance.Length; j++) + { + var returnLabel = Expression.Label(typeof(object)); + var checkIndex = Expression.GreaterThanOrEqual(Expression.Property(param, nameof(Array.Length)), Expression.Constant(j)); + var condition = Expression.IfThen(checkIndex, Expression.Return(returnLabel, Expression.ArrayAccess(param, Expression.Constant(j)))); + var block = Expression.Block(condition, Expression.Label(returnLabel, Expression.Constant(JsValue.Undefined))); + + initializers.Add(Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), block)); + } } else { - initializers[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), param); + initializers.Add(Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), param)); } } diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index 7f0d8e66df..f68388eae6 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -75,8 +75,8 @@ internal Completion EvaluateBody(EvaluationContext context, FunctionInstance fun _bodyStatementList ??= new JintStatementList(Function); AsyncFunctionStart(context, promiseCapability, context => { - context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); - return _bodyStatementList.Execute(context); + context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + return _bodyStatementList.Execute(context); }); result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); }