Fix static JavaScript edge cases and add Test262 coverage (#414)

This commit is contained in:
Chris Tate
2026-09-24 23:01:48 -05:00
committed by GitHub
parent 379bfd0474
commit 5c557d8ea4
92 changed files with 2630 additions and 64 deletions
+3
View File
@@ -10,6 +10,8 @@
"test": "vitest run",
"test:ts7": "node scripts/test-ts7.mjs",
"test:fetch-conformance": "vitest run tests/harness/fetch-conformance.test.ts",
"test:test262": "tsx tests/test262/run.ts",
"test:test262:fetch": "node tests/test262/fetch.mjs",
"test:cache-identity": "node tests/harness/cache-identity.mjs",
"test:sandbox": "node scripts/sandbox-test.mjs",
"test:sandbox:image": "node scripts/sandbox-image.mjs",
@@ -24,6 +26,7 @@
"devDependencies": {
"@types/node": "^24.0.0",
"eslint": "^9.20.0",
"js-yaml": "4.3.0",
"tsx": "^4.19.0",
"typescript": "5.9.3",
"typescript-eslint": "^8.24.0",
@@ -6,13 +6,23 @@ import type { Lowerer } from "../lowerer.js";
import { own } from "../lowerer.js";
import { isRequireMainFilename } from "../expressions/optional-chains.js";
import { STR_METHODS } from "../surfaces.js";
import { lowerOptionalArgument } from "../optional-arguments.js";
import { defaultAfterUndefined, lowerOptionalArgument, lowerStaticallyUndefinedArgument } from "../optional-arguments.js";
function lowerSplitLimitArg(lowerer: Lowerer, node: ts.Expression | undefined, loc: SrcLoc): IrExpr {
const defaultValue: IrExpr = { kind: "numLit", value: 4294967295, type: F64, loc };
return node ? lowerOptionalArgument(lowerer, node, F64, defaultValue) : defaultValue;
}
function lowerRegexSubject(lowerer: Lowerer, node: ts.Expression | undefined, loc: SrcLoc): IrExpr {
const absent: IrExpr = { kind: "strLit", value: "undefined", type: STRING, loc };
if (!node) return absent;
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, node);
if (undefinedArg) return defaultAfterUndefined(undefinedArg, absent);
const value = lowerer.lowerExpr(node);
if (value.kind === "unitLit") return { kind: "strLit", value: value.unit, type: STRING, loc };
return lowerer.ensureString(value, node);
}
/** Extract the checker-proven receiver arm from a runtime-optional property
* read. The receiver is stabilized once before its tag is tested, and a
* missing value throws the same property-read TypeError Node throws before
@@ -56,6 +66,9 @@ export function lowerRegexMethodCall(lowerer: Lowerer, call: ts.CallExpression,
};
const loc = locOf(call);
if (receiverKind === "regex" && name === "test") {
if (call.arguments.length > 1 || call.arguments.some(ts.isSpreadElement)) {
lowerer.noLowering("RegExp.prototype.test with surplus or spread arguments", call);
}
// The statefulness fence, at compile time where the flags are
// visible: a literal receiver (possibly parenthesized). Values that
// flow through variables hit the same fence at runtime.
@@ -68,7 +81,7 @@ export function lowerRegexMethodCall(lowerer: Lowerer, call: ts.CallExpression,
}
}
const receiver = lowerReceiver();
const args = call.arguments.map((a) => lowerer.ensureString(lowerer.lowerExpr(a), a));
const args = [lowerRegexSubject(lowerer, call.arguments[0], loc)];
return { kind: "regexIntrinsic", method: "test", receiver, args, type: BOOL, loc };
}
// `re.exec(s)` for non-g/y regexes: spec-identical to `s.match(re)`
@@ -79,7 +92,7 @@ export function lowerRegexMethodCall(lowerer: Lowerer, call: ts.CallExpression,
// compile time on literal receivers, exactly test()'s stance; values
// reaching the runtime with those flags abort there.
if (receiverKind === "regex" && name === "exec") {
if (call.arguments.length !== 1) return null; // exec takes exactly the subject
if (call.arguments.length > 1 || call.arguments.some(ts.isSpreadElement)) return null;
let recv: ts.Expression = access.expression;
while (ts.isParenthesizedExpression(recv)) recv = recv.expression;
if (ts.isRegularExpressionLiteral(recv)) {
@@ -89,9 +102,13 @@ export function lowerRegexMethodCall(lowerer: Lowerer, call: ts.CallExpression,
}
}
const re = lowerReceiver();
const subject = lowerer.lowerExprExpecting(call.arguments[0]!, STRING);
const subject = lowerRegexSubject(lowerer, call.arguments[0], loc);
const resultT: IrType = { kind: "union", unionId: lowerer.unions.intern([arrayOf(STRING), { kind: "nullT" }]) };
return { kind: "regexIntrinsic", method: "match", receiver: subject, args: [re], type: resultT, loc };
// The shared match intrinsic takes the string first; preserve exec's
// receiver-before-subject evaluation order before swapping operands.
const saved = lowerer.declareHiddenLocal("%execReceiver", re.type);
const result: IrExpr = { kind: "regexIntrinsic", method: "match", receiver: subject, args: [varRef(saved.id, re.type, loc)], type: resultT, loc };
return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: saved.id, init: re, loc }], result, type: resultT, loc };
}
// `s.match(re)` for non-g/y regexes: Node's exec-shaped result reduced
// to the honest slice — the `string[] | null` union holding
@@ -4783,6 +4783,9 @@ export function lowerForkCall(lowerer: Lowerer, expr: ts.CallExpression, loc: Sr
}
if (member === "stringify") {
const indent = stringifySpaceIndent(lowerer, call);
if (call.arguments.length === 0) {
return lowerer.wrappedUndefined(lowerer.withUndefinedArm(STRING), loc)!;
}
const argNode = call.arguments[0]!;
const value = lowerer.lowerExpr(argNode);
const optionalString = lowerOptionalStringifyRoot(lowerer, value, indent, loc);
@@ -8518,7 +8521,7 @@ const DATE_METHOD_HINT =
if (name === "toISOString") {
return { kind: "libCall", fn: "date.toISOStringValue", args: [receiver], type: STRING, loc };
}
const fn = DATE_GETTER_FNS[name];
const fn = own(DATE_GETTER_FNS, name);
if (fn !== undefined) {
return { kind: "libCall", fn, args: [receiver], type: F64, loc };
}
@@ -35,6 +35,7 @@ import { npmStaticPackageOfPath } from "../npm-static.js";
import { countedFor, varRef } from "../../ir/build.js";
import { rejectStaticThis } from "./static-this.js";
import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node-module.js";
import { lowerOptionalArgument } from "./optional-arguments.js";
/** How a parameter participates in CALL-SITE COMPLETION (the frontend
* completes every call to the one full signature, so the IR and backends
@@ -7655,9 +7656,10 @@ export function lowerPromiseMethodCall(lowerer: Lowerer, call: ts.CallExpression
if (receiver.type.kind !== "bigint") return null;
if (name === "valueOf" && call.arguments.length === 0) return receiver;
if (name === "toString" && call.arguments.length <= 1) {
const defaultRadix: IrExpr = { kind: "numLit", value: 10, type: F64, loc };
const radix: IrExpr = call.arguments[0]
? lowerer.lowerExprExpecting(call.arguments[0], F64)
: { kind: "numLit", value: 10, type: F64, loc };
? lowerOptionalArgument(lowerer, call.arguments[0], F64, defaultRadix)
: defaultRadix;
return { kind: "libCall", fn: "bigint.toString", args: [receiver, radix], type: STRING, loc };
}
return null;
@@ -318,14 +318,14 @@ function fenceProducedArrayElem(lowerer: Lowerer, node: ts.Node, producer: strin
}
// The lib declares wider call forms than the lowered surface —
// indexOf/includes take a fromIndex, join's separator is optional, the
// predicate/mapping HOFs take a thisArg. Each unlowered form is fenced
// join's separator is optional, and the predicate/mapping HOFs take
// a thisArg. Each unlowered form is fenced
// per site (SC2020), never silently truncated to the supported
// arguments. push/unshift lower every declared form (variadic, 0 args
// included — Node returns the unchanged length); reduce/reduceRight
// lower both declared forms (with and without an initial value).
const arity = {
push: [0, Number.MAX_SAFE_INTEGER], unshift: [0, Number.MAX_SAFE_INTEGER], pop: [0, 0], indexOf: [1, 1], includes: [1, 1], join: [1, 1],
push: [0, Number.MAX_SAFE_INTEGER], unshift: [0, Number.MAX_SAFE_INTEGER], pop: [0, 0], indexOf: [1, 2], includes: [1, 2], join: [1, 1],
concat: [0, Number.MAX_SAFE_INTEGER],
slice: [0, 2], shift: [0, 0], splice: [1, 2], at: [1, 1],
map: [1, 1], filter: [1, 1], forEach: [1, 1], find: [1, 1], findIndex: [1, 1], some: [1, 1],
@@ -342,12 +342,10 @@ function fenceProducedArrayElem(lowerer: Lowerer, node: ts.Node, producer: strin
? "the removal forms lower — splice(start, deleteCount?); to insert, build a new array with slice and push"
: name === "join"
? 'pass the separator explicitly: join(",")'
: name === "indexOf" || name === "includes"
? "the fromIndex parameter has no lowering — slice first, or loop"
: name === "map" || name === "filter" || name === "forEach" ||
name === "find" || name === "some" || name === "every" || name === "flatMap"
? "the thisArg parameter has no lowering — use an arrow function"
: undefined;
: name === "map" || name === "filter" || name === "forEach" ||
name === "find" || name === "some" || name === "every" || name === "flatMap"
? "the thisArg parameter has no lowering — use an arrow function"
: undefined;
lowerer.noLowering(
`.${name} with ${call.arguments.length} argument${call.arguments.length === 1 ? "" : "s"}`,
call,
@@ -614,7 +612,11 @@ function lowerArraySearchCall(
const loc = locOf(call);
const receiver = lowerer.lowerExpr(access.expression);
const argNode = call.arguments[0]!;
if (call.arguments.some(ts.isSpreadElement)) lowerer.noLowering(`.${method} with spread arguments`, call);
let needle = lowerer.lowerExpr(argNode);
const fromIndex = call.arguments[1]
? lowerOptionalArgument(lowerer, call.arguments[1], F64, numLit(0, loc))
: numLit(0, loc);
// A derived CLASS VALUE against a base-classval element widens (the same
// pointer — identity search is exact); the coercion path owns the ABI gate.
if (needle.type.kind === "classval" && elem.kind === "classval" && !typeEquals(needle.type, elem)) {
@@ -623,7 +625,7 @@ function lowerArraySearchCall(
// Preserve the existing fast path for an ordinary, non-union needle. Its
// runtime implementation already has exact primitive/reference equality.
if (elem.kind !== "union" && elem.kind !== "bigint" && typeEquals(needle.type, elem)) {
if (call.arguments.length === 1 && elem.kind !== "union" && elem.kind !== "bigint" && typeEquals(needle.type, elem)) {
return {
kind: "arrIntrinsic",
method,
@@ -634,11 +636,12 @@ function lowerArraySearchCall(
};
}
const valueT = arrayValueType(lowerer, elem);
let valueT = arrayValueType(lowerer, elem);
// jsval/dyn arrays have a single engine value representation rather than a
// static undefined arm. Keep their existing runtime path and its explicit
// type fence; typed static arrays take the state-aware helper below.
if (valueT.kind !== "union") {
if (call.arguments.length > 1) lowerer.noLowering(`.${method} with fromIndex on '${lowerer.fmt(elem)}' elements`, call);
if (!typeEquals(needle.type, elem)) lowerer.badType(argNode, lowerer.typeOf(argNode));
return {
kind: "arrIntrinsic",
@@ -650,10 +653,25 @@ function lowerArraySearchCall(
};
}
// Search uses strict equality, never conversion to the array's element
// type. Retain primitive needles of a different kind in the comparison
// union so a nonmatching kind returns a miss instead of throwing.
const needleArms = needle.type.kind === "union"
? lowerer.unions.get(needle.type.unionId)!.arms
: [needle.type];
const scalarNeedle = needleArms.every((arm) =>
arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" ||
arm.kind === "bigint" || arm.kind === "symbol" || isUnitType(arm));
const valueArms = lowerer.unions.get(valueT.unionId)!.arms;
if (scalarNeedle && !valueArms.some((arm) => arm.kind === "func" || arm.kind === "set")) {
const arms = [...valueArms];
for (const arm of needleArms) if (!arms.some((existing) => typeEquals(existing, arm))) arms.push(arm);
if (arms.length !== valueArms.length) valueT = { kind: "union", unionId: lowerer.unions.intern(arms) };
}
const searchNeedle = lowerer.coerceInto(argNode, needle, valueT);
if (!typeEquals(searchNeedle.type, valueT)) lowerer.badType(argNode, lowerer.typeOf(argNode));
const helper = arraySearchHelper(lowerer, method, elem, valueT, loc);
return { kind: "call", callee: helper, args: [receiver, searchNeedle], type: method === "indexOf" ? F64 : BOOL, loc };
return { kind: "call", callee: helper, args: [receiver, searchNeedle, fromIndex], type: method === "indexOf" ? F64 : BOOL, loc };
}
/** SameValueZero over a static value union. `unionEq` supplies strict
@@ -722,6 +740,31 @@ function arraySearchHelper(
const needleRef = varRef("needle.0", valueT, loc);
const stateRef = varRef("state.0", F64, loc);
const nextRef = varRef("next.0", F64, loc);
const fromRef = varRef("from.0", F64, loc);
const integerRef = varRef("integer.0", F64, loc);
// ToIntegerOrInfinity, followed by the relative start index. Keeping the
// length read inside the helper preserves argument effects before search.
const integer: IrExpr = {
kind: "ternary",
cond: { kind: "libCall", fn: "num.isNaN", args: [fromRef], type: BOOL, loc },
then: numLit(0, loc),
else_: { kind: "libCall", fn: "math.trunc", args: [fromRef], type: F64, loc },
type: F64,
loc,
};
const start: IrExpr = {
kind: "ternary",
cond: { kind: "bin", op: "<", left: integerRef, right: numLit(0, loc), type: BOOL, loc },
then: {
kind: "libCall", fn: "math.max",
args: [{ kind: "bin", op: "+", left: varRef("n.0", F64, loc), right: integerRef, type: F64, loc }, numLit(0, loc)],
type: F64, loc,
},
// Adding positive zero canonicalizes a truncated negative zero.
else_: { kind: "bin", op: "+", left: integerRef, right: numLit(0, loc), type: F64, loc },
type: F64,
loc,
};
const rawRead: IrExpr = { kind: "arrayGet", arr: arrRef, index: indexRef, type: elem, loc };
const missing = lowerer.wrappedUndefined(valueT, loc);
if (!missing) throw new InternalCompilerError("array search helper needs an undefined arm");
@@ -799,11 +842,14 @@ function arraySearchHelper(
params: [
{ localId: "a.0", name: "a", type: arrT },
{ localId: "needle.0", name: "needle", type: valueT },
{ localId: "from.0", name: "from", type: F64 },
],
returnType: resultT,
locals: [
{ id: "a.0", name: "a", type: arrT, mutable: true },
{ id: "needle.0", name: "needle", type: valueT, mutable: true },
{ id: "from.0", name: "from", type: F64, mutable: false },
{ id: "integer.0", name: "integer", type: F64, mutable: false },
{ id: "n.0", name: "n", type: F64, mutable: false },
{ id: "i.0", name: "i", type: F64, mutable: true },
{ id: "state.0", name: "state", type: F64, mutable: false },
@@ -812,7 +858,8 @@ function arraySearchHelper(
],
body: [
readLenStmt(arrT, loc),
{ kind: "varDecl", localId: "i.0", init: numLit(0, loc), loc },
{ kind: "varDecl", localId: "integer.0", init: integer, loc },
{ kind: "varDecl", localId: "i.0", init: start, loc },
{
kind: "while",
cond: { kind: "bin", op: "<", left: indexRef, right: varRef("n.0", F64, loc), type: BOOL, loc },
@@ -1452,7 +1452,12 @@ export function fenceStaticHeadersIteration(
node: ts.Node,
): void {
if (lowerer.dynamic) return;
const value = ts.isExpression(node) ? requestInitValueExpr(node) : node;
// The initializer is the value being iterated. Asking for the binding
// pattern's synthesized tuple type can panic in TypeScript serialization.
const source = ts.isArrayBindingPattern(node) && ts.isVariableDeclaration(node.parent) && node.parent.initializer
? node.parent.initializer
: node;
const value = ts.isExpression(source) ? requestInitValueExpr(source) : source;
const sym = isStdlibFetchInterface(lowerer, value, "Headers");
if (!sym) return;
lowerer.noLowering(
@@ -2930,6 +2930,11 @@ export class Lowerer {
const e = peel(node);
if (isArrayRead(e)) return true;
if (ts.isCallExpression(e) && this.runtimeOptionalReduceTypes.has(e)) return true;
if (
ts.isCallExpression(e) && e.arguments.length === 0 &&
ts.isPropertyAccessExpression(e.expression) &&
e.expression.name.text === "stringify" && this.isStdlibGlobal(e.expression.expression, "JSON")
) return true;
if (
ts.isCallExpression(e) &&
ts.isPropertyAccessExpression(e.expression) &&
@@ -6697,12 +6697,48 @@
],
"diags": []
},
"<repo>/tests/corpus/2974-array-search-from-index.ts": {
"order": [
"<repo>/tests/corpus/2974-array-search-from-index.ts"
],
"diags": []
},
"<repo>/tests/corpus/2974-numeric-remainder.ts": {
"order": [
"<repo>/tests/corpus/2974-numeric-remainder.ts"
],
"diags": []
},
"<repo>/tests/corpus/2975-bigint-default-radix.ts": {
"order": [
"<repo>/tests/corpus/2975-bigint-default-radix.ts"
],
"diags": []
},
"<repo>/tests/corpus/2976-json-stringify-no-arguments.js": {
"order": [
"<repo>/tests/corpus/2976-json-stringify-no-arguments.js"
],
"diags": []
},
"<repo>/tests/corpus/2977-nested-array-binding-defaults.js": {
"order": [
"<repo>/tests/corpus/2977-nested-array-binding-defaults.js"
],
"diags": []
},
"<repo>/tests/corpus/2978-regexp-optional-subject.js": {
"order": [
"<repo>/tests/corpus/2978-regexp-optional-subject.js"
],
"diags": []
},
"<repo>/tests/corpus/2979-array-search-mixed-needles.js": {
"order": [
"<repo>/tests/corpus/2979-array-search-mixed-needles.js"
],
"diags": []
},
"<repo>/tests/corpus/300-if-else.ts": {
"order": [
"<repo>/tests/corpus/300-if-else.ts"
@@ -7939,6 +7975,12 @@
],
"diags": []
},
"<repo>/tests/diagnostics/date-formatters.ts": {
"order": [
"<repo>/tests/diagnostics/date-formatters.ts"
],
"diags": []
},
"<repo>/tests/diagnostics/date-union-fences.ts": {
"order": [
"<repo>/tests/diagnostics/date-union-fences.ts"
+3
View File
@@ -17,6 +17,9 @@ importers:
eslint:
specifier: ^9.20.0
version: 9.39.5
js-yaml:
specifier: 4.3.0
version: 4.3.0
tsx:
specifier: ^4.19.0
version: 4.23.0
+17
View File
@@ -1,4 +1,6 @@
export const MAX_INLINE_SANDBOX_COMMAND_BYTES = 768;
// Outside the shell exit-code range, so an actual exit 125 stays a failure.
export const REMOTE_COMMAND_PENDING = 256;
export const shellQuote = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
@@ -20,3 +22,18 @@ export function sandboxCommand(command, args, exitMarker) {
argv: file ? ["sh", scriptPath] : ["sh", "-c", script],
};
}
/** Allow a disconnected command to finish before probing its recorded exit. */
export function sandboxStatusCommand(statusPath, exitMarker, waitSeconds = 20) {
if (!Number.isSafeInteger(waitSeconds) || waitSeconds < 0) {
throw new Error("status wait must be a nonnegative integer");
}
return (
`scriptc_wait=${waitSeconds}; ` +
`while test ! -s ${shellQuote(statusPath)} && test "$scriptc_wait" -gt 0; do ` +
`sleep 1; scriptc_wait=$((scriptc_wait - 1)); done; ` +
`scriptc_status=${REMOTE_COMMAND_PENDING}; test ! -s ${shellQuote(statusPath)} || ` +
`scriptc_status=$(cat ${shellQuote(statusPath)}); ` +
`printf '\\n${exitMarker}%s\\n' "$scriptc_status"`
);
}
+26 -27
View File
@@ -23,7 +23,7 @@ import {
filterExistingWorktreePaths,
workspaceResetCommand,
} from "./worktree-files.mjs";
import { sandboxCommand, shellQuote } from "./sandbox-command.mjs";
import { REMOTE_COMMAND_PENDING, sandboxCommand, sandboxStatusCommand, shellQuote } from "./sandbox-command.mjs";
const root = fileURLToPath(new URL("../", import.meta.url));
const laneCaseShardedFiles = [
@@ -31,6 +31,7 @@ const laneCaseShardedFiles = [
"tests/harness/llvm-differential.test.ts",
"tests/harness/npm.test.ts",
"tests/harness/server.test.ts",
"tests/harness/test262.test.ts",
];
// Coverage analysis is frontend-only: SCRIPTC_SAN cannot change its result.
// It still case-shards across the selected lane so every corpus entry is
@@ -357,7 +358,7 @@ function run(
} else if (exitMarker && remoteExitCode === undefined) {
reject(new Error(`${label ?? command} did not report its remote exit status`));
} else if (remoteExitCode !== undefined && remoteExitCode !== 0) {
reject(new Error(`${label ?? command} remote command exited ${remoteExitCode}`));
reject(Object.assign(new Error(`${label ?? command} remote command exited ${remoteExitCode}`), { remoteExitCode }));
} else {
resolve();
}
@@ -412,6 +413,7 @@ const execIn = async (
worker.name,
...prepared.argv,
];
const deadline = Date.now() + wallTimeoutMs;
try {
await vercel(commandArgs, {
exitMarker,
@@ -420,32 +422,29 @@ const execIn = async (
timeoutMs: wallTimeoutMs,
});
} catch (error) {
if (error.remoteExitCode !== undefined) throw error;
console.warn(`[${label}] CLI completion was not confirmed (${error.message}); checking the remote command status...`);
const probeMarker = `__SCRIPTC_REMOTE_PROBE_${randomBytes(12).toString("hex")}__`;
const probeScript =
`scriptc_status=125; test ! -f ${shellQuote(statusPath)} || ` +
`scriptc_status=$(cat ${shellQuote(statusPath)}); ` +
`printf '\\n${probeMarker}%s\\n' "$scriptc_status"`;
await vercel(
[
"sandbox",
"exec",
"--timeout",
"1m",
"--workdir",
workdir,
worker.name,
"sh",
"-c",
probeScript,
],
{
exitMarker: probeMarker,
idleTimeoutMs: 30_000,
label: `${label} status`,
timeoutMs: 60_000,
},
);
for (;;) {
const remaining = deadline - Date.now();
if (remaining <= 0) throw new Error(`${label} did not confirm completion before its timeout`, { cause: error });
const probeMarker = `__SCRIPTC_REMOTE_PROBE_${randomBytes(12).toString("hex")}__`;
const probeScript = sandboxStatusCommand(statusPath, probeMarker, Math.min(20, Math.floor(remaining / 1000)));
try {
await vercel(
["sandbox", "exec", "--timeout", "1m", "--workdir", workdir, worker.name, "sh", "-c", probeScript],
{
exitMarker: probeMarker,
idleTimeoutMs: 30_000,
label: `${label} status`,
timeoutMs: Math.min(60_000, remaining),
},
);
return;
} catch (probeError) {
if (probeError.remoteExitCode !== REMOTE_COMMAND_PENDING) throw probeError;
console.log(`[${label}] remote command has not recorded completion; waiting...`);
}
}
}
};
@@ -32,6 +32,8 @@ child.once("message", () => {
sent++;
});
});
child.once("exit", (code) => {
// Exit can precede delivery of the last IPC message. Close waits for the
// channel to drain before observing the completed exchange.
child.once("close", (code) => {
console.log("replies", replies, "sent", sent, "disconnected", disconnected, "exit", code);
});
@@ -0,0 +1,39 @@
const values = [1, 2, 1, NaN, -0];
for (const start of [0, -0, 1, 1.9, -1, -1.9, -5, -6, 5, 9, NaN, Infinity, -Infinity]) {
console.log(start, values.indexOf(1, start), values.includes(1, start));
console.log(values.indexOf(NaN, start), values.includes(NaN, start));
console.log(values.indexOf(0, start), values.includes(0, start));
}
console.log(values.indexOf(1, undefined), values.includes(1, undefined));
function search(start?: number): void {
console.log(values.indexOf(1, start), values.includes(1, start));
}
search();
search(2);
const sparse: (number | undefined)[] = [];
sparse[1] = undefined;
sparse[3] = 7;
sparse[1_000_000_000] = 9;
console.log(sparse.indexOf(undefined, 1), sparse.indexOf(undefined, 2));
console.log(sparse.includes(undefined, 2), sparse.includes(undefined, 1_000_000_000));
console.log(sparse.indexOf(9, 100), sparse.indexOf(9, -1), sparse.includes(9, -1));
console.log(["a", "b", "a"].indexOf("a", 1), ["a", "b", "a"].includes("b", -1));
console.log([false, true, false].indexOf(false, 1), [false, true].includes(false, 1));
console.log([1n, 2n, 1n].indexOf(1n, 1), [1n, 2n].includes(1n, -1));
const first = [1];
const second = [2];
const refs = [first, second, first];
console.log(refs.indexOf(first, 1), refs.includes(first, -1), refs.indexOf([1], 0));
// Every operand runs once, in receiver/needle/start order. Argument effects
// happen before the method captures the array length.
let trace = "";
const growing = [1];
function receiver(): number[] { trace += "r"; return growing; }
function needle(): number { trace += "n"; return 2; }
function start(): number { trace += "s"; growing.push(2); return -1; }
console.log(receiver().indexOf(needle(), start()), trace);
trace = "";
console.log(receiver().includes(needle(), void start()), trace);
+10
View File
@@ -0,0 +1,10 @@
for (const value of [-100n, 0n, 100n, 123456789012345678901234567890n]) {
console.log(value.toString(), value.toString(undefined));
}
function printRadix(radix?: number): void {
console.log((255n).toString(radix));
}
printRadix();
printRadix(16);
let effects = 0;
console.log((255n).toString(void effects++), effects);
@@ -0,0 +1,8 @@
const value = JSON.stringify();
console.log(value, typeof value, value === undefined);
console.log(JSON.stringify() === undefined);
function missing() { return JSON.stringify(); }
function observe(value) { console.log(value, typeof value); }
const record = { value: JSON.stringify() };
console.log(missing(), missing() === undefined, record.value === undefined);
observe(JSON.stringify());
@@ -0,0 +1,12 @@
const [[x, y, z] = [4, 5, 6]] = [];
console.log(x, y, z);
let [[a, b] = [7, 8]] = [];
console.log(a, b);
var [[c] = [9]] = [];
console.log(c);
let once = true;
for (var [[d, e] = [10, 11]] = []; once; once = false) {
console.log(d, e);
}
const [[f] = [12]] = [[13]];
console.log(f);
@@ -0,0 +1,15 @@
console.log(/^undefined$/.test(), /^undefined$/.test(undefined));
console.log(/^null$/.test(null), /^123$/.test(123), /^true$/.test(true));
console.log(JSON.stringify(/^(undefined)$/.exec()));
console.log(JSON.stringify(/^(undefined)$/.exec(undefined)));
console.log(JSON.stringify(/^(null)$/.exec(null)));
console.log(JSON.stringify(/^(123)$/.exec(123)));
let effects = 0;
console.log(/^undefined$/.test(void effects++), effects);
let trace = "";
function receiver() { trace += "r"; return /^x$/; }
function subject() { trace += "s"; return "x"; }
console.log(JSON.stringify(receiver().exec(subject())), trace);
let current = /^x$/;
function changeReceiver() { current = /^y$/; return "x"; }
console.log(JSON.stringify(current.exec(changeReceiver())));
@@ -0,0 +1,13 @@
console.log(["true"].indexOf(true), ["0"].includes(0));
console.log([false].indexOf(0), [false].includes("false", 0));
console.log([undefined].indexOf(0), [null].includes(undefined));
console.log([[]].indexOf(0), [[1]].includes("1"));
console.log([1n].indexOf(1), [1].includes(1n));
console.log(["true", "other"].indexOf(true, 1));
let effects = 0;
function needle() { effects++; return true; }
function start() { effects++; return 0; }
console.log(["true"].indexOf(needle(), start()), effects);
const sparse = ["one"];
sparse[3] = "four";
console.log(sparse.includes(undefined, 1), sparse.indexOf(undefined, 1));
+5
View File
@@ -0,0 +1,5 @@
// Unsupported formatters must reach the Date method diagnostic, including
// names inherited from Object.prototype by the compiler's dispatch table.
const date = new Date(0);
date.toString();
date.toLocaleString();
+1 -1
View File
@@ -47,7 +47,7 @@ const localized = (1234.5).toLocaleString();
// Unlowered call FORMS of lowered members. (push is fully variadic now —
// no fenced form remains to pin.)
const nums = [1, 2, 3];
const found = nums.indexOf(2, 1);
const found = nums.indexOf(2, 1); // the fromIndex overload lowers
const joined = nums.join();
nums.forEach((x) => console.log(x), { unused: true }); // thisArg stays fenced
const pos = "abc".includes("b", 1); // the position form lowers now (indexOf's clamp)
+5 -1
View File
@@ -1,6 +1,6 @@
# Test harness
Two lanes over the same suite: plain (`pnpm test`) and sanitized (`SCRIPTC_SAN=1 pnpm test`, ASan + the runtime RC audit). Node is the oracle everywhere — corpus programs run under Node and as compiled binaries, and outputs must agree byte-for-byte. Both lanes must be green before a commit.
Two lanes over the same suite: plain (`pnpm test`) and sanitized (`SCRIPTC_SAN=1 pnpm test`, ASan + the runtime RC audit). Corpus programs use Node as the oracle: they run under Node and as compiled binaries, and outputs must match exactly. The Test262 profile uses upstream assertions as its oracle. Both lanes must be green before a commit.
One deliberate exception to raw byte-compare: `node:test` programs (tests/harness/node-test.test.ts over tests/fixtures/node-test) cannot live in the corpus because Node's spec reporter embeds a real duration in EVERY result line — no node:test program has deterministic stdout, under Node itself included. Those fixtures still run both lanes against the Node oracle, but with one documented normalization applied to both sides (durations, stack frames, the inspect property block); everything else — symbols, indentation, directives, summary counts, the failing-section "test at" locations and error messages — must match byte-exactly, plus exit-code parity against the fixture's `// @exit:` line. Fixtures never console.log inside test bodies: Node's reporter stream lags console output racily, so mixed programs aren't byte-comparable against any oracle.
@@ -86,3 +86,7 @@ Compiler environment variables that can resolve mutable compilation inputs (`CPA
`pnpm test:cache-identity` (optionally `--san`) is the acceptance artifact: it runs the full suite uncached, cache-populating, and cached, then diffs every test's name/status/failure output between the cached and uncached passes and exits nonzero on any drift.
`pnpm build` is incremental (tsbuildinfo under `node_modules/.cache/scriptc-tsc/`); `pnpm build:fresh` is the clean-build escape.
## Test262
The default static compiler has a pinned Test262 regression profile and a separate full-snapshot survey runner. See [tests/test262/README.md](../test262/README.md) for commands, outcome reporting, and the current strict-script and scalar-assertion limits. Both plain and sanitized Sandbox lanes run the regression profile; dynamic islands are disabled.
@@ -0,0 +1,17 @@
date-formatters.ts:4:1 - error SC2020: 'Date.prototype.toString' is part of the standard library types but has no scriptc lowering yet
3 | const date = new Date(0);
4 | date.toString();
| ^~~~~~~~~~~~~~~
5 | date.toLocaleString();
hint: getTime(), valueOf(), toISOString(), the local/UTC calendar getters, and getTimezoneOffset() are supported; Date setters and locale/string formatters have no lowering
date-formatters.ts:5:1 - error SC2020: 'Date.prototype.toLocaleString' is part of the standard library types but has no scriptc lowering yet
4 | date.toString();
5 | date.toLocaleString();
| ^~~~~~~~~~~~~~~~~~~~~
6 |
hint: getTime(), valueOf(), toISOString(), the local/UTC calendar getters, and getTimezoneOffset() are supported; Date setters and locale/string formatters have no lowering
@@ -224,18 +224,9 @@ stdlib-fence.ts:46:19 - error SC2020: 'Number.prototype.toLocaleString without a
hint: the default locale is the host environment's, which a compiled binary cannot carry — pass it explicitly: x.toLocaleString("en-US")
stdlib-fence.ts:50:15 - error SC2020: '.indexOf with 2 arguments' is part of the standard library types but has no scriptc lowering yet
49 | const nums = [1, 2, 3];
50 | const found = nums.indexOf(2, 1);
| ^~~~~~~~~~~~~~~~~~
51 | const joined = nums.join();
hint: the fromIndex parameter has no lowering — slice first, or loop
stdlib-fence.ts:51:16 - error SC2020: '.join with 0 arguments' is part of the standard library types but has no scriptc lowering yet
50 | const found = nums.indexOf(2, 1);
50 | const found = nums.indexOf(2, 1); // the fromIndex overload lowers
51 | const joined = nums.join();
| ^~~~~~~~~~~
52 | nums.forEach((x) => console.log(x), { unused: true }); // thisArg stays fenced
+30 -2
View File
@@ -1,8 +1,9 @@
import { execFileSync } from "node:child_process";
import { execFile, execFileSync } from "node:child_process";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
import { afterEach, expect, test } from "vitest";
import { sandboxCommand } from "../../scripts/sandbox-command.mjs";
import { REMOTE_COMMAND_PENDING, sandboxCommand, sandboxStatusCommand } from "../../scripts/sandbox-command.mjs";
const roots: string[] = [];
const statuses: string[] = [];
@@ -48,3 +49,30 @@ test("uploaded scripts preserve long and shell-sensitive argument bytes", async
await expect(readFile(sentinel)).rejects.toMatchObject({ code: "ENOENT" });
expect(await readFile(prepared.statusPath, "utf8")).toBe("0\n");
});
test("status recovery waits for a disconnected command's actual exit", async () => {
const root = await mkdtemp("/tmp/scriptc-sandbox-status-test-");
roots.push(root);
const path = join(root, "command.status");
const marker = "__STATUS_TEST__";
const pending = promisify(execFile)("sh", ["-c", sandboxStatusCommand(path, marker, 2)]);
await new Promise((resolve) => setTimeout(resolve, 50));
await writeFile(path, "7\n");
expect((await pending).stdout).toBe(`\n${marker}7\n`);
await writeFile(path, "0\n");
expect(execFileSync("sh", ["-c", sandboxStatusCommand(path, marker, 0)], { encoding: "utf8" })).toBe(`\n${marker}0\n`);
await writeFile(path, "125\n");
expect(execFileSync("sh", ["-c", sandboxStatusCommand(path, marker, 0)], { encoding: "utf8" })).toBe(`\n${marker}125\n`);
});
test("an absent or incomplete status remains pending rather than passing", async () => {
const root = await mkdtemp("/tmp/scriptc-sandbox-status-test-");
roots.push(root);
const path = join(root, "command.status");
const marker = "__STATUS_TEST__";
const probe = () => execFileSync("sh", ["-c", sandboxStatusCommand(path, marker, 0)], { encoding: "utf8" });
expect(probe()).toBe(`\n${marker}${REMOTE_COMMAND_PENDING}\n`);
await writeFile(path, "");
expect(probe()).toBe(`\n${marker}${REMOTE_COMMAND_PENDING}\n`);
});
+85
View File
@@ -0,0 +1,85 @@
import { expect, test } from "vitest";
import { boundedRun } from "../test262/execute.js";
import { compileFailureStatus, exclusion, metadata, prepare, summarize, variants } from "../test262/support.mjs";
const source = (yaml: string, body = "assert.sameValue(1, 1);") => `/*---\n${yaml}\n---*/\n${body}`;
test("Test262 YAML metadata handles block text, lists, features and negative phases", () => {
const meta = metadata(source(`description: >
flags: [noStrict] inside a description is not a flag
flags:
- onlyStrict
features: [BigInt]
includes: [propertyHelper.js]
negative:
phase: parse
type: SyntaxError`));
expect(meta.flags).toEqual(["onlyStrict"]);
expect(meta.negative).toEqual({ phase: "parse", type: "SyntaxError" });
expect(meta.features).toEqual(["BigInt"]);
expect(meta.includes).toEqual(["propertyHelper.js"]);
});
test("metadata generates the upstream variants without rewriting execution goals", () => {
expect(variants(metadata(source("description: plain")))).toEqual(["sloppy", "strict"]);
for (const [flag, expected] of [["onlyStrict", "strict"], ["noStrict", "sloppy"], ["module", "module"], ["raw", "raw"]]) {
expect(variants(metadata(source(`flags: [${flag}]`)))).toEqual([expected]);
}
expect(() => metadata(source("flags: [onlyStrict, noStrict]"))).toThrow();
expect(variants(metadata(source("flags: [module, raw]")))).toEqual(["module"]);
expect(() => metadata(source("flags: [futureFlag]"))).toThrow();
expect(() => metadata(source("negative: {phase: compile, type: SyntaxError}"))).toThrow();
});
test("unsupported execution requirements and assertion reflection remain exclusions", () => {
for (const [head, body] of [
["negative: {phase: parse, type: SyntaxError}", "invalid syntax"],
["negative: {phase: runtime, type: TypeError}", "throw new TypeError();"],
["flags: [async]", "$DONE();"],
["includes: [propertyHelper.js]", "verifyProperty({}, 'x', {});"],
["description: global script", "assert.sameValue(this, globalThis);"],
["description: reflection", "assert.sameValue(typeof assert, 'function');"],
["description: mutation", "assert.sameValue = () => {};"],
["description: unsupported helper", "assert.throws(TypeError, () => {});"],
]) {
const text = source(head!, body);
expect(exclusion(text, metadata(text), "strict")).toBeTypeOf("string");
}
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");
});
test("the adapter retains the test body without a function or try/catch wrapper", () => {
const body = source("description: lexical", "const x = 1;\nassert.sameValue(x, 1);");
const result = prepare(body);
expect(result.startsWith('"use strict";\n')).toBe(true);
expect(result).toContain(`\n${body}\n`);
expect(result).not.toContain("try {");
});
test("reports keep exclusions, refusals, failures and passes separate", () => {
expect(summarize([
{ status: "pass" }, { status: "compile-refusal" }, { status: "fail" },
{ status: "excluded", reason: "execution:sloppy" },
{ status: "excluded", reason: "execution:sloppy" },
])).toEqual({
counts: { pass: 1, "compile-refusal": 1, fail: 1, excluded: 2 },
exclusions: { "execution:sloppy": 2 },
});
});
test("internal compiler failures cannot be classified as intentional refusals", () => {
for (const code of ["SC0004", "SC9001", "SC9002"]) {
expect(compileFailureStatus([{ code, loc: { file: "/tmp/harness.ts" } }])).toBe("compiler-error");
}
for (const code of ["SC3003", "SC3004"]) expect(compileFailureStatus([{ code }])).toBe("build-error");
for (const code of ["SC2020", "SC3001", "SC3002"]) expect(compileFailureStatus([{ code }])).toBe("compile-refusal");
expect(compileFailureStatus([{ code: "SC2020", loc: { file: "/tmp/harness.ts" } }])).toBe("harness-refusal");
});
test("a stuck process is killed and reported as a timeout", async () => {
const result = await boundedRun(process.execPath, ["-e", "setInterval(() => {}, 1000)"], 100);
expect(result.timeout).toBe(true);
expect(result.code).not.toBe(0);
});
+81
View File
@@ -0,0 +1,81 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { createContext, runInContext } from "node:vm";
import { describe, expect, test } from "vitest";
import { runSource } from "../test262/execute.js";
import { exclusion, matchesExpectation, metadata, pin, vendorRoot, verifyVendor } from "../test262/support.mjs";
import { shardSelect, shardSuffix } from "./shard.js";
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 {
const context = createContext({});
runInContext(upstreamHarness, context, { timeout: 5000 });
runInContext(`"use strict";\n${source}`, context, { timeout: 5000 });
}
test("Test262 regression inputs retain their pinned upstream bytes", () => {
verifyVendor();
});
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()}`, () => {
for (const path of profileCases) {
test(path, async () => {
const source = readFileSync(join(vendorRoot, path), "utf8");
expect(exclusion(source, metadata(source, path), "strict")).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);
});
}
});
const controls = [
{ name: "scalar SameValue", status: "pass", source: `
assert(true);
assert.sameValue(NaN, NaN);
assert.notSameValue(0, -0);
assert.notSameValue(null, undefined);
assert.sameValue(undefined, undefined);
assert.sameValue(null, null);
assert.notSameValue(1, "1");
` },
{ name: "wrong SameValue", status: "fail", source: "assert.sameValue(1, 2);" },
{ name: "signed zero mismatch", status: "fail", source: "assert.sameValue(0, -0);" },
{ name: "NaN is the same value", status: "fail", source: "assert.notSameValue(NaN, NaN);" },
{ name: "assert requires true, not truthiness", status: "fail", source: "assert(1);" },
];
describe(`Test262 host assertion contract${shardSuffix()}`, () => {
for (const control of shardSelect(controls, (item) => `host:${item.name}`)) {
test(control.name, async () => {
const nodeRun = () => runUpstream(control.source);
if (control.status === "pass") expect(nodeRun).not.toThrow();
else expect(nodeRun).toThrow();
const result = await runSource(control.source, { sanitize });
expect(result, JSON.stringify(result, null, 2)).toMatchObject({ status: control.status });
});
}
test("successful early process exit cannot masquerade as test completion", async () => {
const result = await runSource("process.exit(0);", { sanitize });
expect(result.status).toBe("fail");
});
for (const value of shardSelect(["{}", "[1]", "function () {}"], (value) => `reference:${value}`)) {
test(`reference assertions are explicitly refused: ${value}`, async () => {
const source = `const value = ${value}; try { assert.sameValue(value, value); } catch { }`;
expect(() => runUpstream(source)).not.toThrow();
const result = await runSource(source, { sanitize });
expect(result, JSON.stringify(result)).toMatchObject({ status: "harness-refusal", reason: "reference-assertion" });
});
}
});
+39
View File
@@ -0,0 +1,39 @@
# Static scriptc and Test262
This runner compiles Test262 programs with `dynamic: false` and scriptc's default backend selection, then executes native binaries in separate processes. `upstream.json` pins the Test262 revision, archive checksum, complete snapshot digest, and unmodified vendored regression inputs. Test262's BSD license is retained in `vendor/LICENSE`.
Run the offline regression profile from the repository root:
```bash
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 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.
## Full snapshot surveys
Download and verify the complete pinned snapshot:
```bash
pnpm test:test262:fetch
```
Pass the resulting directory as `--root` and use `--filter` to survey an API family. `--root` also accepts a checkout or extracted archive whose contents match the pinned snapshot. The runner verifies all test, fixture, and upstream harness bytes before selection. `--list` inventories variants and exclusion reasons without compiling. `--limit` selects the first N matching files; `--workers` controls concurrent compiles and otherwise follows `SCRIPTC_TEST_WORKERS`, defaulting to two. `SCRIPTC_TEST_SHARD=i/n` partitions variants using the existing stable case sharder.
`--report` selects a JSON report path; the default is `node_modules/.cache/scriptc-test262/report.json`. `--journal` writes each completed result to a new JSONL file during execution, preserving evidence if a long survey is interrupted; it refuses to overwrite an existing file. Reports retain the upstream revision, profile, adapter hash, compiler package version, backend, sanitizer setting, host, selection counts, feature metadata, exclusions, and per-variant outcomes. Pass counts describe the selected adapted profile and must not be presented as whole-suite ECMAScript conformance. Compiler refusals, runtime refusals, harness refusals, assertion failures, build errors, crashes, and timeouts are separate outcomes. Exclusions are runner capabilities, not scriptc implementation classifications.
`--compile-timeout` and `--runtime-timeout` bound each compiler and native process in milliseconds. The defaults are 120 seconds and 10 seconds. Standard input is closed, output is bounded, and a completion marker prevents an early successful exit from counting as a pass. `--keep` retains generated sources, compile diagnostics, and binaries for investigation; ordinary runs remove temporary artifacts. Sanitizer diagnostics cannot satisfy a known-refusal expectation.
## 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 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`, and `Test262Error`. Scalar SameValue uses the statically compiled `node:assert/strict` implementation. Contract tests compare it with the original Test262 helpers under Node using cases for NaN, signed zero, null/undefined, differing primitive types, failed assertions, 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. Assertion aliases, mutation, reflection, additional assertion methods, and all `includes` helpers are excluded until explicitly implemented and tested.
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.
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.
+23
View File
@@ -0,0 +1,23 @@
// A separate process lets the runner bound compiler hangs and crashes as well
// as program execution. It imports the current compiler source, like Vitest.
import { readFileSync, writeFileSync } from "node:fs";
import { compile } from "../../packages/compiler/src/index.js";
async function main(): Promise<void> {
const request = JSON.parse(readFileSync(process.argv[2]!, "utf8"));
const result = await compile(request.entry, {
outDir: request.workDir,
outPath: request.binary,
dynamic: false,
sanitize: request.sanitize,
...(request.backend === "default" ? {} : { backend: request.backend }),
});
writeFileSync(request.result, JSON.stringify(result.ok
? { ok: true, backend: result.backend, llvmRefusal: result.llvmRefusal }
: { ok: false, diagnostics: result.diagnostics }));
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
+126
View File
@@ -0,0 +1,126 @@
import { spawn } from "node:child_process";
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { compileFailureStatus, completion, harnessSource, prepare } from "./support.mjs";
export interface Outcome {
status: string;
reason?: string;
phase?: string;
backend?: string;
llvmRefusal?: string;
stdout?: string;
stderr?: string;
diagnostics?: unknown[];
workDir?: string;
}
interface ProcessResult {
code: number | null;
signal: string | null;
timeout: boolean;
overflow: boolean;
stdout: string;
stderr: string;
}
export function boundedRun(command: string, args: string[], timeoutMs: number): Promise<ProcessResult> {
return new Promise((resolve, reject) => {
const grouped = process.platform !== "win32";
const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"], detached: grouped });
let stdout = "";
let stderr = "";
let timeout = false;
let overflow = false;
const stop = () => {
try {
if (grouped && child.pid) process.kill(-child.pid, "SIGKILL");
else child.kill("SIGKILL");
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ESRCH") reject(error);
}
};
const timer = setTimeout(() => { timeout = true; stop(); }, timeoutMs);
const collect = (chunk: Buffer, stream: "stdout" | "stderr") => {
if (overflow) return;
if (stream === "stdout") stdout += chunk.toString();
else stderr += chunk.toString();
if (stdout.length + stderr.length > 1024 * 1024) { overflow = true; stop(); }
};
child.stdout.on("data", (chunk) => collect(chunk, "stdout"));
child.stderr.on("data", (chunk) => collect(chunk, "stderr"));
child.on("error", (error) => { clearTimeout(timer); reject(error); });
child.on("close", (code, signal) => {
clearTimeout(timer);
resolve({ code, signal, timeout, overflow, stdout, stderr });
});
});
}
export async function runSource(source: string, options: {
backend?: "default" | "llvm" | "c";
sanitize?: boolean;
compileTimeoutMs?: number;
runtimeTimeoutMs?: number;
keep?: boolean;
} = {}): Promise<Outcome> {
// 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 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 {
writeFileSync(entry, prepare(source));
writeFileSync(join(workDir, "harness.ts"), harnessSource);
writeFileSync(requestFile, JSON.stringify({
entry, binary, workDir, result: resultFile,
backend: options.backend ?? "default", sanitize: options.sanitize ?? false,
}));
const compiled = await boundedRun(process.execPath, [
"--import", "tsx", fileURLToPath(new URL("./compile-worker.ts", import.meta.url)), requestFile,
], options.compileTimeoutMs ?? 120_000);
const finish = (outcome: Outcome): Outcome => ({ ...outcome, ...(options.keep ? { workDir } : {}) });
if (compiled.timeout) return finish({ status: "timeout", phase });
if (compiled.signal) return finish({ status: "crash", phase, reason: compiled.signal, stderr: compiled.stderr });
if (compiled.code !== 0 || compiled.overflow) {
return finish({ status: "build-error", phase, stdout: compiled.stdout, stderr: compiled.stderr, reason: compiled.signal ?? "compiler process failed" });
}
const build = JSON.parse(readFileSync(resultFile, "utf8"));
if (!build.ok) {
return finish({ status: compileFailureStatus(build.diagnostics), phase, diagnostics: build.diagnostics });
}
phase = "runtime";
const result = await boundedRun(binary, [], options.runtimeTimeoutMs ?? 10_000);
const common = { backend: build.backend, ...(build.llvmRefusal ? { llvmRefusal: build.llvmRefusal } : {}) };
if (result.timeout) return finish({ ...common, status: "timeout", phase });
if (result.signal) return finish({ ...common, status: "crash", phase, reason: result.signal, stderr: result.stderr });
const stderr = options.sanitize
? result.stderr.replace(/^==\d+==WARNING: ASan doesn't fully support makecontext\/swapcontext.*\n/gm, "")
: result.stderr;
if (result.code === 86 && stderr === "SCRIPTC_TEST262_HARNESS: reference equality requires an identity-preserving adapter\n") {
return finish({ ...common, status: "harness-refusal", phase, reason: "reference-assertion", stderr });
}
if (result.code === 0 && !result.overflow && stderr === "" && result.stdout === `${completion}\n`) {
return finish({ ...common, status: "pass" });
}
// Require the entire diagnostic, so a sanitizer report following a known
// refusal cannot be hidden by the regression expectation.
const fence = /^Uncaught Error: [^\n]+\[(SC\d{4}) at ([^\n]+):\d+\]\n$/.exec(stderr);
return finish({
...common,
status: fence ? (fence[2]!.endsWith("/harness.ts") ? "harness-refusal" : "runtime-refusal") : "fail",
phase, reason: fence?.[1] ?? `exit ${result.code}; completion marker required`,
stdout: result.stdout, stderr,
});
} catch (error) {
return { status: "runner-error", phase, reason: String(error), ...(options.keep ? { workDir } : {}) };
} finally {
if (!options.keep) await rm(workDir, { recursive: true, force: true });
}
}
+1
View File
@@ -0,0 +1 @@
{}
+29
View File
@@ -0,0 +1,29 @@
import { execFileSync } from "node:child_process";
import { existsSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { directory, pin, sha256, snapshotDigest } from "./support.mjs";
const parent = join(directory, "../../node_modules/.cache/scriptc-test262");
const destination = join(parent, pin.commit);
mkdirSync(parent, { recursive: true });
if (existsSync(destination)) {
if (snapshotDigest(destination) !== pin.snapshotSha256) throw new Error(`Modified snapshot: ${destination}`);
} else {
const temporary = mkdtempSync(join(parent, "download-"));
try {
const response = await fetch(pin.archiveUrl);
if (!response.ok) throw new Error(`Test262 download: HTTP ${response.status}`);
const bytes = Buffer.from(await response.arrayBuffer());
if (sha256(bytes) !== pin.archiveSha256) throw new Error("Test262 archive checksum mismatch");
const archive = join(temporary, "upstream.tar.gz");
const extracted = join(temporary, "source");
writeFileSync(archive, bytes);
mkdirSync(extracted);
execFileSync("tar", ["-xzf", archive, "-C", extracted, "--strip-components=1"]);
if (snapshotDigest(extracted) !== pin.snapshotSha256) throw new Error("Test262 content checksum mismatch");
renameSync(extracted, destination);
} finally {
rmSync(temporary, { recursive: true, force: true });
}
}
console.log(destination);
+42
View File
@@ -0,0 +1,42 @@
// Test262 permits host implementations of its assertion functions. This
// adapter avoids JS function expandos, which currently hit a static fence.
// Keep the supported surface small and check it against the upstream helpers.
import nodeAssert from "node:assert/strict";
export class Test262Error extends Error {}
function scalar(value: unknown): void {
// Native-to-unknown conversion can copy references (notably arrays).
// Refuse these assertions before comparing altered identities. Terminate
// instead of throwing so a test's catch block cannot turn this into a pass.
if ((typeof value === "object" && value !== null) || typeof value === "function") {
console.error("SCRIPTC_TEST262_HARNESS: reference equality requires an identity-preserving adapter");
process.exit(86);
}
}
export function assert(value: unknown, message = "Expected true"): void {
if (value !== true) throw new Test262Error(message);
}
export namespace assert {
export function sameValue(actual: unknown, expected: unknown, message = "Expected SameValue"): void {
scalar(actual);
scalar(expected);
try {
nodeAssert.strictEqual(actual, expected);
} catch {
throw new Test262Error(message);
}
}
export function notSameValue(actual: unknown, expected: unknown, message = "Expected different values"): void {
scalar(actual);
scalar(expected);
try {
nodeAssert.notStrictEqual(actual, expected);
} catch {
throw new Test262Error(message);
}
}
}
+128
View File
@@ -0,0 +1,128 @@
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { parseArgs } from "node:util";
import { shardSelect } from "../harness/shard.js";
import { runSource, type Outcome } from "./execute.js";
import {
directory, exclusion, matchesExpectation, metadata, pin, sha256, snapshotDigest, summarize,
testPaths, variants, vendorRoot, verifyVendor,
} from "./support.mjs";
async function main(): Promise<void> {
const { values } = parseArgs({ options: {
root: { type: "string" }, filter: { type: "string" }, limit: { type: "string" },
report: { type: "string" }, journal: { type: "string" }, workers: { type: "string" }, backend: { type: "string", default: "default" },
"compile-timeout": { type: "string" }, "runtime-timeout": { type: "string" },
keep: { type: "boolean" }, list: { type: "boolean" }, help: { type: "boolean" },
} });
if (values.help) {
console.log(`Static scriptc Test262 profile (dynamic engine disabled)
pnpm test:test262 run the vendored regression profile
pnpm test:test262 --root PATH survey a complete pinned snapshot
--filter TEXT select test paths containing TEXT
--limit N first N selected test files
--workers N concurrent compiles (default 2)
--backend default|llvm|c default uses scriptc's normal selection
--report PATH JSON report (default under node_modules/.cache)
--journal PATH append each result as JSONL during the run
--compile-timeout MS --runtime-timeout MS bound each compiler/program process
--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.
Only synchronous strict scripts in the documented adapted profile can pass.
All other variants remain visible as exclusions; negative errors never count as passes.`);
return;
}
function positive(value: string | undefined, fallback: number, name: string): number {
if (value === undefined) return fallback;
const n = Number(value);
if (!Number.isSafeInteger(n) || n <= 0) throw new Error(`${name} must be a positive integer`);
return n;
}
if (!["default", "c", "llvm"].includes(values.backend!)) throw new Error("invalid backend");
const backend = values.backend as "default" | "c" | "llvm";
const workers = positive(values.workers ?? process.env.SCRIPTC_TEST_WORKERS, 2, "workers");
const limit = positive(values.limit, Number.MAX_SAFE_INTEGER, "limit");
const compileTimeoutMs = positive(values["compile-timeout"], 120_000, "compile-timeout");
const runtimeTimeoutMs = positive(values["runtime-timeout"], 10_000, "runtime-timeout");
const root = values.root ? resolve(values.root) : vendorRoot;
if (values.root) {
if (snapshotDigest(root) !== pin.snapshotSha256) throw new Error("Test262 snapshot differs from the pinned revision");
} else verifyVendor();
const allPaths: string[] = values.root ? testPaths(root) : pin.tests;
const selected = allPaths.filter((path) => !values.filter || path.includes(values.filter)).slice(0, limit);
if (!selected.length) throw new Error("no Test262 files matched the selection");
const cases = selected.flatMap((path) => {
const source = readFileSync(join(root, path), "utf8");
const meta = metadata(source, path);
return variants(meta).map((variant: string) => ({
id: `${path}#${variant}`, path, variant, source, features: meta.features,
exclusion: exclusion(source, meta, variant),
}));
});
const shard = shardSelect(cases, (item) => item.id);
if (values.list) {
for (const item of shard) console.log(`${item.id}\t${item.exclusion ?? "run"}`);
return;
}
const cache = join(directory, "../../node_modules/.cache/scriptc-test262");
if (process.env.SCRIPTC_CACHE_DIR === undefined) {
process.env.SCRIPTC_CACHE_DIR = join(cache, "cas");
mkdirSync(process.env.SCRIPTC_CACHE_DIR, { recursive: true, mode: 0o700 });
}
process.env.SCRIPTC_TEST_STABLE_TOOLCHAIN ??= "1";
const results: (Outcome & { id: string; path: string; variant: string; features: string[] })[] = [];
const journalPath = values.journal ? resolve(values.journal) : undefined;
if (journalPath) {
mkdirSync(dirname(journalPath), { recursive: true });
writeFileSync(journalPath, "", { flag: "wx" });
}
let next = 0;
await Promise.all(Array.from({ length: Math.min(workers, shard.length) }, async () => {
for (;;) {
const item = shard[next++];
if (!item) return;
const outcome = item.exclusion
? { status: "excluded", reason: item.exclusion }
: await runSource(item.source, {
backend, sanitize: process.env.SCRIPTC_SAN === "1", compileTimeoutMs, runtimeTimeoutMs, keep: values.keep,
});
const result = { id: item.id, path: item.path, variant: item.variant, features: item.features, ...outcome };
if (journalPath) appendFileSync(journalPath, `${JSON.stringify(result)}\n`);
results.push(result);
if (outcome.status !== "excluded") console.log(`${outcome.status}\t${item.id}`);
}
}));
results.sort((a, b) => a.id.localeCompare(b.id));
const unexpected = results.filter((r) => r.status !== "excluded" &&
(values.root ? r.status !== "pass" : !matchesExpectation(r.id, r)));
const report = {
schema: "scriptc.test262.v1",
revision: pin.commit, snapshotSha256: pin.snapshotSha256,
profile: "static-strict-scalar-adapter-v1",
harnessSha256: sha256(readFileSync(join(directory, "harness.ts"))),
dynamic: false, requestedBackend: backend, sanitize: process.env.SCRIPTC_SAN === "1",
host: `${process.platform}-${process.arch}`, node: process.version,
compilerVersion: JSON.parse(readFileSync(join(directory, "../../packages/compiler/package.json"), "utf8")).version,
scope: values.root ? "survey" : "regression-profile",
upstreamTestFiles: pin.testFiles, availableTestFiles: allPaths.length,
selectedTestFiles: selected.length, selectedVariants: cases.length,
shard: process.env.SCRIPTC_TEST_SHARD ?? null, shardVariants: shard.length,
unexpectedResults: unexpected.length,
...summarize(results), results,
};
const reportPath = resolve(values.report ?? join(cache, "report.json"));
mkdirSync(dirname(reportPath), { recursive: true });
writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
console.log(JSON.stringify(report.counts));
console.log(`Report: ${reportPath}`);
// Exclusions are visible but not failures. An all-excluded local selection
// must not turn green; an empty distributed shard is legitimate.
if (unexpected.length ||
(!process.env.SCRIPTC_TEST_SHARD && !results.some((r) => r.status === "pass"))) process.exitCode = 1;
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});
+148
View File
@@ -0,0 +1,148 @@
import { createHash } from "node:crypto";
import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { load, JSON_SCHEMA } from "js-yaml";
import ts from "typescript";
export const directory = fileURLToPath(new URL(".", import.meta.url));
export const vendorRoot = join(directory, "vendor");
export const pin = JSON.parse(readFileSync(join(directory, "upstream.json"), "utf8"));
export const completion = "__scriptc_test262_complete__";
export const harnessSource = readFileSync(join(directory, "harness.ts"), "utf8");
export const expectations = JSON.parse(readFileSync(join(directory, "expectations.json"), "utf8"));
export const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
export function filesBelow(root, prefix) {
const paths = [];
for (const entry of readdirSync(join(root, prefix), { withFileTypes: true })) {
const path = `${prefix}/${entry.name}`;
if (entry.isDirectory()) paths.push(...filesBelow(root, path));
else if (entry.isFile()) paths.push(path);
else throw new Error(`Test262 snapshot contains a non-regular entry: ${path}`);
}
return paths.sort();
}
// Covers fixtures and harness files too; a checkout with edited tests cannot
// silently claim the pinned revision. Paths are POSIX on every host.
export function snapshotDigest(root) {
const paths = ["LICENSE", ...filesBelow(root, "harness"), ...filesBelow(root, "test")].sort();
const hash = createHash("sha256");
for (const path of paths) {
hash.update(path).update("\0").update(sha256(readFileSync(join(root, path)))).update("\n");
}
return hash.digest("hex");
}
export function verifyVendor() {
for (const [path, expected] of Object.entries(pin.files)) {
if (sha256(readFileSync(join(vendorRoot, path))) !== expected) {
throw new Error(`Pinned Test262 file changed: ${path}`);
}
}
}
export function testPaths(root) {
return filesBelow(root, "test").filter((path) => path.endsWith(".js") && !path.includes("_FIXTURE"));
}
const knownFlags = new Set([
"onlyStrict", "noStrict", "module", "raw", "async", "generated",
"CanBlockIsFalse", "CanBlockIsTrue", "non-deterministic",
]);
export function metadata(source, path = "test.js") {
const match = /\/\*---([\s\S]*?)---\*\//.exec(source);
if (!match) throw new Error(`${path}: missing Test262 frontmatter`);
const value = load(match[1], { schema: JSON_SCHEMA });
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path}: invalid metadata`);
for (const key of ["flags", "includes", "features"]) {
value[key] ??= [];
if (!Array.isArray(value[key]) || value[key].some((item) => typeof item !== "string")) {
throw new Error(`${path}: invalid ${key}`);
}
}
for (const flag of value.flags) {
if (!knownFlags.has(flag)) throw new Error(`${path}: unknown flag ${flag}`);
}
if (value.flags.includes("onlyStrict") && value.flags.includes("noStrict")) {
throw new Error(`${path}: conflicting execution flags`);
}
if (value.negative && (!['parse', 'resolution', 'runtime'].includes(value.negative.phase) || typeof value.negative.type !== "string")) {
throw new Error(`${path}: invalid negative metadata`);
}
return value;
}
export function variants(meta) {
if (meta.flags.includes("module")) return ["module"];
if (meta.flags.includes("raw")) return ["raw"];
if (meta.flags.includes("onlyStrict")) return ["strict"];
if (meta.flags.includes("noStrict")) return ["sloppy"];
return ["sloppy", "strict"];
}
// 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.
export function exclusion(source, meta, variant) {
if (variant !== "strict") return `execution:${variant}`;
if (meta.negative) return `negative-phase:${meta.negative.phase}`;
if (meta.flags.includes("async")) return "execution:async";
if (meta.flags.some((flag) => flag.startsWith("CanBlock"))) return "host:agents";
if (meta.includes.length) return `harness-includes:${meta.includes.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 visit = (node) => {
if (reason) 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)) {
reason = `host:${node.text}`;
} else if (ts.isIdentifier(node) && node.text === "assert") {
const parent = node.parent;
if (ts.isCallExpression(parent) && parent.expression === node) {
// assert(value, message)
} else if (ts.isPropertyAccessExpression(parent) && parent.expression === node &&
["sameValue", "notSameValue"].includes(parent.name.text) &&
ts.isCallExpression(parent.parent) && parent.parent.expression === parent) {
// Supported assertion calls; aliases, mutations, and reflection stay out.
} else reason = "harness:assert-surface";
} else if (ts.isIdentifier(node) && node.text === "Test262Error") {
if (!ts.isNewExpression(node.parent) || node.parent.expression !== node) reason = "harness:Test262Error-surface";
}
ts.forEachChild(node, visit);
};
visit(sf);
return reason;
}
export function prepare(source) {
return `"use strict";\nimport { assert, Test262Error } from "./harness.ts";\n${source}\n;console.log(${JSON.stringify(completion)});\n`;
}
export function summarize(results) {
const counts = {};
const exclusions = {};
for (const result of results) {
counts[result.status] = (counts[result.status] ?? 0) + 1;
if (result.status === "excluded") exclusions[result.reason] = (exclusions[result.reason] ?? 0) + 1;
}
return { counts, exclusions };
}
export function compileFailureStatus(diagnostics) {
if (diagnostics.some((d) => d.code === "SC0004" || /^SC9/.test(d.code))) return "compiler-error";
if (diagnostics.some((d) => d.code === "SC3003" || d.code === "SC3004")) return "build-error";
if (diagnostics.some((d) => d.loc?.file?.endsWith("harness.ts"))) return "harness-refusal";
return "compile-refusal";
}
export function matchesExpectation(id, outcome) {
const expected = expectations[id];
if (!expected) return outcome.status === "pass";
return outcome.status === expected.status && outcome.reason === expected.reason &&
outcome.stderr?.includes(expected.detail) === true;
}
+123
View File
@@ -0,0 +1,123 @@
{
"repository": "https://github.com/tc39/test262",
"commit": "7ab7fafa0003f73fc85c1b95d88094d33f7eb8bd",
"archiveUrl": "https://codeload.github.com/tc39/test262/tar.gz/7ab7fafa0003f73fc85c1b95d88094d33f7eb8bd",
"archiveSha256": "1d497a1e7430094a41d06f38db775df4a63db5d587b2a8b08aba6fad5de19585",
"snapshotSha256": "584d260eaa9aef0c42d18b6b03e8b3755ecd54bdf43e01701f5b3eca3043c4c3",
"testFiles": 53597,
"tests": [
"test/built-ins/Array/prototype/indexOf/15.4.4.14-4-1.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-13.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-14.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-2.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-33.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-4.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-6.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-7.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-8.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-9.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-6-1.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-1.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-2.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-4.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-5.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-1.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-2.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-3.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-4.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-9-9.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-9-b-ii-1.js",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-9-b-ii-4.js",
"test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js",
"test/built-ins/BigInt/prototype/toString/default-radix.js",
"test/built-ins/Math/abs/S15.8.2.1_A1.js",
"test/built-ins/Math/abs/S15.8.2.1_A2.js",
"test/built-ins/Math/abs/S15.8.2.1_A3.js",
"test/built-ins/Math/abs/absolute-value.js",
"test/built-ins/Math/floor/S15.8.2.9_A1.js",
"test/built-ins/Math/floor/S15.8.2.9_A3.js",
"test/built-ins/Number/isNaN/nan.js",
"test/built-ins/RegExp/prototype/test/S15.10.6.3_A1_T16.js",
"test/built-ins/String/prototype/charAt/pos-rounding.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",
"test/language/expressions/addition/S11.6.1_A4_T4.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/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",
"test/language/statements/for/dstr/var-ary-ptrn-elem-obj-id-init.js",
"test/language/statements/let/dstr/ary-ptrn-elem-ary-elem-init.js",
"test/language/statements/let/dstr/ary-ptrn-elem-id-init-exhausted.js",
"test/language/statements/let/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js",
"test/language/statements/let/dstr/ary-ptrn-elem-obj-id-init.js",
"test/language/statements/variable/dstr/ary-ptrn-elem-ary-elem-init.js",
"test/language/statements/variable/dstr/ary-ptrn-elem-id-init-exhausted.js",
"test/language/statements/variable/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js",
"test/language/statements/variable/dstr/ary-ptrn-elem-obj-id-init.js",
"test/staging/sm/JSON/stringify-missing-arguments.js"
],
"files": {
"LICENSE": "4dd9244dfe8197c75348c4b24ab53d29d3b1cfad143ac76b5a3d8942aa354ce0",
"harness/assert.js": "206e274ca325eb8a652e3911c3fbd090e2480d11ed7579dc17a5d17a2360ed48",
"harness/sta.js": "1930c54af79455c484799f43e9a28e2b2f15c40d0917c9941ca54e26db243f35",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-4-1.js": "ef762d69cbdf878821a6f998064590c2c19aa9e3db297968a5db02a6f5cd455a",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-13.js": "3c1ebd36e0e4657c42abbb76a707546a7b46ad954d3e569a84b796695d10da02",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-14.js": "7bb87d45a34bffcc61b39131c30289d6da7b51cd6b2a3db4af38674864fbd15e",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-2.js": "108f7c64202aafa4887a43273c110bdf49bc55e17d3dabfea5be5828ccda87db",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-33.js": "6af320abe37ad74d27a358deeb9a64080912463d64394f64096ba5f0c178a435",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-4.js": "62a81860fc75c3fd53c37d9d09b0d1f5ad8e52a91bbe4c0e7a8ab657090c12a6",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-6.js": "abb1c387bdd9181020f0e0ff87035a9e9a043d5c2d98074f31d5ac064084297f",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-7.js": "323f212b590eade7be9f1bbca00bdc5e00b675efeb4cf4af1b1c58f62c669739",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-8.js": "696f1dcc2323f98d9862f712d72620c00165400ea0868c2d9a82df24ae356bc9",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-5-9.js": "d4f37afdd9429bd844b86cd341202a48f4cdb8948472fdde52f73bb3cde5dc00",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-6-1.js": "282bc00bd45d61988e18b1b5584e2a9fc49b2fb3769510562495d663ba110ebe",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-1.js": "b0129cb88aca9bff611fcb4d0a80429cfb82b0fcd0aab4fd22bc74e55564ba3e",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-2.js": "3170de9144830fafc2c765bc1bf1679f663b7e872c449237b8b3f39ea3587af6",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-4.js": "7a67b842618bf2e11cfa5f18d1dfcd2651a45927bc25371f1c5fdb0848f9c70d",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-7-5.js": "94131da040feb7216a9719f0cf5d26a2e5d768e8334c7f91924ff4c459664150",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-1.js": "e31fede57f8709f99cb7aab0ca5016267408689f50f38ea806955636b7335814",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-2.js": "c0b598189a0b3317d9235a069f3ff95f34ed1d471aba81f03fff7eddc9f76f3d",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-3.js": "d1a958b18cd19eaa8b87ab628f2e01f6a52c9d8306b9c9a43e77e13470490e6a",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-8-4.js": "65ecb449938a5fb8dd583006728f87772a61bfb665107a6d302c9db3550d1126",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-9-9.js": "ed7fa6ec5662502ac72f53ceb0cbc0d2e87718cc2d6405c55598c0090b8e658f",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-9-b-ii-1.js": "db248a7b40b4285a0331fa7eaee0a78e6a9af4e57294c9ce9e89fe392845ca2e",
"test/built-ins/Array/prototype/indexOf/15.4.4.14-9-b-ii-4.js": "07b115c3b90dfb5bcb9e38990fd1db57720c4ac423e052082828ed8ccb6b4214",
"test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js": "ababfd2ad12b4099f9d2108ba6e58ff33feb358522f3bbd4ddde5144cbf0bc7f",
"test/built-ins/BigInt/prototype/toString/default-radix.js": "d901ef7e3fab5b8c528cb60a12c80145a6c5b7fbd02f829fe657259eddbe711a",
"test/built-ins/Math/abs/S15.8.2.1_A1.js": "c5b508cdc6130c8670e8ac6d822ca93e5dbead7566543cb65b793602b7203d7c",
"test/built-ins/Math/abs/S15.8.2.1_A2.js": "a625e117f7858319c2ff25b959539266a6f29473925bb47ad5d8bbfe88e7b31e",
"test/built-ins/Math/abs/S15.8.2.1_A3.js": "1c42eb7d52a6004f76aa26c1acde5e28a95ed5d56252766fdf4b1379f2c08f6e",
"test/built-ins/Math/abs/absolute-value.js": "3db02f9a237e589409d426e7ae4f36f7146c81374927d1bba2ccab7f953ac0f8",
"test/built-ins/Math/floor/S15.8.2.9_A1.js": "b7f8668c0b8b8533043a6e54e469c3dc6a6ac93593dcad8a2cbc813cbeb366a8",
"test/built-ins/Math/floor/S15.8.2.9_A3.js": "895ceeb2d4ae505d08e9246888c2e4a4526779d9edac16320ebf629330f56929",
"test/built-ins/Number/isNaN/nan.js": "d513a909b333f40762bc08a9e2d1435d4ca097ed8b6da76497d0201a7264589e",
"test/built-ins/RegExp/prototype/test/S15.10.6.3_A1_T16.js": "c96f0402289d55e41a2e0ef322060c5a143b599f456b94dc5ebe0493bc689f42",
"test/built-ins/String/prototype/charAt/pos-rounding.js": "09f5e3a97acd6adefd6aaea782a0f09487c3d95935014e0727a8b3b0bd8609b2",
"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",
"test/language/expressions/addition/S11.6.1_A4_T4.js": "88afbecfcbb69c52a7ecc79c4b2dda351fffc5b75453b5b787a7f97afe8f4229",
"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/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",
"test/language/statements/for/dstr/var-ary-ptrn-elem-obj-id-init.js": "8171c2af775fd53eb51e088bdccb79353e9f35bb06ba0ba44339792c5b39dd07",
"test/language/statements/let/dstr/ary-ptrn-elem-ary-elem-init.js": "c1a0b3dc1d0cbedafa439ec9a1c0caabb3ddf350a24aeb880551438b0ba2f971",
"test/language/statements/let/dstr/ary-ptrn-elem-id-init-exhausted.js": "23fcda62dd2929425d0b5a07550a324500ba9e4bf2f077b976afcc2720f77c47",
"test/language/statements/let/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js": "29f0cda5bb499b41f6ceba77cb98063bea79415d006ee01e6bd9e5e975303103",
"test/language/statements/let/dstr/ary-ptrn-elem-obj-id-init.js": "eaa8c6110ee9844b664de6c6843fab0b4b1e677d8825e76dc516a5e685ef07ec",
"test/language/statements/variable/dstr/ary-ptrn-elem-ary-elem-init.js": "f87720eef9f3697de48d6a7feaf5dcd872dfa907e6a41b610f02ee95775ad257",
"test/language/statements/variable/dstr/ary-ptrn-elem-id-init-exhausted.js": "9b93cfddea8593d0b9130d96c7ee7d89f5efc1b3831d7d7aadc0fc503c4fd94c",
"test/language/statements/variable/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js": "f5f4a7260fd8768d5d5d28934ba866ae2854b36570c4b71c8e9a25fb6a077a63",
"test/language/statements/variable/dstr/ary-ptrn-elem-obj-id-init.js": "618ac6762b875a851d13b1721bb842a4ba2eb6ace8c391464031f53a8eeecb86",
"test/staging/sm/JSON/stringify-missing-arguments.js": "99f0f10e69930bda84a593483a9158b7a4e79cc70ab7c58a89ac891e77360e44"
}
}
+28
View File
@@ -0,0 +1,28 @@
Test262: ECMAScript Test Suite ("Software") is protected by copyright and is being
made available under the "BSD License", included below. This Software may be subject to third party rights (rights
from parties other than Ecma International), including patent rights, and no licenses under such third party rights
are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA
CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://www.ecma-international.org/ipr FOR
INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS*.
Copyright (C) 2012 Ecma International
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
* Ecma International Standards hereafter means Ecma International Standards as well as Ecma Technical Reports
+184
View File
@@ -0,0 +1,184 @@
// Copyright (C) 2017 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
Collection of assertion functions used throughout test262
defines:
- assert
- compareArray
- formatIdentityFreeValue
- formatSimpleValue
- isNegativeZero
- isPrimitive
---*/
function isNegativeZero(value) {
return value === 0 && 1 / value === -Infinity;
}
function isPrimitive(value) {
return !value || (typeof value !== 'object' && typeof value !== 'function');
}
function formatIdentityFreeValue(value) {
switch (value === null ? 'null' : typeof value) {
case 'string':
return typeof JSON !== "undefined" ? JSON.stringify(value) : '"' + value + '"';
case 'bigint':
return String(value) + "n";
case 'number':
if (isNegativeZero(value)) return '-0';
// falls through
case 'boolean':
case 'undefined':
case 'null':
return String(value);
}
}
function formatSimpleValue(value) {
var basic = formatIdentityFreeValue(value);
if (basic) return basic;
try {
return String(value);
} catch (err) {
if (err.name === 'TypeError') {
return Object.prototype.toString.call(value);
}
throw err;
}
}
function assert(mustBeTrue, message) {
if (mustBeTrue === true) {
return;
}
if (message === undefined) {
message = 'Expected true but got ' + assert._toString(mustBeTrue);
}
throw new Test262Error(message);
}
assert._isSameValue = function (a, b) {
if (a === b) {
// Handle +/-0 vs. -/+0
return a !== 0 || 1 / a === 1 / b;
}
// Handle NaN vs. NaN
return a !== a && b !== b;
};
assert.sameValue = function (actual, expected, message) {
try {
if (assert._isSameValue(actual, expected)) {
return;
}
} catch (error) {
throw new Test262Error(message + ' (_isSameValue operation threw) ' + error);
return;
}
if (message === undefined) {
message = '';
} else {
message += ' ';
}
message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(expected) + '») to be true';
throw new Test262Error(message);
};
assert.notSameValue = function (actual, unexpected, message) {
if (!assert._isSameValue(actual, unexpected)) {
return;
}
if (message === undefined) {
message = '';
} else {
message += ' ';
}
message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(unexpected) + '») to be false';
throw new Test262Error(message);
};
assert.throws = function (expectedErrorConstructor, func, message) {
var expectedName, actualName;
if (typeof func !== "function") {
throw new Test262Error('assert.throws requires two arguments: the error constructor ' +
'and a function to run');
return;
}
if (message === undefined) {
message = '';
} else {
message += ' ';
}
try {
func();
} catch (thrown) {
if (typeof thrown !== 'object' || thrown === null) {
message += 'Thrown value was not an object!';
throw new Test262Error(message);
} else if (thrown.constructor !== expectedErrorConstructor) {
expectedName = expectedErrorConstructor.name;
actualName = thrown.constructor.name;
if (expectedName === actualName) {
message += 'Expected a ' + expectedName + ' but got a different error constructor with the same name';
} else {
message += 'Expected a ' + expectedName + ' but got a ' + actualName;
}
throw new Test262Error(message);
}
return;
}
message += 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all';
throw new Test262Error(message);
};
assert.compareArray = function (actual, expected, message) {
message = message === undefined ? '' : message;
if (typeof message === 'symbol') {
message = message.toString();
}
if (isPrimitive(actual)) {
assert(false, "Actual argument [" + actual + "] shouldn't be primitive. " + String(message));
} else if (isPrimitive(expected)) {
assert(false, "Expected argument [" + expected + "] shouldn't be primitive. " + String(message));
}
var result = compareArray(actual, expected);
if (result) return;
var format = compareArray.format;
assert(false, "Actual " + format(actual) + " and expected " + format(expected) + " should have the same contents. " + String(message));
};
function compareArray(a, b) {
if (b.length !== a.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!assert._isSameValue(b[i], a[i])) {
return false;
}
}
return true;
}
compareArray.format = function (arrayLike) {
return "[" + Array.prototype.map.call(arrayLike, String).join(", ") + "]";
};
assert._formatIdentityFreeValue = formatIdentityFreeValue;
assert._toString = formatSimpleValue;
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
Provides both:
- An error class to avoid false positives when testing for thrown exceptions
- A function to explicitly throw an exception using the Test262Error class
defines: [Test262Error, $DONOTEVALUATE]
---*/
function Test262Error(message) {
if (!(this instanceof Test262Error)) return new Test262Error(message);
this.message = message || "";
}
Test262Error.prototype.toString = function () {
return "Test262Error: " + this.message;
};
Test262Error.thrower = function (message) {
throw new Test262Error(message);
};
function $DONOTEVALUATE() {
throw "Test262: This statement should not be evaluated.";
}
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf returns -1 if 'length' is 0 (empty array)
---*/
var i = [].indexOf(42);
assert.sameValue(i, -1, 'i');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf - value of 'fromIndex' is a number (value
is -Infinity)
---*/
assert.sameValue([true].indexOf(true, -Infinity), 0, '[true].indexOf(true, -Infinity)');
@@ -0,0 +1,12 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf - value of 'fromIndex' is a number (value
is NaN)
---*/
assert.sameValue([true].indexOf(true, NaN), 0, '[true].indexOf(true, NaN)');
assert.sameValue([true].indexOf(true, -NaN), 0, '[true].indexOf(true, -NaN)');
@@ -0,0 +1,13 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf when fromIndex is floating point number
---*/
var a = new Array(1, 2, 3);
assert.sameValue(a.indexOf(3, 0.49), 2, '0.49 resolves to 0');
assert.sameValue(a.indexOf(1, 0.51), 0, '0.51 resolves to 0');
assert.sameValue(a.indexOf(1, 1.51), -1, '1.51 resolves to 1');
@@ -0,0 +1,15 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf match on the first element, a middle
element and the last element when 'fromIndex' is passed
---*/
assert.sameValue([0, 1, 2, 3, 4].indexOf(0, 0), 0, '[0, 1, 2, 3, 4].indexOf(0, 0)');
assert.sameValue([0, 1, 2, 3, 4].indexOf(2, 1), 2, '[0, 1, 2, 3, 4].indexOf(2, 1)');
assert.sameValue([0, 1, 2, 3, 4].indexOf(2, 2), 2, '[0, 1, 2, 3, 4].indexOf(2, 2)');
assert.sameValue([0, 1, 2, 3, 4].indexOf(4, 2), 4, '[0, 1, 2, 3, 4].indexOf(4, 2)');
assert.sameValue([0, 1, 2, 3, 4].indexOf(4, 4), 4, '[0, 1, 2, 3, 4].indexOf(4, 4)');
@@ -0,0 +1,12 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf returns 0 if fromIndex is 'undefined'
---*/
var a = [1, 2, 3];
// undefined resolves to 0
assert.sameValue(a.indexOf(1, undefined), 0, 'a.indexOf(1,undefined)');
@@ -0,0 +1,14 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf - 'fromIndex' isn't passed
---*/
var arr = [0, 1, 2, 3, 4];
//'fromIndex' will be set as 0 if not passed by default
assert.sameValue(arr.indexOf(0), arr.indexOf(0, 0), 'arr.indexOf(0)');
assert.sameValue(arr.indexOf(2), arr.indexOf(2, 0), 'arr.indexOf(2)');
assert.sameValue(arr.indexOf(4), arr.indexOf(4, 0), 'arr.indexOf(4)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf - value of 'fromIndex' is a number (value
is 0)
---*/
assert.sameValue([true].indexOf(true, 0), 0, '[true].indexOf(true, 0)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf - value of 'fromIndex' is a number (value
is +0)
---*/
assert.sameValue([true].indexOf(true, +0), 0, '[true].indexOf(true, +0)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf - value of 'fromIndex' is a number (value
is -0)
---*/
assert.sameValue([true].indexOf(true, -0), 0, '[true].indexOf(true, -0)');
@@ -0,0 +1,15 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf returns -1 if fromIndex is greater than
Array length
---*/
var a = [1, 2, 3];
assert.sameValue(a.indexOf(1, 5), -1, 'a.indexOf(1,5)');
assert.sameValue(a.indexOf(1, 3), -1, 'a.indexOf(1,3)');
assert.sameValue([].indexOf(1, 0), -1, '[ ].indexOf(1,0)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf returns -1 when 'fromIndex' is length of
array - 1
---*/
assert.sameValue([1, 2, 3].indexOf(1, 2), -1, '[1, 2, 3].indexOf(1, 2)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf returns correct index when 'fromIndex' is
length of array - 1
---*/
assert.sameValue([1, 2, 3].indexOf(3, 2), 2, '[1, 2, 3].indexOf(3, 2)');
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf returns -1 when 'fromIndex' is 1
---*/
assert.sameValue([1, 2, 3].indexOf(1, 1), -1, '[1, 2, 3].indexOf(1, 1)');
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf returns correct index when 'fromIndex' is 1
---*/
assert.sameValue([1, 2, 3].indexOf(2, 1), 1, '[1, 2, 3].indexOf(2, 1)');
@@ -0,0 +1,15 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf with negative fromIndex
---*/
var a = new Array(1, 2, 3);
assert.sameValue(a.indexOf(2, -1), -1, 'a.indexOf(2,-1)');
assert.sameValue(a.indexOf(2, -2), 1, 'a.indexOf(2,-2)');
assert.sameValue(a.indexOf(1, -3), 0, 'a.indexOf(1,-3)');
assert.sameValue(a.indexOf(1, -5.3), 0, 'a.indexOf(1,-5.3)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf returns correct index when 'fromIndex' is
-1
---*/
assert.sameValue([1, 2, 3, 4].indexOf(4, -1), 3, '[1, 2, 3, 4].indexOf(4, -1)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf returns -1 when abs('fromIndex') is length
of array - 1
---*/
assert.sameValue([1, 2, 3, 4].indexOf(1, -3), -1, '[1, 2, 3, 4].indexOf(1, -3)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf returns -1 when abs('fromIndex') is length
of array
---*/
assert.sameValue([1, 2, 3, 4].indexOf(0, -4), -1, '[1, 2, 3, 4].indexOf(0, -4)');
@@ -0,0 +1,20 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf must return correct index (Sparse Array)
---*/
var a = new Array(0, 1);
a[4294967294] = 2; // 2^32-2 - is max array element
a[4294967295] = 3; // 2^32-1 added as non-array element property
a[4294967296] = 4; // 2^32 added as non-array element property
a[4294967297] = 5; // 2^32+1 added as non-array element property
// start searching near the end so in case implementation actually tries to test all missing elements!!
assert.sameValue(a.indexOf(2, 4294967290), 4294967294, 'a.indexOf(2,4294967290 )');
assert.sameValue(a.indexOf(3, 4294967290), -1, 'a.indexOf(3,4294967290)');
assert.sameValue(a.indexOf(4, 4294967290), -1, 'a.indexOf(4,4294967290)');
assert.sameValue(a.indexOf(5, 4294967290), -1, 'a.indexOf(5,4294967290)');
@@ -0,0 +1,18 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Array.prototype.indexOf - type of array element is different from
type of search element
---*/
assert.sameValue(["true"].indexOf(true), -1, '["true"].indexOf(true)');
assert.sameValue(["0"].indexOf(0), -1, '["0"].indexOf(0)');
assert.sameValue([false].indexOf(0), -1, '[false].indexOf(0)');
assert.sameValue([undefined].indexOf(0), -1, '[undefined].indexOf(0)');
assert.sameValue([null].indexOf(0), -1, '[null].indexOf(0)');
assert.sameValue([
[]
].indexOf(0), -1, '[[]].indexOf(0)');
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: Array.prototype.indexOf - search element is NaN
---*/
assert.sameValue([+NaN, NaN, -NaN].indexOf(NaN), -1, '[+NaN, NaN, -NaN].indexOf(NaN)');
@@ -0,0 +1,16 @@
// Copyright 2016 The V8 project authors. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/*---
esid: sec-array.prototype.indexof
description: >
Return +0 when fromIndex is -0 and return index refers to the first position
info: |
22.1.3.12 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
...
6. If n ≥ 0, then
a. If n is -0, let k be +0; else let k be n.
...
---*/
assert.sameValue(1 / [true].indexOf(true, -0), +Infinity)
@@ -0,0 +1,19 @@
// Copyright 2018 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-bigint.prototype.tostring
description: toString with default radix
features: [BigInt]
---*/
assert.sameValue((-100n).toString(), "-100", "(-100n).toString() === '-100'");
assert.sameValue((0n).toString(), "0", "(0n).toString() === '0'");
assert.sameValue((100n).toString(), "100", "(100n).toString() === '100'");
assert.sameValue((-100n).toString(undefined), "-100",
"(-100n).toString(undefined) === '-100'");
assert.sameValue((0n).toString(undefined), "0",
"(0n).toString(undefined) === '0'");
assert.sameValue((100n).toString(undefined), "100",
"(100n).toString(undefined) === '100'");
@@ -0,0 +1,10 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: If x is NaN, Math.abs(x) is NaN
es5id: 15.8.2.1_A1
description: Checking if Math.abs(NaN) is NaN
---*/
assert.sameValue(Math.abs(NaN), NaN);
@@ -0,0 +1,10 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: If x is -0, Math.abs(x) is +0
es5id: 15.8.2.1_A2
description: Checking if Math.abs(-0) equals to +0
---*/
assert.sameValue(Math.abs(-0), 0);
@@ -0,0 +1,12 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: If x is -Infinity, Math.abs(x) is +Infinity
es5id: 15.8.2.1_A3
description: Checking if Math.abs(-Infinity) equals to +Infinity
---*/
// CHECK#1
var x = -Infinity;
assert.sameValue(Math.abs(x), +Infinity, 'Math.abs(-Infinity) must return +Infinity');
@@ -0,0 +1,22 @@
// Copyright (C) 2016 The V8 Project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-math.abs
description: >
Returns the absolute value of x
info: |
Math.abs ( x )
Returns the absolute value of x; the result has the same magnitude as x but
has positive sign.
---*/
assert.sameValue(Math.abs(-42), 42, "-42");
assert.sameValue(Math.abs(42), 42, "42");
assert.sameValue(Math.abs(-0.000001), 0.000001, "-0.000001");
assert.sameValue(Math.abs(0.000001), 0.000001, "0.000001");
assert.sameValue(Math.abs(-1e-17), 1e-17, "-1e-17");
assert.sameValue(Math.abs(1e-17), 1e-17, "1e-17");
assert.sameValue(Math.abs(-9007199254740991), 9007199254740991, "-(2**53-1)");
assert.sameValue(Math.abs(9007199254740991), 9007199254740991, "2**53-1");
@@ -0,0 +1,10 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: If x is NaN, Math.floor(x) is NaN
es5id: 15.8.2.9_A1
description: Checking if Math.floor(NaN) is NaN
---*/
assert.sameValue(Math.floor(NaN), NaN);
@@ -0,0 +1,10 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: If x is -0, Math.floor(x) is -0
es5id: 15.8.2.9_A3
description: Checking if Math.floor(x) is -0, where x is -0
---*/
assert.sameValue(Math.floor(-0), -0);
+10
View File
@@ -0,0 +1,10 @@
// Copyright (c) 2014 Ryan Lewis. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es6id: 20.1.2.4
author: Ryan Lewis
description: Number.IsNaN should return true if called with NaN.
---*/
assert.sameValue(Number.isNaN(NaN), true, 'Number.isNaN(NaN)');
@@ -0,0 +1,12 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: Equivalent to the expression RegExp.prototype.exec(string) != null
es5id: 15.10.6.3_A1_T16
description: RegExp is /undefined/ and call test() without arguments
---*/
var __re = /undefined/;
assert.sameValue(__re.test(), __re.exec() !== null, '__re.test() must return __re.exec() !== null');
@@ -0,0 +1,25 @@
// Copyright (C) 2016 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-string.prototype.charat
description: Rounding of the provided "pos" number
info: |
[...]
3. Let position be ? ToInteger(pos).
[...]
7.1.4 ToInteger
1. Let number be ? ToNumber(argument).
2. If number is NaN, return +0.
3. If number is +0, -0, +∞, or -∞, return number.
4. Return the number value that is the same sign as number and whose
magnitude is floor(abs(number)).
---*/
assert.sameValue('abc'.charAt(-0.99999), 'a', '-0.99999');
assert.sameValue('abc'.charAt(-0.00001), 'a', '-0.00001');
assert.sameValue('abc'.charAt(0.00001), 'a', '0.00001');
assert.sameValue('abc'.charAt(0.99999), 'a', '0.99999');
assert.sameValue('abc'.charAt(1.00001), 'b', '1.00001');
assert.sameValue('abc'.charAt(1.99999), 'b', '1.99999');
@@ -0,0 +1,40 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: |
The result of an addition is determined using the rules of IEEE 754
double-precision arithmetics
es5id: 11.6.1_A4_T1
description: If either operand is NaN, the result is NaN
---*/
//CHECK#1
if (isNaN(Number.NaN + 1) !== true ) {
throw new Test262Error('#1: NaN + 1 === Not-a-Number. Actual: ' + (NaN + 1));
}
//CHECK#2
if (isNaN(1 + Number.NaN) !== true ) {
throw new Test262Error('#2: 1 + NaN === Not-a-Number. Actual: ' + (1 + NaN));
}
//CHECK#3
if (isNaN(Number.NaN + Number.POSITIVE_INFINITY) !== true ) {
throw new Test262Error('#3: NaN + Infinity === Not-a-Number. Actual: ' + (NaN + Infinity));
}
//CHECK#4
if (isNaN(Number.POSITIVE_INFINITY + Number.NaN) !== true ) {
throw new Test262Error('#4: Infinity + NaN === Not-a-Number. Actual: ' + (Infinity + NaN));
}
//CHECK#5
if (isNaN(Number.NaN + Number.NEGATIVE_INFINITY) !== true ) {
throw new Test262Error('#5: NaN + Infinity === Not-a-Number. Actual: ' + (NaN + Infinity));
}
//CHECK#6
if (isNaN(Number.NEGATIVE_INFINITY + Number.NaN) !== true ) {
throw new Test262Error('#6: Infinity + NaN === Not-a-Number. Actual: ' + (Infinity + NaN));
}
@@ -0,0 +1,20 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: |
The result of an addition is determined using the rules of IEEE 754
double-precision arithmetics
es5id: 11.6.1_A4_T2
description: The sum of two infinities of opposite sign is NaN
---*/
//CHECK#1
if (isNaN(Number.POSITIVE_INFINITY + Number.NEGATIVE_INFINITY) !== true ) {
throw new Test262Error('#1: Infinity + -Infinity === Not-a-Number. Actual: ' + (Infinity + -Infinity));
}
//CHECK#2
if (isNaN(Number.NEGATIVE_INFINITY + Number.POSITIVE_INFINITY) !== true ) {
throw new Test262Error('#2: -Infinity + Infinity === Not-a-Number. Actual: ' + (-Infinity + Infinity));
}
@@ -0,0 +1,22 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: |
The result of an addition is determined using the rules of IEEE 754
double-precision arithmetics
es5id: 11.6.1_A4_T3
description: >
The sum of two infinities of the same sign is the infinity of that
sign
---*/
//CHECK#1
if (Number.POSITIVE_INFINITY + Number.POSITIVE_INFINITY !== Number.POSITIVE_INFINITY ) {
throw new Test262Error('#1: Infinity + Infinity === Infinity. Actual: ' + (Infinity + Infinity));
}
//CHECK#2
if (Number.NEGATIVE_INFINITY + Number.NEGATIVE_INFINITY !== Number.NEGATIVE_INFINITY ) {
throw new Test262Error('#2: -Infinity + -Infinity === -Infinity. Actual: ' + (-Infinity + -Infinity));
}
@@ -0,0 +1,52 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: |
The result of an addition is determined using the rules of IEEE 754
double-precision arithmetics
es5id: 11.6.1_A4_T4
description: >
The sum of an infinity and a finite value is equal to the infinite
operand
---*/
//CHECK#1
if (Number.POSITIVE_INFINITY + 1 !== Number.POSITIVE_INFINITY ) {
throw new Test262Error('#1: Infinity + 1 === Infinity. Actual: ' + (Infinity + 1));
}
//CHECK#2
if (-1 + Number.POSITIVE_INFINITY !== Number.POSITIVE_INFINITY ) {
throw new Test262Error('#2: -1 + Infinity === Infinity. Actual: ' + (-1 + Infinity));
}
//CHECK#3
if (Number.NEGATIVE_INFINITY + 1 !== Number.NEGATIVE_INFINITY ) {
throw new Test262Error('#3: -Infinity + 1 === -Infinity. Actual: ' + (-Infinity + 1));
}
//CHECK#4
if (-1 + Number.NEGATIVE_INFINITY !== Number.NEGATIVE_INFINITY ) {
throw new Test262Error('#4: -1 + -Infinity === -Infinity. Actual: ' + (-1 + -Infinity));
}
//CHECK#5
if (Number.POSITIVE_INFINITY + Number.MAX_VALUE !== Number.POSITIVE_INFINITY ) {
throw new Test262Error('#5: Infinity + Number.MAX_VALUE === Infinity. Actual: ' + (Infinity + Number.MAX_VALUE));
}
//CHECK#6
if (-Number.MAX_VALUE + Number.POSITIVE_INFINITY !== Number.POSITIVE_INFINITY ) {
throw new Test262Error('#6: -Number.MAX_VALUE + Infinity === Infinity. Actual: ' + (-Number.MAX_VALUE + Infinity));
}
//CHECK#7
if (Number.NEGATIVE_INFINITY + Number.MAX_VALUE !== Number.NEGATIVE_INFINITY ) {
throw new Test262Error('#7: -Infinity + Number.MAX_VALUE === -Infinity. Actual: ' + (-Infinity + Number.MAX_VALUE));
}
//CHECK#8
if (-Number.MAX_VALUE + Number.NEGATIVE_INFINITY !== Number.NEGATIVE_INFINITY ) {
throw new Test262Error('#8: -Number.MAX_VALUE + -Infinity === -Infinity. Actual: ' + (-Number.MAX_VALUE + -Infinity));
}
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-ary-elem-init.case
// - src/dstr-binding/default/const-stmt.template
/*---
description: BindingElement with array binding pattern and initializer is used (`const` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
const [[x, y, z] = [4, 5, 6]] = [];
assert.sameValue(x, 4);
assert.sameValue(y, 5);
assert.sameValue(z, 6);
@@ -0,0 +1,35 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-exhausted.case
// - src/dstr-binding/default/const-stmt.template
/*---
description: Destructuring initializer with an exhausted iterator (`const` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
5. If iteratorRecord.[[done]] is true, let v be undefined.
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
const [x = 23] = [];
assert.sameValue(x, 23);
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-fn-name-arrow.case
// - src/dstr-binding/default/const-stmt.template
/*---
description: SingleNameBinding does assign name to arrow functions (`const` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
c. ReturnIfAbrupt(v).
d. If IsAnonymousFunctionDefinition(Initializer) is true, then
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
const [arrow = () => {}] = [];
assert.sameValue(arrow.name, 'arrow');
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-obj-id-init.case
// - src/dstr-binding/default/const-stmt.template
/*---
description: BindingElement with object binding pattern and initializer is used (`const` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
const [{ x, y, z } = { x: 44, y: 55, z: 66 }] = [];
assert.sameValue(x, 44);
assert.sameValue(y, 55);
assert.sameValue(z, 66);
@@ -0,0 +1,54 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-ary-elem-init.case
// - src/dstr-binding/default/for-var.template
/*---
description: BindingElement with array binding pattern and initializer is used (for statement)
esid: sec-for-statement-runtime-semantics-labelledevaluation
features: [destructuring-binding]
flags: [generated]
info: |
IterationStatement :
for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement
1. Let varDcl be the result of evaluating VariableDeclarationList.
[...]
13.3.2.4 Runtime Semantics: Evaluation
VariableDeclarationList : VariableDeclarationList , VariableDeclaration
1. Let next be the result of evaluating VariableDeclarationList.
2. ReturnIfAbrupt(next).
3. Return the result of evaluating VariableDeclaration.
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for BindingPattern
passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
var iterCount = 0;
for (var [[x, y, z] = [4, 5, 6]] = []; iterCount < 1; ) {
assert.sameValue(x, 4);
assert.sameValue(y, 5);
assert.sameValue(z, 6);
iterCount += 1;
}
assert.sameValue(iterCount, 1, 'Iteration occurred as expected');
@@ -0,0 +1,53 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-exhausted.case
// - src/dstr-binding/default/for-var.template
/*---
description: Destructuring initializer with an exhausted iterator (for statement)
esid: sec-for-statement-runtime-semantics-labelledevaluation
features: [destructuring-binding]
flags: [generated]
info: |
IterationStatement :
for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement
1. Let varDcl be the result of evaluating VariableDeclarationList.
[...]
13.3.2.4 Runtime Semantics: Evaluation
VariableDeclarationList : VariableDeclarationList , VariableDeclaration
1. Let next be the result of evaluating VariableDeclarationList.
2. ReturnIfAbrupt(next).
3. Return the result of evaluating VariableDeclaration.
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for BindingPattern
passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
5. If iteratorRecord.[[done]] is true, let v be undefined.
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
var iterCount = 0;
for (var [x = 23] = []; iterCount < 1; ) {
assert.sameValue(x, 23);
iterCount += 1;
}
assert.sameValue(iterCount, 1, 'Iteration occurred as expected');
@@ -0,0 +1,54 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-fn-name-arrow.case
// - src/dstr-binding/default/for-var.template
/*---
description: SingleNameBinding does assign name to arrow functions (for statement)
esid: sec-for-statement-runtime-semantics-labelledevaluation
features: [destructuring-binding]
flags: [generated]
info: |
IterationStatement :
for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement
1. Let varDcl be the result of evaluating VariableDeclarationList.
[...]
13.3.2.4 Runtime Semantics: Evaluation
VariableDeclarationList : VariableDeclarationList , VariableDeclaration
1. Let next be the result of evaluating VariableDeclarationList.
2. ReturnIfAbrupt(next).
3. Return the result of evaluating VariableDeclaration.
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for BindingPattern
passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
c. ReturnIfAbrupt(v).
d. If IsAnonymousFunctionDefinition(Initializer) is true, then
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
var iterCount = 0;
for (var [arrow = () => {}] = []; iterCount < 1; ) {
assert.sameValue(arrow.name, 'arrow');
iterCount += 1;
}
assert.sameValue(iterCount, 1, 'Iteration occurred as expected');
@@ -0,0 +1,54 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-obj-id-init.case
// - src/dstr-binding/default/for-var.template
/*---
description: BindingElement with object binding pattern and initializer is used (for statement)
esid: sec-for-statement-runtime-semantics-labelledevaluation
features: [destructuring-binding]
flags: [generated]
info: |
IterationStatement :
for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement
1. Let varDcl be the result of evaluating VariableDeclarationList.
[...]
13.3.2.4 Runtime Semantics: Evaluation
VariableDeclarationList : VariableDeclarationList , VariableDeclaration
1. Let next be the result of evaluating VariableDeclarationList.
2. ReturnIfAbrupt(next).
3. Return the result of evaluating VariableDeclaration.
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for BindingPattern
passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
var iterCount = 0;
for (var [{ x, y, z } = { x: 44, y: 55, z: 66 }] = []; iterCount < 1; ) {
assert.sameValue(x, 44);
assert.sameValue(y, 55);
assert.sameValue(z, 66);
iterCount += 1;
}
assert.sameValue(iterCount, 1, 'Iteration occurred as expected');
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-ary-elem-init.case
// - src/dstr-binding/default/let-stmt.template
/*---
description: BindingElement with array binding pattern and initializer is used (`let` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
let [[x, y, z] = [4, 5, 6]] = [];
assert.sameValue(x, 4);
assert.sameValue(y, 5);
assert.sameValue(z, 6);
@@ -0,0 +1,35 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-exhausted.case
// - src/dstr-binding/default/let-stmt.template
/*---
description: Destructuring initializer with an exhausted iterator (`let` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
5. If iteratorRecord.[[done]] is true, let v be undefined.
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
let [x = 23] = [];
assert.sameValue(x, 23);
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-fn-name-arrow.case
// - src/dstr-binding/default/let-stmt.template
/*---
description: SingleNameBinding does assign name to arrow functions (`let` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
c. ReturnIfAbrupt(v).
d. If IsAnonymousFunctionDefinition(Initializer) is true, then
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
let [arrow = () => {}] = [];
assert.sameValue(arrow.name, 'arrow');
@@ -0,0 +1,36 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-obj-id-init.case
// - src/dstr-binding/default/let-stmt.template
/*---
description: BindingElement with object binding pattern and initializer is used (`let` statement)
esid: sec-let-and-const-declarations-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
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.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
let [{ x, y, z } = { x: 44, y: 55, z: 66 }] = [];
assert.sameValue(x, 44);
assert.sameValue(y, 55);
assert.sameValue(z, 66);
@@ -0,0 +1,35 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-ary-elem-init.case
// - src/dstr-binding/default/var-stmt.template
/*---
description: BindingElement with array binding pattern and initializer is used (`var` statement)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
var [[x, y, z] = [4, 5, 6]] = [];
assert.sameValue(x, 4);
assert.sameValue(y, 5);
assert.sameValue(z, 6);
@@ -0,0 +1,34 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-exhausted.case
// - src/dstr-binding/default/var-stmt.template
/*---
description: Destructuring initializer with an exhausted iterator (`var` statement)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
5. If iteratorRecord.[[done]] is true, let v be undefined.
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
var [x = 23] = [];
assert.sameValue(x, 23);
@@ -0,0 +1,35 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-id-init-fn-name-arrow.case
// - src/dstr-binding/default/var-stmt.template
/*---
description: SingleNameBinding does assign name to arrow functions (`var` statement)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
SingleNameBinding : BindingIdentifier Initializeropt
[...]
6. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be GetValue(defaultValue).
c. ReturnIfAbrupt(v).
d. If IsAnonymousFunctionDefinition(Initializer) is true, then
[...]
7. If environment is undefined, return PutValue(lhs, v).
8. Return InitializeReferencedBinding(lhs, v).
---*/
var [arrow = () => {}] = [];
assert.sameValue(arrow.name, 'arrow');
@@ -0,0 +1,35 @@
// This file was procedurally generated from the following sources:
// - src/dstr-binding/ary-ptrn-elem-obj-id-init.case
// - src/dstr-binding/default/var-stmt.template
/*---
description: BindingElement with object binding pattern and initializer is used (`var` statement)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [destructuring-binding]
flags: [generated]
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
13.3.3.6 Runtime Semantics: IteratorBindingInitialization
BindingElement : BindingPatternInitializer opt
[...]
2. If iteratorRecord.[[done]] is true, let v be undefined.
3. If Initializer is present and v is undefined, then
a. Let defaultValue be the result of evaluating Initializer.
b. Let v be ? GetValue(defaultValue).
4. Return the result of performing BindingInitialization of BindingPattern
with v and environment as the arguments.
---*/
var [{ x, y, z } = { x: 44, y: 55, z: 66 }] = [];
assert.sameValue(x, 44);
assert.sameValue(y, 55);
assert.sameValue(z, 66);
@@ -0,0 +1,13 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
/*---
description: |
JSON.stringify with no arguments
info: bugzilla.mozilla.org/show_bug.cgi?id=648471
esid: pending
---*/
assert.sameValue(JSON.stringify(), undefined);