Skip to content

Commit

Permalink
Support params arrays in delegate invocation (#1520)
Browse files Browse the repository at this point in the history
Co-authored-by: Marko Lahma <[email protected]>
  • Loading branch information
mainlyer and lahma authored Apr 2, 2023
1 parent 9984761 commit 1445918
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
33 changes: 33 additions & 0 deletions Jint.Tests/Runtime/DelegateWrapperTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
21 changes: 18 additions & 3 deletions Jint/Runtime/Interop/DefaultTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,18 +256,33 @@ private Delegate BuildDelegate(Type type, Func<JsValue, JsValue[], JsValue> func
parameters[i] = Expression.Parameter(arguments[i].ParameterType, arguments[i].Name);
}

var initializers = new MethodCallExpression[parameters.Length];
var initializers = new List<MethodCallExpression>(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<ParamArrayAttribute>() 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));
}
}

Expand Down
4 changes: 2 additions & 2 deletions Jint/Runtime/Interpreter/JintFunctionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 1445918

Please sign in to comment.