Skip to content

Commit 6a05885

Browse files
committed
Make eval("arguments") work inside of lambdas.
1 parent a30187d commit 6a05885

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2488,6 +2488,16 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat
24882488
{
24892489
top->byteCodeFunction->SetEnclosedByGlobalFunc();
24902490
}
2491+
2492+
// In the case of lambdas defined inside of eval in param scope, argumentsSymbol can be nullptr
2493+
// We still get correct behavior if it is, but we need to check if that's the case.
2494+
if (top->GetCallsEval() && !enclosingNonLambda->IsGlobalFunction()
2495+
&& enclosingNonLambda->GetArgumentsSymbol() != nullptr)
2496+
{
2497+
enclosingNonLambda->SetHasArguments(true);
2498+
enclosingNonLambda->SetHasHeapArguments(true);
2499+
enclosingNonLambda->GetArgumentsSymbol()->SetHasNonLocalReference();
2500+
}
24912501
}
24922502

24932503
// If this is a named function expression and has deferred child, mark has non-local reference.
@@ -3168,6 +3178,16 @@ void VisitNestedScopes(ParseNode* pnodeScopeList, ParseNode* pnodeParent, ByteCo
31683178
// Push the param scope
31693179
byteCodeGenerator->PushScope(paramScope);
31703180

3181+
/*
3182+
We need this call to happen *before* visiting the param scope. If we have a split scope,
3183+
and this function has parameters that default to a lambda which uses the implicit "arguments" inside
3184+
of eval, we need this function's argumentsSymbol to be set already, so that we can mark it as
3185+
non-locally referenced inside of PostVisitFunction.
3186+
3187+
See Github issue #1209 for more.
3188+
*/
3189+
AddVarsToScope(pnodeScope->sxFnc.pnodeVars, byteCodeGenerator);
3190+
31713191
if (pnodeScope->sxFnc.HasNonSimpleParameterList() && !paramScope->GetCanMergeWithBodyScope())
31723192
{
31733193
// Set param scope as the current child scope.
@@ -3182,7 +3202,6 @@ void VisitNestedScopes(ParseNode* pnodeScopeList, ParseNode* pnodeParent, ByteCo
31823202
funcInfo->SetCurrentChildScope(bodyScope);
31833203

31843204
PreVisitBlock(pnodeScope->sxFnc.pnodeBodyScope, byteCodeGenerator);
3185-
AddVarsToScope(pnodeScope->sxFnc.pnodeVars, byteCodeGenerator);
31863205

31873206
if (!pnodeScope->sxFnc.HasNonSimpleParameterList() || paramScope->GetCanMergeWithBodyScope())
31883207
{

test/es6/default-splitscope.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,23 @@ var tests = [
913913
}
914914
}
915915
assert.areEqual([2, 3], f16(1, undefined, 2, 3), "Rest should remain unaffected when arguments is updated");
916+
917+
function f18(a, b = () => eval("arguments[0]")) {
918+
return b();
919+
}
920+
assert.areEqual(1, f18(1), "eval('arguments') inside split scope should work correctly");
921+
assert.areEqual([1, 2], f18([1, 2]), "eval('arguments') inside split scope should work correctly");
922+
923+
function f19(a, b = eval('() => () => eval("arguments[0]"')) {
924+
return b()();
925+
}
926+
assert.areEqual(1, f18(1), "nested eval('arguments') inside split scope should work correctly");
927+
assert.areEqual([1, 2], f18([1, 2]), "nested eval('arguments') inside split scope should work correctly");
928+
929+
function f20(a, c = () => eval("arguments.length")) {
930+
return c();
931+
}
932+
assert.areEqual(1, f20(1), "arguments.length is set correctly with eval in split scope lambda");
916933
}
917934
},
918935
{

test/es6/lambda1.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,52 @@ var tests = [
269269
assert.areEqual('foo', k('foo')(), "lambda captures formal parameters named arguments");
270270
}
271271
},
272+
{
273+
name: "eval('arguments') works correctly within lambdas",
274+
body: function () {
275+
var arguments = 'not arguments object';
276+
assert.areEqual(arguments, (() => eval("arguments"))(), "arguments in lambda should bind to outside arguments since it is not given its own arguments binding");
277+
278+
function h () {
279+
return () => {
280+
assert.areEqual(5, eval("arguments.length"), "captured arguments length is respected");
281+
assert.areEqual(h, eval("arguments.callee"), "arguments caller is respected");
282+
assert.areEqual(1, eval("arguments[0]"), "first argument is 1");
283+
assert.areEqual(2, eval("arguments[1]"), "second argument is 2");
284+
assert.areEqual(3, eval("arguments[2]"), "third argument is 3");
285+
assert.areEqual('abc', eval("arguments[3]"), "fourth argument is 'abc'");
286+
assert.areEqual(null, eval("arguments[4]"), "fifth argument is null");
287+
};
288+
}
289+
h(1, 2, 3, 'abc', null)();
290+
291+
function i () {
292+
return () => () => { return () => eval("arguments"); };
293+
}
294+
var args = i('a', 'b', 'c', 123, undefined)()()();
295+
296+
assert.areEqual(5, args.length, "captured arguments (through multiple lambdas) length is respected");
297+
assert.areEqual(i, args.callee, "arguments (through multiple lambdas) caller is respected");
298+
assert.areEqual('a', args[0], "first argument (through multiple lambdas) is 'a'");
299+
assert.areEqual('b', args[1], "second argument (through multiple lambdas) is 'b'");
300+
assert.areEqual('c', args[2], "third argument (through multiple lambdas) is 'c'");
301+
assert.areEqual(123, args[3], "fourth argument (through multiple lambdas) is 123");
302+
assert.areEqual(undefined, args[4], "fifth argument (through multiple lambdas) is undefined");
303+
304+
function j () {
305+
var arguments = 'not an arguments object';
306+
return () => eval("arguments");
307+
}
308+
309+
assert.areEqual('not an arguments object', j()(), "lambda captures local variables named arguments");
310+
311+
function k (arguments) {
312+
return () => eval("arguments");
313+
}
314+
315+
assert.areEqual('foo', k('foo')(), "lambda captures formal parameters named arguments");
316+
}
317+
},
272318
{
273319
name: "Capturing dynamically scoped variables",
274320
body: function () {

0 commit comments

Comments
 (0)