Source code
Revision control
Copy as Markdown
Other Tools
// (1) arguments.length as the head of an optional chain.
// (1a) ?. property access.
function optDot() { return arguments.length?.foo; }
assertEq(optDot(), undefined);
assertEq(optDot(1, 2, 3), undefined);
// (1b) ?.[] element access.
function optElem() { return arguments.length?.["toString"]; }
assertEq(typeof optElem(1, 2), "function");
// (1c) ?.() call. arguments.length is a number, so calling it throws
// TypeError (not ReferenceError) and never reads |arguments| itself.
function optCall() {
try {
arguments.length?.();
return "no throw";
} catch (e) {
return e.constructor.name;
}
}
assertEq(optCall(), "TypeError");
// (1d) The optimization must still produce the correct count, not read an
// enclosing |arguments|. inner() takes zero arguments regardless of outer's.
function outer() {
function inner() { return arguments.length?.toString(); }
return inner();
}
assertEq(outer(1, 2, 3, 4, 5), "0");
// (1e) Longer optional chains still produce the right count.
function optChain() { return arguments.length?.toString?.(); }
assertEq(optChain(1, 2, 3), "3");
function optChainElemCall() { return arguments.length?.["toString"]?.(); }
assertEq(optChainElemCall(1, 2), "2");
function optChainParens() { return (arguments.length)?.toString(); }
assertEq(optChainParens(1, 2, 3, 4), "4");
function outerChain() {
function inner() { return arguments.length?.toString?.(); }
return inner();
}
assertEq(outerChain(1, 2, 3, 4, 5), "0");
// (2) Non-optional call of arguments.length. Same elided-binding hazard:
// the callee must be the number, yielding TypeError rather than ReferenceError.
function nonOptCall() {
try {
arguments.length();
return "no throw";
} catch (e) {
return e.constructor.name;
}
}
assertEq(nonOptCall(), "TypeError");
// (3) arguments.length as a destructuring-assignment target. The binding must
// not be elided here, since the write needs a real |arguments| object.
// (3a) Array pattern.
function destrArray(a, b, c) {
[arguments.length] = [99];
return arguments.length;
}
assertEq(destrArray(1, 2, 3), 99);
// (3b) Object pattern.
function destrObject(a, b) {
({ p: arguments.length } = { p: 42 });
return arguments.length;
}
assertEq(destrObject(1, 2), 42);
// (3c) for-of with a destructuring pattern target.
function destrForOf(a) {
for ([arguments.length] of [[7]]) {
}
return arguments.length;
}
assertEq(destrForOf(1), 7);
// (3d) Nested / mixed pattern.
function destrNested(a, b, c, d) {
[, arguments.length] = [1, 5];
return arguments.length;
}
assertEq(destrNested(1, 2, 3, 4), 5);
// (4) Plain reads still take the fast path and return the correct value.
function plainRead(a, b, c) { return arguments.length; }
assertEq(plainRead(1, 2, 3), 3);