From df2cc5dd8f7bcdbb24c3be71b3d54bbcb3c118ac Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Mon, 28 Sep 2026 09:24:38 -0500 Subject: [PATCH] Admit direct arguments reads in Test262 scripts --- tests/harness/test262-runner.test.ts | 12 +++++ tests/test262/support.mjs | 15 ++++++- tests/test262/upstream.json | 4 ++ .../func-decl-args-trailing-comma-null.js | 43 ++++++++++++++++++ ...func-expr-args-trailing-comma-undefined.js | 44 +++++++++++++++++++ 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/test262/vendor/test/language/arguments-object/func-decl-args-trailing-comma-null.js create mode 100644 tests/test262/vendor/test/language/arguments-object/func-expr-args-trailing-comma-undefined.js diff --git a/tests/harness/test262-runner.test.ts b/tests/harness/test262-runner.test.ts index 1180cd535..be5a9a7da 100644 --- a/tests/harness/test262-runner.test.ts +++ b/tests/harness/test262-runner.test.ts @@ -73,6 +73,18 @@ test("receiver-bound this is admitted without adapting script-level this", () => } }); +test("arguments reads are admitted only for indexed and length access inside a function", () => { + for (const [body, expected] of [ + ["function f() { return arguments.length === 1 && arguments[0] === 3; } assert(f(3));", undefined], + ["function f() { return arguments; }", "host:arguments"], + ["function f() { return Array.isArray(arguments); }", "host:arguments"], + ["assert.sameValue(arguments.length, 0);", "host:arguments"], + ]) { + const text = source("description: arguments", body!); + expect(exclusion(text, metadata(text), "strict")).toBe(expected); + } +}); + test("negative parse cases require the compiler's matching source diagnostic", () => { const text = source("negative: {phase: parse, type: SyntaxError}", "$DONOTEVALUATE();\nconst = ;"); expect(exclusion(text, metadata(text), "strict")).toBeUndefined(); diff --git a/tests/test262/support.mjs b/tests/test262/support.mjs index 1056c134b..de1e4529f 100644 --- a/tests/test262/support.mjs +++ b/tests/test262/support.mjs @@ -95,6 +95,14 @@ function hasOwnThisBinding(node) { return false; } +function argumentsOwner(node) { + for (let parent = node.parent; parent; parent = parent.parent) { + if (ts.isArrowFunction(parent)) continue; + if (ts.isFunctionLike(parent) && parent.body) return parent; + } + return undefined; +} + // The first static profile adapts scripts to standalone strict modules. Be // conservative about observable global-script semantics and helper reflection. // Exclusions are runner limitations, never implementation support claims. @@ -111,7 +119,7 @@ export function exclusion(source, meta, variant) { if (unsupportedIncludes.length) return `harness-includes:${unsupportedIncludes.join(",")}`; const sf = ts.createSourceFile("test.js", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS); let reason; - const forbidden = new Set(["$262", "$DONE", "$DONOTEVALUATE", "globalThis", "eval", "Function", "print", "process", "require", "arguments"]); + const forbidden = new Set(["$262", "$DONE", "$DONOTEVALUATE", "globalThis", "eval", "Function", "print", "process", "require"]); if (variant === "sloppy") for (const name of ["module", "exports", "__dirname", "__filename"]) forbidden.add(name); if (meta.flags.includes("async")) forbidden.delete("$DONE"); const visit = (node) => { @@ -121,6 +129,11 @@ export function exclusion(source, meta, variant) { reason = "host:script-environment"; } else if (ts.isIdentifier(node) && forbidden.has(node.text)) { reason = `host:${node.text}`; + } else if (ts.isIdentifier(node) && node.text === "arguments" && + (!argumentsOwner(node) || + !(ts.isPropertyAccessExpression(node.parent) && node.parent.expression === node && node.parent.name.text === "length") && + !(ts.isElementAccessExpression(node.parent) && node.parent.expression === node))) { + reason = "host:arguments"; } else if (ts.isIdentifier(node) && node.text === "assert") { const parent = node.parent; if (ts.isCallExpression(parent) && parent.expression === node) { diff --git a/tests/test262/upstream.json b/tests/test262/upstream.json index f6e00dcdc..6122f9cde 100644 --- a/tests/test262/upstream.json +++ b/tests/test262/upstream.json @@ -168,6 +168,8 @@ "test/built-ins/String/prototype/trim/15.5.4.20-2-47.js", "test/built-ins/String/prototype/trim/15.5.4.20-2-50.js", "test/built-ins/String/prototype/trim/15.5.4.20-2-6.js", + "test/language/arguments-object/func-decl-args-trailing-comma-null.js", + "test/language/arguments-object/func-expr-args-trailing-comma-undefined.js", "test/language/expressions/addition/S11.6.1_A4_T1.js", "test/language/expressions/addition/S11.6.1_A4_T2.js", "test/language/expressions/addition/S11.6.1_A4_T3.js", @@ -372,6 +374,8 @@ "test/built-ins/String/prototype/trim/15.5.4.20-2-47.js": "3e1a745af465177a96a29601e56299afc044060dbc2ac3061a59032be451cfde", "test/built-ins/String/prototype/trim/15.5.4.20-2-50.js": "38b84997faa99e422408917eeac7f3575bd6064b429ea390a9d041fd77fce532", "test/built-ins/String/prototype/trim/15.5.4.20-2-6.js": "3d5857f24117f65f07a70edd0d254dfa07f593c76effed94500847ccf8564c06", + "test/language/arguments-object/func-decl-args-trailing-comma-null.js": "80aecea8edd0d7bac1ddcfcd6cb97dc9a513b25a9283bb9cedd21fd77dd64d50", + "test/language/arguments-object/func-expr-args-trailing-comma-undefined.js": "cf39515a9031db4c81db231a157516bdc97617c392e4ef28890b2ae74777a99e", "test/language/expressions/addition/S11.6.1_A4_T1.js": "12928eb319dcf0cc785e399580effb0b7495709296dce9c0a58e8408616a959f", "test/language/expressions/addition/S11.6.1_A4_T2.js": "aee914241b607f56a98de04492f2d580e7bc69cca232af679d0c02b7025cc06b", "test/language/expressions/addition/S11.6.1_A4_T3.js": "987b1c3cf66b6bbb2f6d8aaf6d1435cb1238b19132fa861a91aa2c35085d229d", diff --git a/tests/test262/vendor/test/language/arguments-object/func-decl-args-trailing-comma-null.js b/tests/test262/vendor/test/language/arguments-object/func-decl-args-trailing-comma-null.js new file mode 100644 index 000000000..561cf5061 --- /dev/null +++ b/tests/test262/vendor/test/language/arguments-object/func-decl-args-trailing-comma-null.js @@ -0,0 +1,43 @@ +// This file was procedurally generated from the following sources: +// - src/arguments/args-trailing-comma-null.case +// - src/arguments/default/func-decl.template +/*--- +description: A trailing comma after null should not increase the arguments.length (function declaration) +esid: sec-arguments-exotic-objects +flags: [generated] +info: | + 9.4.4 Arguments Exotic Objects + + Most ECMAScript functions make an arguments object available to their code. Depending upon the + characteristics of the function definition, its arguments object is either an ordinary object + or an arguments exotic object. + + Trailing comma in the arguments list + + Left-Hand-Side Expressions + + Arguments : + ( ) + ( ArgumentList ) + ( ArgumentList , ) + + ArgumentList : + AssignmentExpression + ... AssignmentExpression + ArgumentList , AssignmentExpression + ArgumentList , ... AssignmentExpression +---*/ + + +var callCount = 0; +// Stores a reference `ref` for case evaluation +function ref() { + assert.sameValue(arguments.length, 2); + assert.sameValue(arguments[0], 42); + assert.sameValue(arguments[1], null); + callCount = callCount + 1; +} + +ref(42, null,); + +assert.sameValue(callCount, 1, 'function invoked exactly once'); diff --git a/tests/test262/vendor/test/language/arguments-object/func-expr-args-trailing-comma-undefined.js b/tests/test262/vendor/test/language/arguments-object/func-expr-args-trailing-comma-undefined.js new file mode 100644 index 000000000..7bf644a2e --- /dev/null +++ b/tests/test262/vendor/test/language/arguments-object/func-expr-args-trailing-comma-undefined.js @@ -0,0 +1,44 @@ +// This file was procedurally generated from the following sources: +// - src/arguments/args-trailing-comma-undefined.case +// - src/arguments/default/func-expr.template +/*--- +description: A trailing comma after undefined should not increase the arguments.length (function expression) +esid: sec-arguments-exotic-objects +flags: [generated] +info: | + 9.4.4 Arguments Exotic Objects + + Most ECMAScript functions make an arguments object available to their code. Depending upon the + characteristics of the function definition, its arguments object is either an ordinary object + or an arguments exotic object. + + Trailing comma in the arguments list + + Left-Hand-Side Expressions + + Arguments : + ( ) + ( ArgumentList ) + ( ArgumentList , ) + + ArgumentList : + AssignmentExpression + ... AssignmentExpression + ArgumentList , AssignmentExpression + ArgumentList , ... AssignmentExpression +---*/ + + +var callCount = 0; +// Stores a reference `ref` for case evaluation +var ref; +ref = function() { + assert.sameValue(arguments.length, 2); + assert.sameValue(arguments[0], 42); + assert.sameValue(arguments[1], undefined); + callCount = callCount + 1; +}; + +ref(42, undefined,); + +assert.sameValue(callCount, 1, 'function invoked exactly once');