Expand static Test262 execution for sloppy scripts and parse negatives

This commit is contained in:
Chris Tate
2026-09-27 11:17:21 -05:00
parent 3d04a561ef
commit b6f6ea2afb
12 changed files with 287 additions and 32 deletions
+21 -5
View File
@@ -33,7 +33,6 @@ test("metadata generates the upstream variants without rewriting execution goals
test("unsupported execution requirements and assertion reflection remain exclusions", () => {
for (const [head, body] of [
["negative: {phase: parse, type: SyntaxError}", "assert.sameValue(1, 1);"],
["negative: {phase: runtime, type: TypeError}", "throw new TypeError();"],
["includes: [propertyHelper.js]", "verifyProperty({}, 'x', {});"],
["description: global script", "assert.sameValue(this, globalThis);"],
@@ -46,7 +45,11 @@ test("unsupported execution requirements and assertion reflection remain exclusi
}
const text = source("description: scalar", "// globalThis and this in comments are harmless\nassert.sameValue('this', 'this');");
expect(exclusion(text, metadata(text), "strict")).toBeUndefined();
expect(exclusion(text, metadata(text), "sloppy")).toBe("execution:sloppy");
expect(exclusion(text, metadata(text), "sloppy")).toBeUndefined();
const scriptGlobal = source("description: script global", "assert.sameValue((function () { return this; })(), globalThis);");
expect(exclusion(scriptGlobal, metadata(scriptGlobal), "sloppy")).toBe("host:script-environment");
const commonJsGlobal = source("description: CommonJS global", "assert.sameValue(module.exports, {});");
expect(exclusion(commonJsGlobal, metadata(commonJsGlobal), "sloppy")).toBe("host:module");
const asyncText = source("flags: [async]", "Promise.resolve().then(() => $DONE());");
expect(exclusion(asyncText, metadata(asyncText), "strict")).toBeUndefined();
});
@@ -60,15 +63,25 @@ test("negative parse cases require the compiler's matching source diagnostic", (
code: "SC0001", message: "Variable declaration expected.", loc: { file: "/tmp/main.js", start: offset },
}] };
expect(matchesParseNegative(match, text)).toBe(true);
expect(matchesParseNegative(match, text, "sloppy")).toBe(true);
expect(matchesParseNegative(match, text, "sloppy")).toBe(false);
const sloppyOffset = prepare(text, false, undefined, "sloppy").indexOf("const = ;") + "const ".length;
expect(matchesParseNegative({ status: "compile-refusal", diagnostics: [{
code: "SC0001", message: "Variable declaration expected.", loc: { file: "/tmp/main.cjs", start: sloppyOffset },
}] }, text, "sloppy")).toBe(true);
expect(matchesParseNegative({ status: "compile-refusal", diagnostics: [{
code: "SC0001", message: "Cannot find name '$DONOTEVALUATE'.", loc: { file: "/tmp/main.js", start: 0 },
}] }, text)).toBe(false);
expect(matchesParseNegative({ status: "compiler-error", diagnostics: match.diagnostics }, text)).toBe(false);
const strictOnly = source("negative: {phase: parse, type: SyntaxError}", "with ({}) {}");
expect(exclusion(strictOnly, metadata(strictOnly), "sloppy")).toBe("negative-phase:parse");
expect(exclusion(strictOnly, metadata(strictOnly), "sloppy")).toBeUndefined();
const generatedOnly = source("negative: {phase: parse, type: SyntaxError}", "function f() {");
expect(exclusion(generatedOnly, metadata(generatedOnly), "strict")).toBe("negative-phase:parse");
expect(exclusion(generatedOnly, metadata(generatedOnly), "strict")).toBeUndefined();
const typeOnly = source("negative: {phase: parse, type: SyntaxError}", "const value = 'a' * 2;");
const typeOffset = prepare(typeOnly).indexOf("'a' * 2");
expect(matchesParseNegative({ status: "compile-refusal", diagnostics: [{
code: "SC0001", message: "An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.",
loc: { file: "/tmp/main.js", start: typeOffset },
}] }, typeOnly)).toBe(false);
});
test("built-in error assertions are admitted without permitting constructor aliases", () => {
@@ -91,6 +104,9 @@ test("the adapter retains the test body without a function or try/catch wrapper"
expect(result.startsWith('"use strict";\n')).toBe(true);
expect(result).toContain(`\n${body}\n`);
expect(result).not.toContain("try {");
const sloppy = prepare(body, false, "done", "sloppy");
expect(sloppy).toContain(`\n${body}\n`);
expect(sloppy).not.toContain('"use strict";');
});
test("reports keep exclusions, refusals, failures and passes separate", () => {
+32 -11
View File
@@ -10,12 +10,23 @@ const sanitize = process.env.SCRIPTC_SAN === "1";
const upstreamHarness = ["assert.js", "sta.js"]
.map((name) => readFileSync(join(vendorRoot, "harness", name), "utf8")).join("\n");
function runUpstream(source: string): void {
function runUpstream(source: string, variant: "strict" | "sloppy" = "strict"): void {
const context = createContext({});
runInContext(upstreamHarness, context, { timeout: 5000 });
runInContext(`"use strict";\n${source}`, context, { timeout: 5000 });
runInContext(variant === "strict" ? `"use strict";\n${source}` : source, context, { timeout: 5000 });
}
test("sloppy script source executes through a CommonJS entry", async () => {
const source = readFileSync(join(vendorRoot, "test/language/expressions/addition/S11.6.1_A4_T1.js"), "utf8");
const meta = metadata(source);
expect(exclusion(source, meta, "sloppy")).toBeUndefined();
const context = createContext({});
runInContext(upstreamHarness, context, { timeout: 5000 });
runInContext(source, context, { timeout: 5000 });
const result = await runSource(source, { sanitize, variant: "sloppy" });
expect(result, JSON.stringify(result, null, 2)).toMatchObject({ status: "pass" });
});
test("Test262 regression inputs retain their pinned upstream bytes", () => {
verifyVendor();
});
@@ -27,20 +38,30 @@ test("negative parse cases require a matching compiler syntax diagnostic", async
expect(matchesParseNegative(result, source), JSON.stringify(result, null, 2)).toBe(true);
});
const profileCases = shardSelect<string>(pin.tests, (path) => `${path}#strict`);
// The initial profile is small enough that a valid shard can own no cases.
// Vitest rejects an empty describe block, so register this suite only when
// this shard actually owns a program. The host checks below still run.
if (profileCases.length > 0) describe(`Test262 static strict profile${shardSuffix()}`, () => {
const profileCases = shardSelect<string>(pin.tests, (path) => {
const source = readFileSync(join(vendorRoot, path), "utf8");
const variant = metadata(source, path).flags.includes("noStrict") ? "sloppy" : "strict";
return `${path}#${variant}`;
});
// Vitest rejects an empty describe block when a valid shard owns no cases.
if (profileCases.length > 0) describe(`Test262 static script profile${shardSuffix()}`, () => {
for (const path of profileCases) {
test(path, async () => {
const source = readFileSync(join(vendorRoot, path), "utf8");
expect(exclusion(source, metadata(source, path), "strict")).toBeUndefined();
const meta = metadata(source, path);
const variant = meta.flags.includes("noStrict") ? "sloppy" : "strict";
expect(exclusion(source, meta, variant)).toBeUndefined();
// Independently check the unchanged test with the original global-script
// harness. Node is a host sanity check, not the conformance oracle.
runUpstream(source);
const result = await runSource(source, { sanitize });
expect(matchesExpectation(`${path}#strict`, result), JSON.stringify(result, null, 2)).toBe(true);
if (meta.negative?.phase === "parse") {
let error: unknown;
try { runUpstream(source, variant); } catch (caught) { error = caught; }
expect(error).toMatchObject({ name: "SyntaxError" });
} else runUpstream(source, variant);
const result = await runSource(source, { sanitize, variant });
if (meta.negative?.phase === "parse") {
expect(matchesParseNegative(result, source, variant), JSON.stringify(result, null, 2)).toBe(true);
} else expect(matchesExpectation(`${path}#${variant}`, result), JSON.stringify(result, null, 2)).toBe(true);
});
}
});
+4 -4
View File
@@ -8,7 +8,7 @@ Run the offline regression profile from the repository root:
pnpm test:test262
```
The vendored inputs are regression fixtures for developing the runner and compiler. Strict variants are checked against `expectations.json`; entries without an expectation require successful completion. Sloppy variants remain visible as exclusions. A recorded refusal remains a refusal, and a different outcome fails the regression gate. An external snapshot survey treats every executed non-pass as a failing exit status. An all-excluded selection also fails, except for empty distributed shards.
The vendored inputs are regression fixtures for developing the runner and compiler. Variants are checked against `expectations.json`; entries without an expectation require successful completion. A recorded refusal remains a refusal, and a different outcome fails the regression gate. An external snapshot survey treats every executed non-pass as a failing exit status. An all-excluded selection also fails, except for empty distributed shards.
The same regression tests and host assertion checks are included in `pnpm test`. `pnpm test:sandbox` partitions the cases across both plain and sanitized lanes. The default backend may fall back from LLVM to C according to the ordinary compiler policy; every executed result records the actual backend. `--backend llvm` or `--backend c` pins a backend, and `SCRIPTC_SAN=1` enables sanitizers.
@@ -28,12 +28,12 @@ Pass the resulting directory as `--root` and use `--filter` to survey an API fam
## Execution profile and limits
The current profile is `static-strict-scalar-adapter-v1`. It selects synchronous, positive strict-script variants and adapts each script to a standalone module. Preparation adds a strict directive, imports the host assertions, appends a completion marker, and leaves the upstream test body unchanged. Each program executes in a fresh native process. Conservative syntax checks reject known global-script dependencies, but do not establish that every module adaptation preserves the original script's semantics. Reports are local investigation artifacts, not published compatibility assessments.
The current profile is `static-adapted-v4`. It adapts strict scripts to standalone modules and sloppy scripts to CommonJS entries. Preparation supplies host assertions and a completion marker while leaving the upstream test body unchanged. Each program executes in a fresh native process. Conservative syntax checks reject known global-script dependencies, but cannot establish that every adaptation preserves the original script's semantics. Reports are local investigation artifacts, not published compatibility assessments.
The unmodified upstream assertion helper currently reaches scriptc's JavaScript function-expando refusal. Test262 explicitly permits host implementations of harness functions in its [interpretation rules](https://github.com/tc39/test262/blob/7ab7fafa0003f73fc85c1b95d88094d33f7eb8bd/INTERPRETING.md#host-defined-functions). `harness.ts` supplies `assert`, `assert.sameValue`, `assert.notSameValue`, `assert.compareArray`, and `Test262Error`. Scalar SameValue and scalar array-element comparisons use the statically compiled `node:assert/strict` implementation. Contract tests compare successful and failing assertions with the original Test262 helpers under Node, including NaN, signed zero, null/undefined, differing primitive types, and premature termination.
The unmodified upstream assertion helper currently reaches scriptc's JavaScript function-expando refusal. Test262 explicitly permits host implementations of harness functions in its [interpretation rules](https://github.com/tc39/test262/blob/7ab7fafa0003f73fc85c1b95d88094d33f7eb8bd/INTERPRETING.md#host-defined-functions). `harness.ts` supplies `assert`, `assert.sameValue`, `assert.notSameValue`, `assert.compareArray`, `assert.throws` for selected Error constructors, `$DONE`, and `Test262Error`. Scalar SameValue and scalar array-element comparisons use the statically compiled `node:assert/strict` implementation. Contract tests compare successful and failing assertions with the original Test262 helpers under Node, including NaN, signed zero, null/undefined, differing primitive types, and premature termination.
Reference-equality assertions terminate with a distinct harness-refusal result: conversion at a native function boundary can copy references, so this adapter cannot faithfully compare every object, array, or function identity. Tests cannot catch this refusal and accidentally pass. Sparse array comparisons are not yet handled faithfully across that boundary. Assertion aliases, mutation, reflection, additional assertion methods, and `includes` helpers other than `compareArray.js` remain excluded.
Sloppy variants, raw tests, modules, asynchronous completion, agents, and negative tests are currently excluded. Parse, resolution, and runtime negative phases retain their distinct reasons; arbitrary compiler errors never satisfy a negative expectation. `$262`, dynamic evaluation, observable script-global state, and other unsupported host requirements are excluded as well. The runtime and compiler continue to enforce their existing refusal boundaries for admitted source.
Raw tests, module variants, agents, resolution negatives, and runtime negatives remain excluded. Parse negatives pass only when a source-matched syntax or vetted JavaScript early-error diagnostic appears; TypeScript type errors and harness-only failures never satisfy a negative expectation. `$262`, dynamic evaluation, observable script-global state, and other unsupported host requirements are excluded as well. The runtime and compiler continue to enforce their existing refusal boundaries for admitted source.
To expand the regression profile, review the original test's execution requirements, copy its source and license unchanged from the pinned snapshot, add its path and SHA-256 to `upstream.json`, and execute it under the upstream Node harness and static scriptc. Preserve meaningful failures with narrowly documented expectations. Extend the host adapter only with controls that demonstrate both successful and deliberately failing assertions; then run the focused harness and the full sandbox gate.
+3 -2
View File
@@ -64,6 +64,7 @@ export function boundedRun(command: string, args: string[], timeoutMs: number):
export async function runSource(source: string, options: {
backend?: "default" | "llvm" | "c";
asyncTest?: boolean;
variant?: "strict" | "sloppy";
sanitize?: boolean;
compileTimeoutMs?: number;
runtimeTimeoutMs?: number;
@@ -72,14 +73,14 @@ export async function runSource(source: string, options: {
// Keep sources OUTSIDE node_modules: scriptc intentionally treats imports
// under that directory as package code, with different compilation rules.
const workDir = mkdtempSync(join(tmpdir(), "scriptc-test262-"));
const entry = join(workDir, "main.js");
const entry = join(workDir, options.variant === "sloppy" ? "main.cjs" : "main.js");
const binary = join(workDir, process.platform === "win32" ? "program.exe" : "program");
const resultFile = join(workDir, "compile.json");
const requestFile = join(workDir, "request.json");
let phase = "compile";
try {
const marker = `${completion}:${randomBytes(16).toString("hex")}`;
writeFileSync(entry, prepare(source, options.asyncTest ?? false, marker));
writeFileSync(entry, prepare(source, options.asyncTest ?? false, marker, options.variant));
writeFileSync(join(workDir, "harness.ts"), harnessSource.replace(completion, marker));
writeFileSync(join(workDir, "assert-throws.js"), assertThrowsSource);
writeFileSync(requestFile, JSON.stringify({
+3 -2
View File
@@ -29,7 +29,7 @@ async function main(): Promise<void> {
--list list variants and exclusions without compiling
--keep retain generated sources and native artifacts
SCRIPTC_SAN=1 enables sanitizers; SCRIPTC_TEST_SHARD=i/n partitions variants.
Adapted strict scripts and source-matched strict or sloppy syntax negatives can pass.
Adapted strict and sloppy scripts and source-matched syntax negatives can pass.
Other variants remain visible as exclusions.`);
return;
}
@@ -87,6 +87,7 @@ Other variants remain visible as exclusions.`);
? { status: "excluded", reason: item.exclusion }
: await runSource(item.source, {
backend, sanitize: process.env.SCRIPTC_SAN === "1", compileTimeoutMs, runtimeTimeoutMs, keep: values.keep, asyncTest: item.asyncTest,
variant: item.variant === "sloppy" ? "sloppy" : "strict",
});
const outcome = item.negative?.phase === "parse" && matchesParseNegative(raw, item.source, item.variant)
? { status: "pass", phase: "compile" }
@@ -103,7 +104,7 @@ Other variants remain visible as exclusions.`);
const report = {
schema: "scriptc.test262.v1",
revision: pin.commit, snapshotSha256: pin.snapshotSha256,
profile: "static-adapted-v2",
profile: "static-adapted-v4",
harnessSha256: sha256(harnessSource + assertThrowsSource),
dynamic: false, requestedBackend: backend, sanitize: process.env.SCRIPTC_SAN === "1",
host: `${process.platform}-${process.arch}`, node: process.version,
+47 -8
View File
@@ -89,21 +89,26 @@ export function variants(meta) {
// Exclusions are runner limitations, never implementation support claims.
export function exclusion(source, meta, variant) {
if (meta.negative) {
if (meta.negative.phase !== "parse" || meta.negative.type !== "SyntaxError" || !parseDiagnostics(source, variant).length) {
if (meta.negative.phase !== "parse" || meta.negative.type !== "SyntaxError" || (variant !== "strict" && variant !== "sloppy")) {
return `negative-phase:${meta.negative.phase}`;
}
return undefined;
}
if (variant !== "strict") return `execution:${variant}`;
if (variant !== "strict" && variant !== "sloppy") return `execution:${variant}`;
if (meta.flags.some((flag) => flag.startsWith("CanBlock"))) return "host:agents";
const unsupportedIncludes = meta.includes.filter((name) => name !== "compareArray.js");
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"]);
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) => {
if (reason) return;
if (variant === "sloppy" && node.kind === ts.SyntaxKind.ThisKeyword) {
reason = "host:script-environment";
return;
}
if (node.kind === ts.SyntaxKind.ThisKeyword || node.kind === ts.SyntaxKind.ImportKeyword || ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
reason = "host:script-environment";
} else if (ts.isIdentifier(node) && forbidden.has(node.text)) {
@@ -134,18 +139,19 @@ export function exclusion(source, meta, variant) {
return reason;
}
export function prepare(source, asyncTest = false, marker = completion) {
export function prepare(source, asyncTest = false, marker = completion, variant = "strict") {
const names = asyncTest ? "assert, Test262Error, $DONE" : "assert, Test262Error";
const end = asyncTest ? "" : `;console.log(${JSON.stringify(marker)});\n`;
if (variant === "sloppy") return `const { ${names} } = require("./harness.ts");\n${source}\n${end}`;
return `"use strict";\nimport { ${names} } from "./harness.ts";\n${source}\n${end}`;
}
function parseDiagnostics(source, variant) {
if (variant !== "strict" && variant !== "sloppy") return [];
const prepared = prepare(source);
const prepared = prepare(source, false, completion, variant);
const sourceStart = prepared.indexOf(source);
const sourceEnd = sourceStart + source.length;
const file = ts.createSourceFile("main.js", prepared, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
const file = ts.createSourceFile(variant === "sloppy" ? "main.cjs" : "main.js", prepared, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
const raw = variant === "sloppy"
? ts.createSourceFile("test.js", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS).parseDiagnostics
: [];
@@ -157,13 +163,46 @@ function parseDiagnostics(source, variant) {
ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"))));
}
// TypeScript reports these JavaScript grammar and early errors in its semantic
// pass. Matching the compiler's diagnostic at the original source location
// keeps type-checking errors and the harness's $DONOTEVALUATE error out.
const earlyErrorCodes = new Set([
1005, 1013, 1048, 1091, 1136, 1156, 1186, 1214, 1215, 1346, 1347, 1359,
1500, 1508, 1522, 1524, 2300, 2364, 2451, 2462, 2491, 2779, 17012, 18061,
]);
function earlyErrorDiagnostics(source, variant) {
const prepared = prepare(source, false, completion, variant);
const start = prepared.indexOf(source);
const end = start + source.length;
const suffix = variant === "sloppy" ? "main.cjs" : "main.js";
const fileName = join(directory, "__test262_parse__", suffix);
const isMain = (path) => path.replaceAll("\\", "/").endsWith(`/__test262_parse__/${suffix}`);
const options = {
allowJs: true, checkJs: true, noEmit: true, noLib: true, types: [],
strict: true, target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext,
};
const host = ts.createCompilerHost(options);
host.fileExists = isMain;
host.readFile = (path) => isMain(path) ? prepared : undefined;
host.getSourceFile = (path, languageVersion) =>
isMain(path) ? ts.createSourceFile(path, prepared, languageVersion, true) : undefined;
const program = ts.createProgram([fileName], options, host);
const file = program.getSourceFile(fileName);
if (!file) return [];
return program.getSemanticDiagnostics(file).filter((diagnostic) =>
earlyErrorCodes.has(diagnostic.code) && diagnostic.start >= start && diagnostic.start < end &&
!(variant === "sloppy" && (diagnostic.code === 1214 || diagnostic.code === 1215)));
}
export function matchesParseNegative(outcome, source, variant = "strict") {
if (outcome.status !== "compile-refusal") return false;
return parseDiagnostics(source, variant).some((parsed) => outcome.diagnostics?.some((reported) =>
const matches = (parsed) => outcome.diagnostics?.some((reported) =>
reported.code === "SC0001" &&
reported.loc?.file?.endsWith("/main.js") &&
reported.loc?.file?.endsWith(variant === "sloppy" ? "/main.cjs" : "/main.js") &&
reported.loc.start === parsed.start &&
reported.message === ts.flattenDiagnosticMessageText(parsed.messageText, "\n")));
reported.message === ts.flattenDiagnosticMessageText(parsed.messageText, "\n"));
return parseDiagnostics(source, variant).some(matches) || earlyErrorDiagnostics(source, variant).some(matches);
}
export function summarize(results) {
+10
View File
@@ -6,6 +6,8 @@
"snapshotSha256": "584d260eaa9aef0c42d18b6b03e8b3755ecd54bdf43e01701f5b3eca3043c4c3",
"testFiles": 53597,
"tests": [
"test/annexB/language/function-code/function-redeclaration-block.js",
"test/annexB/language/function-code/if-decl-else-decl-b-func-existing-fn-no-init.js",
"test/built-ins/Array/prototype/at/returns-item-relative-index.js",
"test/built-ins/Array/prototype/at/returns-item.js",
"test/built-ins/Array/prototype/at/returns-undefined-for-holes-in-sparse-arrays.js",
@@ -169,10 +171,13 @@
"test/language/expressions/addition/S11.6.1_A4_T2.js",
"test/language/expressions/addition/S11.6.1_A4_T3.js",
"test/language/expressions/addition/S11.6.1_A4_T4.js",
"test/language/expressions/assignmenttargettype/direct-arrowfunction-2.js",
"test/language/expressions/function/dflt-params-duplicates.js",
"test/language/statements/const/dstr/ary-ptrn-elem-ary-elem-init.js",
"test/language/statements/const/dstr/ary-ptrn-elem-id-init-exhausted.js",
"test/language/statements/const/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js",
"test/language/statements/const/dstr/ary-ptrn-elem-obj-id-init.js",
"test/language/statements/const/dstr/ary-ptrn-rest-not-final-obj.js",
"test/language/statements/for/dstr/var-ary-ptrn-elem-ary-elem-init.js",
"test/language/statements/for/dstr/var-ary-ptrn-elem-id-init-exhausted.js",
"test/language/statements/for/dstr/var-ary-ptrn-elem-id-init-fn-name-arrow.js",
@@ -191,6 +196,8 @@
"LICENSE": "4dd9244dfe8197c75348c4b24ab53d29d3b1cfad143ac76b5a3d8942aa354ce0",
"harness/assert.js": "206e274ca325eb8a652e3911c3fbd090e2480d11ed7579dc17a5d17a2360ed48",
"harness/sta.js": "1930c54af79455c484799f43e9a28e2b2f15c40d0917c9941ca54e26db243f35",
"test/annexB/language/function-code/function-redeclaration-block.js": "8d4a3f259dabceeee101959aac117a19f5bfe9e374e1178b45f45a1142195d9e",
"test/annexB/language/function-code/if-decl-else-decl-b-func-existing-fn-no-init.js": "ccd95922fd6c784012f7299445f0859db04812232a2266dd90070b026244bbf5",
"test/built-ins/Array/prototype/at/returns-item-relative-index.js": "71f53cf86f1387101b19b6ae9939d47ced34497d3cee12f954eef454f912ba88",
"test/built-ins/Array/prototype/at/returns-item.js": "793b0f4a94ccf54a1e43813b3fde39d8e1f216cde9e22be674ecefd6a46e8e63",
"test/built-ins/Array/prototype/at/returns-undefined-for-holes-in-sparse-arrays.js": "32d020ce560af714f8c429f6d168507797304f72ef340f31c725c925e38d3969",
@@ -354,10 +361,13 @@
"test/language/expressions/addition/S11.6.1_A4_T2.js": "aee914241b607f56a98de04492f2d580e7bc69cca232af679d0c02b7025cc06b",
"test/language/expressions/addition/S11.6.1_A4_T3.js": "987b1c3cf66b6bbb2f6d8aaf6d1435cb1238b19132fa861a91aa2c35085d229d",
"test/language/expressions/addition/S11.6.1_A4_T4.js": "88afbecfcbb69c52a7ecc79c4b2dda351fffc5b75453b5b787a7f97afe8f4229",
"test/language/expressions/assignmenttargettype/direct-arrowfunction-2.js": "66ecd486e3cdb383ac6cdd655b70fc495b61bff90e6a125e491d9f1ed183c9f3",
"test/language/expressions/function/dflt-params-duplicates.js": "243c05deb19b7d54a13d4f69059a8fb8d55911f066b5cc0ae14cfc41503c28ae",
"test/language/statements/const/dstr/ary-ptrn-elem-ary-elem-init.js": "00f23ce81293acc6bd718a5a0de431928ed18db45c7ce849a0f76c365bc1ec9a",
"test/language/statements/const/dstr/ary-ptrn-elem-id-init-exhausted.js": "73e765928b855ea0f89bd159a317ec212179260523d444e38b3518c8ac6c1681",
"test/language/statements/const/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js": "086210fc19498dad9b760b90398a3b46b366f3c4d4ee184200136641752ba832",
"test/language/statements/const/dstr/ary-ptrn-elem-obj-id-init.js": "caf5d2dd8f46c3baa7d794cfdb26657437ea82bdda7a554137e52216df3afedb",
"test/language/statements/const/dstr/ary-ptrn-rest-not-final-obj.js": "c7247a6bf291a50f71c4f5b00f687f4890dc02539ecd60da1e8af8ae47e1c6ad",
"test/language/statements/for/dstr/var-ary-ptrn-elem-ary-elem-init.js": "06c43d01d5efa91acb451aafda580924e4c0ecf704d4bb94d88f55025cc4707e",
"test/language/statements/for/dstr/var-ary-ptrn-elem-id-init-exhausted.js": "8f06d6d8cb5ada2ca9a6900f5b25e586b50476d3182c0204673266c008626f70",
"test/language/statements/for/dstr/var-ary-ptrn-elem-id-init-fn-name-arrow.js": "2dc08413b20e87691d30bf136d9074d1e06019eedcddddbd46d94771cf6a0737",
@@ -0,0 +1,18 @@
// Copyright (C) 2019 Adrian Heine. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: In non-strict mode, duplicate LexicallyDeclaredNames in a block are allowed if they are bound by FunctionDeclarations
esid: sec-block-duplicates-allowed-static-semantics
es6id: B.3.3.4
flags: [noStrict]
info: |
B.3.3.4 Changes to Block Static Semantics: Early Errors
For web browser compatibility, that rule is modified with the addition of the **highlighted** text:
Block: {StatementList}
It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries, **unless the source code matching this production is not strict mode code and the duplicate entries are only bound by FunctionDeclarations**.
---*/
{ function a() {} function a() {} }
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/annex-b-fns/func-existing-fn-no-init.case
// - src/annex-b-fns/func/if-decl-else-decl-b.template
/*---
description: Existing variable binding is not modified (IfStatement with a declaration in both statement positions in function scope)
esid: sec-functiondeclarations-in-ifstatement-statement-clauses
flags: [generated, noStrict]
info: |
The following rules for IfStatement augment those in 13.6:
IfStatement[Yield, Return]:
if ( Expression[In, ?Yield] ) FunctionDeclaration[?Yield] else Statement[?Yield, ?Return]
if ( Expression[In, ?Yield] ) Statement[?Yield, ?Return] else FunctionDeclaration[?Yield]
if ( Expression[In, ?Yield] ) FunctionDeclaration[?Yield] else FunctionDeclaration[?Yield]
if ( Expression[In, ?Yield] ) FunctionDeclaration[?Yield]
B.3.3.1 Changes to FunctionDeclarationInstantiation
[...]
2. If instantiatedVarNames does not contain F, then
[...]
---*/
var init;
(function() {
init = f;
if (false) function _f() {} else function f() { return 'inner declaration'; }
function f() {
return 'outer declaration';
}
}());
assert.sameValue(init(), 'outer declaration');
@@ -0,0 +1,20 @@
// This file was procedurally generated from the following sources:
// - src/assignment-target-type/arrowfunction-2.case
// - src/assignment-target-type/invalid/direct.template
/*---
description: Static Semantics AssignmentTargetType, Return invalid. (Direct assignment)
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
Direct assignment
ArrowFunction
Static Semantics AssignmentTargetType, Return invalid.
---*/
$DONOTEVALUATE();
(x => x) = 1;
@@ -0,0 +1,61 @@
// This file was procedurally generated from the following sources:
// - src/function-forms/dflt-params-duplicates.case
// - src/function-forms/syntax/func-expr.template
/*---
description: It is a Syntax Error if BoundNames of FormalParameters contains any duplicate elements. (function expression)
esid: sec-function-definitions-runtime-semantics-evaluation
features: [default-parameters]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
FunctionExpression : function ( FormalParameters ) { FunctionBody }
[...]
3. Let closure be FunctionCreate(Normal, FormalParameters, FunctionBody,
scope, strict).
[...]
9.2.1 [[Call]] ( thisArgument, argumentsList)
[...]
7. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
[...]
9.2.1.3 OrdinaryCallEvaluateBody ( F, argumentsList )
1. Let status be FunctionDeclarationInstantiation(F, argumentsList).
[...]
9.2.12 FunctionDeclarationInstantiation(func, argumentsList)
[...]
23. Let iteratorRecord be Record {[[iterator]]:
CreateListIterator(argumentsList), [[done]]: false}.
24. If hasDuplicates is true, then
[...]
25. Else,
b. Let formalStatus be IteratorBindingInitialization for formals with
iteratorRecord and env as arguments.
[...]
14.1.2 Static Semantics: Early Errors
StrictFormalParameters : FormalParameters
- It is a Syntax Error if BoundNames of FormalParameters contains any
duplicate elements.
FormalParameters : FormalParameterList
- It is a Syntax Error if IsSimpleParameterList of FormalParameterList is
false and BoundNames of FormalParameterList contains any duplicate
elements.
---*/
$DONOTEVALUATE();
0, function(x = 0, x) {
};
@@ -0,0 +1,32 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-rest-not-final-obj.case
// - src/dstr-binding/default/const-stmt.template
/*---
description: Rest element (object binding pattern) may not be followed by any element (`const` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
LexicalBinding : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let value be GetValue(rhs).
3. ReturnIfAbrupt(value).
4. Let env be the running execution context's LexicalEnvironment.
5. Return the result of performing BindingInitialization for BindingPattern
using value and env as the arguments.
13.3.3 Destructuring Binding Patterns
ArrayBindingPattern[Yield] :
[ Elisionopt BindingRestElement[?Yield]opt ]
[ BindingElementList[?Yield] ]
[ BindingElementList[?Yield] , Elisionopt BindingRestElement[?Yield]opt ]
---*/
$DONOTEVALUATE();
const [...{ x }, y] = [1, 2, 3];