mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-30 07:14:39 +08:00
Merge main into perf/static-immutability
This commit is contained in:
@@ -400,6 +400,74 @@ export function checkedNarrowCall(
|
||||
};
|
||||
}
|
||||
|
||||
/** A checked extraction found a value arm other than the one the checker
|
||||
* promised (a narrowing the program invalidated before the read). */
|
||||
export const OTHER_ARM_FAILURE_MESSAGE =
|
||||
"a value of another type is not representable in the target union (a value narrowed or asserted past it still held it)";
|
||||
|
||||
/** checkedNarrowCall's in-place form with the TypeError of the consuming
|
||||
* operation: `messageFor` names Node's error for finding another arm
|
||||
* (`o.f is not a function`), or null for the stranded-value message. Null
|
||||
* when the operand cannot be evaluated more than once.
|
||||
*
|
||||
* The failure path stays small for wide unions: arms are grouped by their
|
||||
* message (every non-nullish arm without its own shares one), so it tests
|
||||
* at most the null and undefined arms before a final throw. */
|
||||
export function checkedArmExtraction(
|
||||
lowerer: Lowerer,
|
||||
value: IrExpr,
|
||||
tag: number,
|
||||
arm: IrType,
|
||||
messageFor: (found: IrType) => string | null,
|
||||
): IrExpr | null {
|
||||
if (value.type.kind !== "union" || !isSafeToRepeat(value)) return null;
|
||||
const unionId = value.type.unionId;
|
||||
const from = lowerer.unions.get(unionId);
|
||||
const stored = from?.arms[tag];
|
||||
if (!from || from.arms.length < 2 || !stored || !typeEquals(stored, arm)) return null;
|
||||
const loc = value.loc;
|
||||
const isTag = (t: number): IrExpr => ({
|
||||
kind: "unionIsTag",
|
||||
unionId,
|
||||
tag: t,
|
||||
negated: false,
|
||||
value,
|
||||
type: BOOL,
|
||||
loc,
|
||||
});
|
||||
const groups = new Map<string, number[]>();
|
||||
from.arms.forEach((found, t) => {
|
||||
if (t === tag) return;
|
||||
const message =
|
||||
messageFor(found) ??
|
||||
(isUnitType(found)
|
||||
? unionNarrowFailureMessage(found, (type) => lowerer.fmt(type))
|
||||
: OTHER_ARM_FAILURE_MESSAGE);
|
||||
const tags = groups.get(message);
|
||||
if (tags) tags.push(t);
|
||||
else groups.set(message, [t]);
|
||||
});
|
||||
// The largest group needs no test of its own: it is whatever remains.
|
||||
const ordered = [...groups].sort((a, b) => a[1].length - b[1].length);
|
||||
const [lastMessage] = ordered.pop()!;
|
||||
let failure: IrExpr = nodeThrowExpr(1, "", lastMessage, arm, loc);
|
||||
for (const [message, tags] of ordered.reverse()) {
|
||||
let cond = isTag(tags[0]!);
|
||||
for (const t of tags.slice(1))
|
||||
cond = { kind: "logical", op: "||", left: cond, right: isTag(t), type: BOOL, loc };
|
||||
const thrown = nodeThrowExpr(1, "", message, arm, loc);
|
||||
failure = { kind: "ternary", cond, then: thrown, else_: failure, type: arm, loc };
|
||||
}
|
||||
return {
|
||||
kind: "ternary",
|
||||
cond: isTag(tag),
|
||||
then: { kind: "unionNarrow", unionId, tag, value, type: arm, loc },
|
||||
else_: failure,
|
||||
type: arm,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
/** The operand of a checked extraction in either form checkedNarrowCall
|
||||
* produces, or null for every other expression. */
|
||||
export function checkedNarrowOperand(lowerer: Lowerer, value: IrExpr): IrExpr | null {
|
||||
|
||||
@@ -509,10 +509,13 @@ export function finishOptionalChain(
|
||||
return { kind: "optChain", id, receiver, body, type: DYN, loc };
|
||||
}
|
||||
let type = lowerer.irTypeOf(expr);
|
||||
// The checker can call a receiver present that holds a nullish value at
|
||||
// runtime (runtime-optional reads; a stale narrowing restored to its
|
||||
// declared type). Either nullish arm short-circuits to undefined.
|
||||
const hiddenOptionalResult =
|
||||
(type.kind !== "union" || lowerer.armTag(type.unionId, UNDEFINED_T) < 0) &&
|
||||
receiver.type.kind === "union" &&
|
||||
lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0 &&
|
||||
(lowerer.unions.get(receiver.type.unionId)?.arms.some(isUnitType) ?? false) &&
|
||||
body.type.kind !== "generator" &&
|
||||
body.type.kind !== "jsval"
|
||||
? lowerer.withUndefinedArmOf(body.type)
|
||||
|
||||
@@ -176,6 +176,7 @@ import {
|
||||
completeArgs,
|
||||
} from "./call-arguments.js";
|
||||
import { isThisParameter, type ParamShape } from "./call-signatures.js";
|
||||
import { checkedArmExtraction } from "./coercions/unions.js";
|
||||
import {
|
||||
requireObjLitGenericReceiver,
|
||||
objLitGenericFnNodeOf,
|
||||
@@ -8996,8 +8997,19 @@ export function lowerObjectMethodCall(
|
||||
});
|
||||
let callee = target ? lowerer.fieldGetExpr(target, locOf(access), access) : null;
|
||||
if (callee?.type.kind === "union" && callableType !== null) {
|
||||
const helper = lowerer.narrowedArmHelper(callee.type.unionId, callableType, locOf(access));
|
||||
if (helper !== null) {
|
||||
// Calling a cleared callback field throws Node's call TypeError.
|
||||
const inPlace = checkedArmExtraction(
|
||||
lowerer,
|
||||
callee,
|
||||
lowerer.armTag(callee.type.unionId, callableType),
|
||||
callableType,
|
||||
() => `${access.getText()} is not a function`,
|
||||
);
|
||||
const helper = inPlace
|
||||
? null
|
||||
: lowerer.narrowedArmHelper(callee.type.unionId, callableType, locOf(access));
|
||||
if (inPlace) callee = inPlace;
|
||||
else if (helper !== null) {
|
||||
callee = lowerer.checkedNarrowCall(helper, callee, callableType, locOf(access));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { dirname } from "node:path";
|
||||
import * as posix from "node:path/posix";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
import { checkedClassAssertion, checkedClassUnionAssertion } from "./class-assertions.js";
|
||||
import { checkedArmExtraction } from "./coercions/unions.js";
|
||||
import { narrowClassUnion, narrowStoredClassValue } from "./class-unions.js";
|
||||
import { lowerUnionFieldRead, lowerUnionFieldWrite } from "./expressions/union-fields.js";
|
||||
import { captureContextArguments } from "./function-context.js";
|
||||
@@ -1073,6 +1074,13 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr {
|
||||
// tests, so only side-effect-free reads compose — and when every arm
|
||||
// agrees the whole expression folds to that one string (dropping only
|
||||
// the pure read, the trust-the-checker bet lowerUnitComparison makes).
|
||||
// An effectful producer (a getter) evaluates once into a hidden local.
|
||||
const prelude: IrStmt[] = [];
|
||||
if (operand.type.kind === "union" && !isSafeToRepeat(operand)) {
|
||||
const local = lowerer.declareHiddenLocal("%typeofOperand", operand.type);
|
||||
prelude.push({ kind: "varDecl", localId: local.id, init: operand, loc });
|
||||
operand = varRef(local.id, operand.type, loc);
|
||||
}
|
||||
if (operand.type.kind === "union" && isSafeToRepeat(operand)) {
|
||||
const def = lowerer.unions.get(operand.type.unionId);
|
||||
const answers = def?.arms.map(typeofAnswer);
|
||||
@@ -1123,7 +1131,9 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr {
|
||||
loc,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
return prelude.length > 0
|
||||
? { kind: "seqExpr", stmts: prelude, result, type: STRING, loc }
|
||||
: result;
|
||||
}
|
||||
}
|
||||
if (operand.type.kind === "dyn") {
|
||||
@@ -3662,6 +3672,10 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx
|
||||
narrowed.className !== expr.type.className &&
|
||||
lowerer.isSubclassOf(narrowed.className, expr.type.className)
|
||||
) {
|
||||
if (lowerer.narrowing.untrusted(node, narrowed)) {
|
||||
const checked = checkedClassAssertion(lowerer, expr, narrowed, expr.loc);
|
||||
if (checked) return checked;
|
||||
}
|
||||
return { kind: "downcast", value: expr, type: narrowed, loc: expr.loc };
|
||||
}
|
||||
return expr;
|
||||
@@ -3708,6 +3722,8 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx
|
||||
const arrayTags = lowerer.arrayValueTags(expr.type.unionId);
|
||||
if (arrayTags.length === 1) {
|
||||
const arm = def!.arms[arrayTags[0]!]!;
|
||||
const checked = checkedStaleArm(lowerer, node, expr, arrayTags[0]!, arm);
|
||||
if (checked) return checked;
|
||||
return {
|
||||
kind: "unionNarrow",
|
||||
unionId: expr.type.unionId,
|
||||
@@ -3745,6 +3761,14 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx
|
||||
const values = literalValues(lowerer.checker.getTypeOfSymbol(property));
|
||||
const arm = values && literalUnionArm(def, values, (id) => lowerer.shapes.get(id));
|
||||
if (arm) {
|
||||
const checked = checkedStaleArm(
|
||||
lowerer,
|
||||
node,
|
||||
expr,
|
||||
lowerer.armTag(expr.type.unionId, arm),
|
||||
arm,
|
||||
);
|
||||
if (checked) return checked;
|
||||
return {
|
||||
kind: "unionNarrow",
|
||||
unionId: expr.type.unionId,
|
||||
@@ -3779,6 +3803,10 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx
|
||||
) &&
|
||||
lowerer.isSubclassOf(narrowed.className, valueType.className)
|
||||
) {
|
||||
const checked = checkedStaleArm(lowerer, node, expr, valueTag, valueType, narrowed);
|
||||
if (checked) {
|
||||
return checkedClassAssertion(lowerer, checked, narrowed, expr.loc) ?? checked;
|
||||
}
|
||||
return {
|
||||
kind: "downcast",
|
||||
value: {
|
||||
@@ -3795,6 +3823,8 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
const checked = checkedStaleArm(lowerer, node, expr, tag, narrowed);
|
||||
if (checked) return checked;
|
||||
return {
|
||||
kind: "unionNarrow",
|
||||
unionId: expr.type.unionId,
|
||||
@@ -3805,6 +3835,66 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx
|
||||
};
|
||||
}
|
||||
|
||||
/** The checked form of maybeNarrow's arm extraction for a narrowing the
|
||||
* checker may hold past code that replaced the value (a call clearing the
|
||||
* property; see NarrowingStability). Every other arm throws the TypeError
|
||||
* Node raises for the operation consuming the read (`Cannot read
|
||||
* properties of undefined (reading 'x')`, `o.f is not a function`), or the
|
||||
* stranded-value TypeError where Node would carry the value on. Null when
|
||||
* the narrowing is provably current. `narrowed` is the checker's type when
|
||||
* it refines the arm (a subclass view). */
|
||||
function checkedStaleArm(
|
||||
lowerer: Lowerer,
|
||||
node: ts.Node,
|
||||
value: IrExpr,
|
||||
tag: number,
|
||||
arm: IrType,
|
||||
narrowed: IrType = arm,
|
||||
): IrExpr | null {
|
||||
if (value.type.kind !== "union" || tag < 0) return null;
|
||||
if (!lowerer.narrowing.untrusted(node, narrowed)) return null;
|
||||
const inPlace = checkedArmExtraction(lowerer, value, tag, arm, (found) =>
|
||||
staleReadMessage(node, found),
|
||||
);
|
||||
if (inPlace) return inPlace;
|
||||
const helper = lowerer.narrowedArmHelper(value.type.unionId, arm, value.loc);
|
||||
return helper ? lowerer.checkedNarrowCall(helper, value, arm, value.loc) : null;
|
||||
}
|
||||
|
||||
/** Node's TypeError for the operation consuming `node` when it holds a
|
||||
* value of type `found`, or null where Node would not throw. */
|
||||
function staleReadMessage(node: ts.Node, found: IrType): string | null {
|
||||
let child: ts.Node = node;
|
||||
let parent = node.parent;
|
||||
while (ts.isParenthesizedExpression(parent)) {
|
||||
child = parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (ts.isCallExpression(parent) && parent.expression === child && !parent.questionDotToken) {
|
||||
return found.kind === "func" ? null : `${node.getText()} is not a function`;
|
||||
}
|
||||
const unit = found.kind === "undefinedT" ? "undefined" : found.kind === "nullT" ? "null" : null;
|
||||
if (unit === null) return null;
|
||||
if (
|
||||
!(ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) ||
|
||||
parent.expression !== child ||
|
||||
parent.questionDotToken
|
||||
)
|
||||
return null;
|
||||
let key: string | null = null;
|
||||
if (ts.isPropertyAccessExpression(parent)) key = parent.name.text;
|
||||
else if (ts.isStringLiteral(parent.argumentExpression)) key = parent.argumentExpression.text;
|
||||
else if (ts.isNumericLiteral(parent.argumentExpression))
|
||||
key = String(Number(parent.argumentExpression.text));
|
||||
if (key === null) return null;
|
||||
const store = parent.parent;
|
||||
return ts.isBinaryExpression(store) &&
|
||||
store.left === parent &&
|
||||
store.operatorToken.kind === ts.SyntaxKind.EqualsToken
|
||||
? `Cannot set properties of ${unit} (setting '${key}')`
|
||||
: `Cannot read properties of ${unit} (reading '${key}')`;
|
||||
}
|
||||
|
||||
/** A runtime-optional slot can be assigned while a truthy guard is in
|
||||
* force. TypeScript then types a direct member receiver as the plain arm,
|
||||
* even though an OOB-safe assignment may have restored undefined. Check
|
||||
|
||||
@@ -71,6 +71,7 @@ import {
|
||||
import { everyExprChild, everyStmtChild, transformStmtList } from "../../ir/traverse.js";
|
||||
import { RuntimeOptionalProvenance } from "./runtime-optional-provenance.js";
|
||||
import { presentAtUse, reassignedBindings } from "./runtime-optional-guards.js";
|
||||
import { NarrowingStability } from "./narrowing-stability.js";
|
||||
import { ArrayElementStates } from "./runtime-optional-elements.js";
|
||||
import { indexReadInBounds } from "./runtime-optional-bounds.js";
|
||||
import { ArrayOwnership } from "./runtime-optional-ownership.js";
|
||||
@@ -1883,6 +1884,10 @@ export class Lowerer {
|
||||
* discriminant was captured. A case match narrows the captured value,
|
||||
* not that mutable binding; preserve its tagged representation on reads. */
|
||||
readonly unstableSwitchBindings = new Set<ts.Symbol>();
|
||||
/** Whether a checker narrowing of a mutable member or closure-assigned
|
||||
* binding still holds at its use (calls do not reset TypeScript's
|
||||
* narrowing). Stale ones read the declared storage or check it. */
|
||||
readonly narrowing = new NarrowingStability(this);
|
||||
/** All storage slots widened for runtime absence, including slots whose
|
||||
* current control-flow branch has temporarily narrowed the value. */
|
||||
readonly runtimeOptionalStorageLocals = new Set<IrLocal>();
|
||||
@@ -5690,6 +5695,7 @@ export class Lowerer {
|
||||
|
||||
run(): LowerResult {
|
||||
const parts = this.splitFiles();
|
||||
this.narrowing.setFiles(parts.map((fp) => fp.sf));
|
||||
// The coverage remainder deliberately visits every body that reachable
|
||||
// emit skipped. Those files are already phase-managed, so an ordinary
|
||||
// checker miss would stay a one-node IPC query instead of falling back
|
||||
@@ -6059,6 +6065,7 @@ export class Lowerer {
|
||||
emitReachable(extraRoots?: readonly string[]): { reachable: Set<string>; result: LowerResult } {
|
||||
this.deferredModuleInitializers = new DeferredModuleInitializers();
|
||||
const parts = this.splitFiles();
|
||||
this.narrowing.setFiles(parts.map((fp) => fp.sf));
|
||||
// Direct lowering callers do not necessarily run program preflight.
|
||||
// Establish the same managed header/top-level batch here before
|
||||
// collection, while the production path simply finds warm memos.
|
||||
@@ -7043,6 +7050,11 @@ export class Lowerer {
|
||||
// receiver-kind check downstream sees the narrowed arm.
|
||||
const narrowed = this.chainNarrowedType.get(node);
|
||||
if (narrowed) return narrowed;
|
||||
// A stale narrowing of a mutable member or closure-assigned binding in
|
||||
// an absence/type test (or a slot accepting the declared type) reads
|
||||
// as declared, so the test inspects the stored value.
|
||||
const widened = this.narrowing.widenedType(node);
|
||||
if (widened) return widened;
|
||||
const t = this.checker.getTypeAtLocation(node);
|
||||
// ALIASED-TYPEOF narrowing: inside a branch a `type === 'string'`
|
||||
// test proves (type = typeof val, both never reassigned), references
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { expect, test } from "vitest";
|
||||
import { compile, deserializeModule, validateModule } from "../../index.js";
|
||||
import type { IrModule } from "../../ir/ir.js";
|
||||
|
||||
async function lower(source: string): Promise<IrModule> {
|
||||
const dir = await mkdtemp(join(tmpdir(), "scriptc-narrowing-stability-"));
|
||||
try {
|
||||
const entry = join(dir, "main.ts"),
|
||||
output = join(dir, "main.ir.json");
|
||||
await writeFile(entry, source);
|
||||
const result = await compile(entry, { outDir: dir, outPath: output, outputKind: "ir" });
|
||||
if (!result.ok)
|
||||
throw new Error(result.diagnostics.map((item) => `${item.code}: ${item.message}`).join("\n"));
|
||||
const module = deserializeModule(await readFile(output, "utf8"));
|
||||
expect(validateModule(module)).toEqual([]);
|
||||
return module;
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
/** The TypeError messages a function's checked reads can throw. */
|
||||
function thrownMessages(module: IrModule, name: string): string[] {
|
||||
const fn = module.functions.find((f) => f.name === name || f.name.endsWith(`.${name}`));
|
||||
if (!fn) throw new Error(`no function ${name}`);
|
||||
const messages: string[] = [];
|
||||
JSON.stringify(fn.body, (_key, value: unknown) => {
|
||||
const node = value as { kind?: string; fn?: string; args?: { value?: unknown }[] };
|
||||
if (node?.kind === "libCall" && node.fn === "error.nodeThrow")
|
||||
messages.push(String(node.args?.[2]?.value));
|
||||
return value;
|
||||
});
|
||||
return messages;
|
||||
}
|
||||
|
||||
const SOURCE = `
|
||||
class Tree {
|
||||
parent: Tree | undefined;
|
||||
depth: number;
|
||||
constructor(parent: Tree | undefined) {
|
||||
this.parent = parent;
|
||||
this.depth = parent ? parent.depth + 1 : 0;
|
||||
}
|
||||
}
|
||||
class Box {
|
||||
label: string | undefined = undefined;
|
||||
clear(): void { this.label = undefined; }
|
||||
}
|
||||
function visit(t: Tree): number { return t.depth; }
|
||||
function constructionOnly(t: Tree): number {
|
||||
if (t.parent) {
|
||||
visit(t);
|
||||
return t.parent.depth;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function noCall(b: Box): number {
|
||||
if (b.label !== undefined) return b.label.length + b.label.length;
|
||||
return 0;
|
||||
}
|
||||
function libraryCalls(b: Box, xs: number[]): number {
|
||||
if (b.label !== undefined) {
|
||||
xs.push(Math.max(1, 2));
|
||||
console.log("x");
|
||||
return b.label.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function userCall(b: Box): number {
|
||||
if (b.label !== undefined) {
|
||||
visit(new Tree(undefined));
|
||||
return b.label.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function methodCall(b: Box): number {
|
||||
if (b.label !== undefined) {
|
||||
b.clear();
|
||||
return b.label.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function loopBackEdge(b: Box): number {
|
||||
let n = 0;
|
||||
if (b.label !== undefined) {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
n += b.label.length;
|
||||
b.clear();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
const b = new Box();
|
||||
b.label = "abc";
|
||||
console.log(constructionOnly(new Tree(new Tree(undefined))), noCall(b), libraryCalls(b, []),
|
||||
userCall(b), methodCall(b), loopBackEdge(b));
|
||||
`;
|
||||
|
||||
test("narrowed reads keep the unchecked extraction where nothing can change the field", async () => {
|
||||
const module = await lower(SOURCE);
|
||||
expect(thrownMessages(module, "constructionOnly")).toEqual([]);
|
||||
expect(thrownMessages(module, "noCall")).toEqual([]);
|
||||
expect(thrownMessages(module, "libraryCalls")).toEqual([]);
|
||||
});
|
||||
|
||||
test("narrowed reads after code that can clear the field check it with Node's TypeError", async () => {
|
||||
const module = await lower(SOURCE);
|
||||
const read = "Cannot read properties of undefined (reading 'length')";
|
||||
expect(thrownMessages(module, "userCall")).toContain(read);
|
||||
expect(thrownMessages(module, "methodCall")).toContain(read);
|
||||
expect(thrownMessages(module, "loopBackEdge")).toContain(read);
|
||||
});
|
||||
|
||||
test("standard-library arguments read as declared only where the lowering is generic", async () => {
|
||||
// JSON.stringify and clearInterval lower only the narrowed argument types;
|
||||
// console methods and type-parameter slots take the declared value.
|
||||
const module = await lower(`
|
||||
interface Opts {
|
||||
p?: { [k: string]: number };
|
||||
}
|
||||
function touch(): number {
|
||||
return 1;
|
||||
}
|
||||
let handle: ReturnType<typeof setInterval> | null = null;
|
||||
const stop = (): void => {
|
||||
handle = null;
|
||||
};
|
||||
function specialized(o: Opts): string {
|
||||
o.p = { a: 1 };
|
||||
touch();
|
||||
return JSON.stringify(o.p);
|
||||
}
|
||||
function clearAfterCall(): void {
|
||||
if (handle !== null) {
|
||||
touch();
|
||||
clearInterval(handle);
|
||||
}
|
||||
}
|
||||
function formatted(o: Opts, out: (Opts["p"])[]): void {
|
||||
o.p = { a: 2 };
|
||||
touch();
|
||||
console.log(o.p);
|
||||
out.push(o.p);
|
||||
}
|
||||
handle = setInterval(() => {}, 1000);
|
||||
console.log(specialized({}));
|
||||
clearAfterCall();
|
||||
stop();
|
||||
formatted({}, []);
|
||||
`);
|
||||
expect(thrownMessages(module, "formatted")).toEqual([]);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
||||
* the order-parity suite holding both lanes to identical diagnostics and
|
||||
* module order over the whole corpus. The phase-4 flip deleted the 5.9.3
|
||||
* pipeline; its recorded verdicts live on as the canary baselines
|
||||
* (test/ts7/baselines/order-parity.json), and the rationale prose the old
|
||||
* (test/ts7/baselines/order-parity/), and the rationale prose the old
|
||||
* program.ts carried was folded in here where behavior demands it.
|
||||
*
|
||||
* Differences from the retired 5.9.3 lane, each deliberate and each
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/001-hello.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/002-log-args.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/100-number-format.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1000-json-stringify-basics.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1001-json-escapes-unicode.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1002-json-parse-cast.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1003-json-parse-unions.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1004-json-parse-errors.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1005-json-nested.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1006-json-fs-config.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1007-json-rc-stress.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1008-json-null-arms.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1009-json-optional-fields.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/101-arithmetic.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1010-json-stringify-space.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1011-json-unknown-typeof.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/102-comparisons.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1020-async-basics.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1021-async-ordering.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1022-async-exceptions.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1023-async-rc-stress.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1024-async-pending-exit.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1025-async-promise-capture.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1026-throw-promise.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1027-async-return-promise.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1028-async-return-record-literals.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1029-async-eager-chains.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/103-ternary.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/104-ternary-empty-arrays.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1050-comptime-tables.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1051-comptime-strings.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1052-comptime-records.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1053-comptime-json.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1054-comptime-modules/tables.ts",
|
||||
"<repo>/tests/corpus/1054-comptime-modules/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/110-number-radix.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1100-island-eval-basics.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1101-island-marshal-unicode.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1102-island-rc-stress.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/111-record-entries.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1110-math-members.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1111-math-random.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1112-number-methods.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1113-string-methods.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1114-string-unicode-island.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1115-parse-globals.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1116-island-static-mix.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1117-typeof-static-union.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1118-object-spread-conditional.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1119-switch-union.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/112-class-descriptor-key.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1120-web-globals-encoders.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1121-infinity-number-tostring.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1122-any-captures.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1123-any-conditional-spread.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1124-union-narrowed-retag.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/113-promise-checked-exits.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/114-live-record-delete.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/115-array-compound.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/116-text-decoder-runtime.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/117-weak-cache-cycle.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/118-fs-stored-callbacks.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/119-native-patterns.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1200-regex-test-basics.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1201-regex-replace.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1202-regex-substitutions.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1203-regex-split.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1204-regex-empty-unicode.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1205-regex-rc-stress.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1206-regex-dynamic-mix.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1300-errors-basics.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1301-errors-subclass.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1302-errors-typed-catch.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1303-errors-rc-stress.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1304-errors-uncaught.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1305-errors-async-rejections.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1306-errors-runtime-instances.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1350-path-normalize-join.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1351-path-parts.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1352-path-resolve-relative.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1353-os-basics.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1354-builtin-bare-specifiers.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1355-url-parse.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1356-url-file-bridge.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1357-fsp-roundtrip.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1358-crypto-random.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1359-json-module/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1360-spawn-sync.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1361-spawn-events.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1362-spawn-timers.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1363-spawn-unhandled-error.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/1364-union-truthiness.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user