mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Skip untaken global-detection arms, and fix the shim test checks
- An if statement whose condition is a global-detection test (static-typeof.ts) lowers only its taken arm, unless the untaken arm declares a var or function binding. Code Node never runs no longer counts as a deferred site, so global-detection-typeof.cjs is fully static. - The resolver parity sweep skips specifiers the package-shim registry serves: they resolve to the built-in module by design. - undici-shim-types.test.ts joins the TypeScript 5 import allowlist. - node-types-timers uses a 200 ms timer, so the immediate runs first on a loaded machine as well.
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
storedMatchAllRowsReadByElements,
|
||||
} from "./containers/match-row-uses.js";
|
||||
import { dynUndefinedExpr, nodeThrowExpr, numLit, varRef } from "../../ir/build.js";
|
||||
import { declaresHoistedBinding, staticCondition } from "./static-typeof.js";
|
||||
import { lowerCollectionSpread } from "./containers/collection-methods.js";
|
||||
import { lowerEventObjectPropertyWrite } from "./lower-event-target.js";
|
||||
import { lowerDynObjectLiteral } from "./expressions/object-literals.js";
|
||||
@@ -1501,6 +1502,20 @@ export function lowerStmt(lowerer: Lowerer, stmt: ts.Statement): IrStmt | IrStmt
|
||||
}
|
||||
if (ts.isIfStatement(stmt)) {
|
||||
const cond = lowerer.lowerCondition(stmt.expression);
|
||||
// A global-detection condition (static-typeof.ts) folds; its untaken
|
||||
// arm is code Node never runs and does not lower, unless it declares a
|
||||
// hoisted binding.
|
||||
const decided = staticCondition(lowerer, stmt.expression);
|
||||
if (decided !== null) {
|
||||
const taken = decided ? stmt.thenStatement : stmt.elseStatement;
|
||||
const untaken = decided ? stmt.elseStatement : stmt.thenStatement;
|
||||
if (untaken === undefined || !declaresHoistedBinding(untaken)) {
|
||||
const arm = taken === undefined ? [] : lowerer.lowerScopedBlock(taken);
|
||||
return decided
|
||||
? { kind: "if", cond, then: arm, else_: null, loc: locOf(stmt) }
|
||||
: { kind: "if", cond, then: [], else_: arm, loc: locOf(stmt) };
|
||||
}
|
||||
}
|
||||
// Aliased-typeof narrows (ms's `var type = typeof val` guard): the
|
||||
// then-branch lowers under what a TRUE condition proves, the
|
||||
// else-branch under a FALSE one — the var/let alias narrowing the
|
||||
|
||||
@@ -98,3 +98,28 @@ export function staticallySelected(lowerer: Lowerer, expr: ts.Expression): ts.Ex
|
||||
e = taken ? e.whenTrue : e.whenFalse;
|
||||
}
|
||||
}
|
||||
|
||||
/** True when `stmt` declares a binding that is visible outside it: a `var`
|
||||
* or a function declaration, outside nested functions. Such an arm still
|
||||
* lowers when its condition is decided, for the hoisted binding. */
|
||||
export function declaresHoistedBinding(stmt: ts.Statement): boolean {
|
||||
let found = false;
|
||||
const visit = (n: ts.Node): void => {
|
||||
if (found) return;
|
||||
if (ts.isFunctionDeclaration(n)) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
if (ts.isFunctionLike(n) || ts.isClassDeclaration(n) || ts.isClassExpression(n)) return;
|
||||
if (
|
||||
ts.isVariableDeclarationList(n) &&
|
||||
(n.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) === 0
|
||||
) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
n.forEachChild(visit);
|
||||
};
|
||||
visit(stmt);
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
resolveTypeDirective,
|
||||
setProjectPathMappings,
|
||||
} from "../../src/frontend/resolve.js";
|
||||
import { packageShimFor } from "../../src/frontend/package-shims.js";
|
||||
import { isRelativeSpecifier } from "../../src/frontend/workspace-registry.js";
|
||||
import { options5 } from "./harness.js";
|
||||
|
||||
@@ -138,6 +139,10 @@ test("relative and bare specifiers across the whole test tree resolve identicall
|
||||
// the resolver follows Node — corpus 2124 pins the behavior
|
||||
// differentially, so parity has nothing to check here.
|
||||
if (spec.startsWith("#/")) continue;
|
||||
// A package scriptc implements itself (package-shims.ts) resolves
|
||||
// to its built-in module by design; 5.9.3 answers the installed
|
||||
// package.
|
||||
if (packageShimFor(file, spec) !== null) continue;
|
||||
bare++;
|
||||
// PROJECT imports (#alias / self-name): ts5's NON-external answers
|
||||
// for bare specifiers are exactly the package.json-mediated
|
||||
|
||||
@@ -30,6 +30,7 @@ const ALLOWED_TYPESCRIPT5_IMPORTS = new Set([
|
||||
"packages/compiler/src/library/semantic-source.ts",
|
||||
"packages/compiler/test/ts7/harness.ts",
|
||||
"packages/compiler/test/ts7/resolver-parity.test.ts",
|
||||
"packages/compiler/test/undici-shim-types.test.ts",
|
||||
]);
|
||||
|
||||
function sourceFilesUnder(dir) {
|
||||
|
||||
@@ -52,8 +52,9 @@ const program = `class Holder {
|
||||
timer: NodeJS.Timeout | null = null;
|
||||
}
|
||||
const holder = new Holder();
|
||||
holder.timer = setTimeout(() => console.log("never"), 50);
|
||||
const kept = setTimeout(() => console.log("fired", holder.timer === null), 10);
|
||||
holder.timer = setTimeout(() => console.log("never"), 1000);
|
||||
// Long enough that the immediate runs first even on a loaded machine.
|
||||
const kept = setTimeout(() => console.log("fired", holder.timer === null), 200);
|
||||
kept.unref().ref();
|
||||
kept.refresh();
|
||||
console.log("hasRef", kept.hasRef());
|
||||
|
||||
Reference in New Issue
Block a user