@scriptc/compiler 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +145 -9
- package/dist/backend/cc.d.ts +23 -0
- package/dist/backend/cc.js +89 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.js +2 -2
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +37 -2
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-shapes.js +3 -2
- package/dist/backend/emission/emit-shapes.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +7 -8
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +1 -0
- package/dist/backend/emission/emitter.js +123 -3
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +198 -15
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +1 -0
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +25 -0
- package/dist/diagnostics/diagnostic.js +88 -2
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lib-exports.d.ts +16 -0
- package/dist/frontend/lib-exports.js +35 -0
- package/dist/frontend/lib-exports.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +76 -6
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +37 -4
- package/dist/frontend/lowering/lower-builtins.js +240 -6
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +124 -34
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +34 -2
- package/dist/frontend/lowering/lower-classes.js +257 -34
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +25 -3
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +10 -0
- package/dist/frontend/lowering/lower-exprs.js +411 -10
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +16 -1
- package/dist/frontend/lowering/lower-inspect.js +132 -5
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +25 -4
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.js +4 -2
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +6 -5
- package/dist/frontend/lowering/lower-modules.js +56 -18
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +163 -0
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +72 -2
- package/dist/frontend/lowering/lowerer.js +339 -19
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +9 -2
- package/dist/frontend/lowering/surfaces.js +44 -7
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.d.ts +3 -0
- package/dist/frontend/npm-static-rewrite.js +677 -0
- package/dist/frontend/npm-static-rewrite.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +8 -1
- package/dist/frontend/npm-static.js +159 -18
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.js +2 -2
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.d.ts +6 -0
- package/dist/frontend/program.js +110 -22
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.js +120 -5
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/shared.d.ts +8 -2
- package/dist/frontend/shared.js +22 -6
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/ts7/checker.js +1 -1
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/types.d.ts +25 -1
- package/dist/frontend/types.js +121 -7
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.js +256 -17
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +86 -1
- package/dist/ir/nodes.js +183 -0
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +89 -6
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/profile.d.ts +52 -0
- package/dist/library/profile.js +232 -0
- package/dist/library/profile.js.map +1 -0
- package/package.json +2 -2
- package/surface-manifest.json +67 -2
|
@@ -17,10 +17,10 @@ import * as ts from "../ts7/adapter.js";
|
|
|
17
17
|
import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, isCheckerPanic, genericSignatureTypeDiag, indexSignatureTypeDiag, noLoweringDiag, recordShapeMismatchDiag, requiresDynamicApiDiag, requiresDynamicPackageDiag, requiresDynamicTypeDiag, unionMismatchDiag, unsupportedDiag, unsupportedTypeDiag, } from "../../diagnostics/diagnostic.js";
|
|
18
18
|
import { arrayOf, BOOL, canAdaptDynFuncTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, F64, isJsonSafeType, isUndefinedArmedUnion, isUnitType, JSVAL, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID } from "../../ir/nodes.js";
|
|
19
19
|
import { provenanceActive } from "../provenance-registry.js";
|
|
20
|
-
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, overridesDtsPath, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
20
|
+
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
21
21
|
import { containsRecord, containsUnion, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../types.js";
|
|
22
22
|
import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalMember, nodeTypesOnlySymbol } from "./surfaces.js";
|
|
23
|
-
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
|
|
23
|
+
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
|
|
24
24
|
import { registerBuiltinErrorClasses, registerBuiltinEmitterClass, registerBuiltinStreamClasses, builtinErrorInfoOf, builtinEmitterInfoOf, builtinStreamInfoOf, analyzeClassDecoration, classIteratorDrainCall, classIteratorNextCall, classIteratorOf, classIteratorOpenCall, classIteratorRestDrainCall, classMemberNameOf, classValueRef, collectClassShape, exactClassOfReceiver, collectClassShapeInner, ctorAbiEquals, findMethodOn, findStaticOn, findGenericMethodOn, findGenericStaticOn, genericClassInstanceType, isSubclassOf, inHierarchy, overrideBelow, staticShadowBelow, upcastTo, lowerClassMembers, lowerClassCtor, lowerClassExpression, lowerClassExpressionInfo, lowerClassMethodMember, lowerClassValueProperty, lowerStaticMethod, throwingSetterFn, fieldInitStmts, lowerStaticFieldInits, lowerStaticFieldRead, lowerDerivedCtorBody, superCallStmt, lowerSuperMethodCall, superThisRef, lowerSuperAccessorRead, lowerSuperAccessorWrite, inheritsBuiltinErrorCtor, inheritsBuiltinEmitterCtor, errorMessageArg, lowerNew, accessorCall } from "./lower-classes.js";
|
|
25
25
|
import { mixinCallClassInfoOf, mixinIntersectionInstanceType } from "./lower-mixins.js";
|
|
26
26
|
import { bindingNeverReassigned, bodyReadsArguments, isThisParameter, paramShape, paramShapes, checkDefaultParamBodyType, completeArgs, wrappedUndefined, undefinedArgFor, requireExactArityValue, bodyReturnType, declaredReturnType, collectSignature, collectSignatureInner, collectGenericSignature, genericFnOf, lowerGenericCall, lowerGenericFnValue, inferTypeParamBindings, lowerGenericInstance, lowerCall, lowerTimersMemberCall, lowerPromiseMethodCall, lowerFilterNarrowCall, isTopLevelFnSymbol, lowerNestedFunctionDecl, lambdaSignature, lowerLambda, lowerFunction } from "./lower-calls.js";
|
|
@@ -148,7 +148,7 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
148
148
|
const dynamic = options.dynamic ?? false;
|
|
149
149
|
const targetPlatform = options.targetPlatform ?? process.platform;
|
|
150
150
|
const startupCrash = options.startupCrash ?? null;
|
|
151
|
-
const reachable = new Lowerer(program, entry, moduleOrder, dynamic, { targetPlatform }).discover();
|
|
151
|
+
const reachable = new Lowerer(program, entry, moduleOrder, dynamic, { targetPlatform }).discover(options.libRoots);
|
|
152
152
|
const emit = new Lowerer(program, entry, moduleOrder, dynamic, { reachable, targetPlatform, startupCrash });
|
|
153
153
|
const result = emit.run();
|
|
154
154
|
if (options.coverage !== true)
|
|
@@ -568,6 +568,12 @@ export class Lowerer {
|
|
|
568
568
|
/** Synthetic URLSearchParams.forEach loop functions, interned per
|
|
569
569
|
* callback arity/return — Map's pattern over the sp index walk. */
|
|
570
570
|
spHofHelpers = new Map();
|
|
571
|
+
/** The primitive-constructor VALUES (`String`/`Number`/`Boolean` as
|
|
572
|
+
* bare identifiers — CLI option tables store and compare them): one
|
|
573
|
+
* synthesized coercion function per constructor per program, interned
|
|
574
|
+
* here by name so every reference is the SAME zero-capture closure and
|
|
575
|
+
* `opt.type === String` is JS identity (see primitiveCtorClosure). */
|
|
576
|
+
primitiveCtorFns = new Map();
|
|
571
577
|
/** Optional-chain lowering state. While a chain body lowers, the guarded
|
|
572
578
|
* receiver NODE reads as a chainRecv (typed by the narrowed arm) instead
|
|
573
579
|
* of re-lowering, its checker type reads non-nullish (typeOf), and the
|
|
@@ -918,6 +924,16 @@ export class Lowerer {
|
|
|
918
924
|
this.flushDeferred(symbol);
|
|
919
925
|
return symbol;
|
|
920
926
|
}
|
|
927
|
+
// DEFAULT-SNAPSHOT storage lives on the EXPORTER'S default alias
|
|
928
|
+
// symbol (`export default someLet` — collectGlobals registers the
|
|
929
|
+
// Node-semantics snapshot there). getAliasedSymbol would resolve
|
|
930
|
+
// PAST it to the live let; walk the default-import hops and stop at
|
|
931
|
+
// the first default symbol carrying storage instead.
|
|
932
|
+
const snap = this.defaultSnapshotSymbolOf(symbol);
|
|
933
|
+
if (snap) {
|
|
934
|
+
this.flushDeferred(snap);
|
|
935
|
+
return snap;
|
|
936
|
+
}
|
|
921
937
|
symbol = this.checker.getAliasedSymbol(symbol);
|
|
922
938
|
}
|
|
923
939
|
// CommonJS export plumbing: a binding that resolved to a PROPERTY of a
|
|
@@ -933,6 +949,51 @@ export class Lowerer {
|
|
|
933
949
|
this.flushDeferred(symbol);
|
|
934
950
|
return symbol;
|
|
935
951
|
}
|
|
952
|
+
/** The default-snapshot storage symbol a DEFAULT-import alias chain
|
|
953
|
+
* lands on, or null. A mutable entity-name default (`export default
|
|
954
|
+
* someLet`) registers its Node-semantics snapshot global under the
|
|
955
|
+
* exporter's default ALIAS symbol; the checker's getAliasedSymbol
|
|
956
|
+
* resolves through that symbol to the live let, so this walk follows
|
|
957
|
+
* the default hops syntactically — default import clauses, `{ default
|
|
958
|
+
* as x }` specifiers, `export { default } from` re-exports — and stops
|
|
959
|
+
* at the first default symbol carrying registered storage. Local
|
|
960
|
+
* `export { x as default }` specifiers (no module specifier) are LIVE
|
|
961
|
+
* bindings in Node and fall through to ordinary alias resolution. */
|
|
962
|
+
defaultSnapshotSymbolOf(alias) {
|
|
963
|
+
let sym = alias;
|
|
964
|
+
for (let hop = 0; sym !== undefined && hop < 32; hop++) {
|
|
965
|
+
if (hop > 0 && sym.flags & ts.SymbolFlags.Alias && this.globalsBySymbol.has(sym))
|
|
966
|
+
return sym;
|
|
967
|
+
const d = this.checker
|
|
968
|
+
.declarationsOf(sym)
|
|
969
|
+
.find((x) => ts.isImportClause(x) || ts.isImportSpecifier(x) || ts.isExportSpecifier(x));
|
|
970
|
+
let spec;
|
|
971
|
+
let name;
|
|
972
|
+
if (d && ts.isImportClause(d) && ts.isImportDeclaration(d.parent)) {
|
|
973
|
+
spec = d.parent.moduleSpecifier;
|
|
974
|
+
name = "default";
|
|
975
|
+
}
|
|
976
|
+
else if (d && ts.isImportSpecifier(d)) {
|
|
977
|
+
const idecl = d.parent.parent.parent;
|
|
978
|
+
if (ts.isImportDeclaration(idecl))
|
|
979
|
+
spec = idecl.moduleSpecifier;
|
|
980
|
+
name = (d.propertyName ?? d.name).text;
|
|
981
|
+
}
|
|
982
|
+
else if (d && ts.isExportSpecifier(d)) {
|
|
983
|
+
const edecl = d.parent.parent;
|
|
984
|
+
if (ts.isExportDeclaration(edecl))
|
|
985
|
+
spec = edecl.moduleSpecifier;
|
|
986
|
+
name = (d.propertyName ?? d.name).text;
|
|
987
|
+
}
|
|
988
|
+
if (d === undefined || spec === undefined || !ts.isStringLiteral(spec) || name !== "default")
|
|
989
|
+
return null;
|
|
990
|
+
const dep = resolveImport(this.program, d.getSourceFile(), spec.text);
|
|
991
|
+
if (!dep)
|
|
992
|
+
return null;
|
|
993
|
+
sym = defaultExportSymbolOf(this, dep) ?? undefined;
|
|
994
|
+
}
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
936
997
|
/** A module's CJS export-table member symbol by NAME (the checker's
|
|
937
998
|
* module-symbol exports map — present in tsgo even where no expando
|
|
938
999
|
* property symbol exists at the attachment/use sites). */
|
|
@@ -1018,9 +1079,13 @@ export class Lowerer {
|
|
|
1018
1079
|
return null;
|
|
1019
1080
|
}
|
|
1020
1081
|
const spec = importDecl.moduleSpecifier.text;
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1082
|
+
// Bare specifiers resolve only for opted-in --npm-static packages —
|
|
1083
|
+
// their CJS entries take the same default-binding interop as a
|
|
1084
|
+
// relative require; every other bare import answers null and keeps
|
|
1085
|
+
// its own machinery.
|
|
1086
|
+
const dep = isRelativeSpecifier(spec)
|
|
1087
|
+
? resolveImport(this.program, importDecl.getSourceFile(), spec)
|
|
1088
|
+
: npmStaticDepSf7(this.program, importDecl.getSourceFile(), spec);
|
|
1024
1089
|
if (!dep || !isJsSourceFile(dep) || isNodeEsmFile(dep))
|
|
1025
1090
|
return null;
|
|
1026
1091
|
return dep;
|
|
@@ -1376,6 +1441,20 @@ export class Lowerer {
|
|
|
1376
1441
|
// the emitted program references, flushing deferred class diagnostics
|
|
1377
1442
|
// that a reached type makes relevant.
|
|
1378
1443
|
const artifacts = this.moduleArtifacts(functions);
|
|
1444
|
+
// Types still naming a class that never REGISTERED after retention's
|
|
1445
|
+
// flush (JS graphs whose class fences deferred to runtime — the
|
|
1446
|
+
// sentence-walker's path params, printer tables whose func-typed
|
|
1447
|
+
// fields spell the fenced class): the emitter would name a struct
|
|
1448
|
+
// that does not exist — the compile-C escape family. No instance of
|
|
1449
|
+
// such a class can ever exist (every construction site fenced), so
|
|
1450
|
+
// the slots are inert by construction: rewrite each to the f64 dummy
|
|
1451
|
+
// placeholder (boxNewC's uncollected-class stance, applied to unboxed
|
|
1452
|
+
// slots), uniformly across params/locals/globals/fields/body types so
|
|
1453
|
+
// every producer and consumer agrees. Programs with no unregistered
|
|
1454
|
+
// reference are untouched — byte-stability holds.
|
|
1455
|
+
if (this.diags.length === 0) {
|
|
1456
|
+
this.sanitizeUnregisteredClassTypes([functions, this.globalsList, artifacts.classes, artifacts.records, artifacts.unions]);
|
|
1457
|
+
}
|
|
1379
1458
|
const module = this.diags.length > 0
|
|
1380
1459
|
? null
|
|
1381
1460
|
: {
|
|
@@ -1400,6 +1479,45 @@ export class Lowerer {
|
|
|
1400
1479
|
...(this.npmLazyTraps ? { npmLazyTraps: this.npmLazyTraps } : {}),
|
|
1401
1480
|
};
|
|
1402
1481
|
}
|
|
1482
|
+
/** The unregistered-class type sweep (run()'s last step before the
|
|
1483
|
+
* module assembles): every `{kind:"object"}` TYPE naming a class with
|
|
1484
|
+
* no registered ClassInfo is rewritten IN PLACE to the f64 dummy.
|
|
1485
|
+
* classval types are exempt (they emit the class-independent
|
|
1486
|
+
* `ScrClassObj *` — inert-but-valid storage, the validator's own
|
|
1487
|
+
* stance), and only type objects rewrite — node-level classNames
|
|
1488
|
+
* (`new`, upcasts) cannot reach here (their lowerings fence without a
|
|
1489
|
+
* registered class), so the validator still backstops those. */
|
|
1490
|
+
sanitizeUnregisteredClassTypes(roots) {
|
|
1491
|
+
const isUnregisteredObjectType = (v) => typeof v === "object" && v !== null &&
|
|
1492
|
+
v.kind === "object" &&
|
|
1493
|
+
typeof v.className === "string" &&
|
|
1494
|
+
!this.classes.has(v.className);
|
|
1495
|
+
const sweep = (node) => {
|
|
1496
|
+
if (node === null || typeof node !== "object")
|
|
1497
|
+
return;
|
|
1498
|
+
if (Array.isArray(node)) {
|
|
1499
|
+
node.forEach((item, i) => {
|
|
1500
|
+
if (isUnregisteredObjectType(item))
|
|
1501
|
+
node[i] = F64;
|
|
1502
|
+
else
|
|
1503
|
+
sweep(item);
|
|
1504
|
+
});
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
const rec = node;
|
|
1508
|
+
for (const key of Object.keys(rec)) {
|
|
1509
|
+
if (key === "loc")
|
|
1510
|
+
continue;
|
|
1511
|
+
const v = rec[key];
|
|
1512
|
+
if (isUnregisteredObjectType(v))
|
|
1513
|
+
rec[key] = F64;
|
|
1514
|
+
else
|
|
1515
|
+
sweep(v);
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
for (const root of roots)
|
|
1519
|
+
sweep(root);
|
|
1520
|
+
}
|
|
1403
1521
|
/** True when `t` (recursively) names a class instance type with no
|
|
1404
1522
|
* registered ClassInfo — the shape of a JS class whose collection fenced.
|
|
1405
1523
|
* Used by run()'s global pruning; shapes/unions recurse with a seen-set
|
|
@@ -1461,7 +1579,7 @@ export class Lowerer {
|
|
|
1461
1579
|
* demand-driven) and lifted lambdas lower inline with their enclosing
|
|
1462
1580
|
* body; both fire edges through the same hooks and are not units
|
|
1463
1581
|
* themselves. */
|
|
1464
|
-
discover() {
|
|
1582
|
+
discover(extraRoots) {
|
|
1465
1583
|
const parts = this.splitFiles();
|
|
1466
1584
|
this.collectProgram(parts);
|
|
1467
1585
|
// Decorated classes analyze post-collection here too: the %init seeds
|
|
@@ -1569,6 +1687,12 @@ export class Lowerer {
|
|
|
1569
1687
|
this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf));
|
|
1570
1688
|
drainInstances();
|
|
1571
1689
|
});
|
|
1690
|
+
// LIBRARY mode's extra reachability roots (LowerOptions.libRoots): the
|
|
1691
|
+
// profile-mapped exports are called from outside the graph, so they
|
|
1692
|
+
// seed the worklist beside the init bodies. Unknown names are inert
|
|
1693
|
+
// (the export-map resolution reports them as SC4002 later).
|
|
1694
|
+
for (const root of extraRoots ?? [])
|
|
1695
|
+
this.onEdge?.(root);
|
|
1572
1696
|
while (queue.length > 0) {
|
|
1573
1697
|
// A body-level poison outside the per-statement catches (a fenced
|
|
1574
1698
|
// constructor/method parameter default lowered by declareParams):
|
|
@@ -2459,9 +2583,15 @@ export class Lowerer {
|
|
|
2459
2583
|
}
|
|
2460
2584
|
if (expected.kind === "array" && expr.type.kind === "array" && expected.elem.kind !== "jsval") {
|
|
2461
2585
|
const helper = this.arrayWidthHelper(expr.type, expected, expr.loc);
|
|
2462
|
-
if (
|
|
2586
|
+
if (helper)
|
|
2587
|
+
return { kind: "call", callee: helper, args: [expr], type: expected, loc: expr.loc };
|
|
2588
|
+
// The EMPTY-array lift (widthLiftPlan's emptyArr rule), top-level:
|
|
2589
|
+
// `cmd.aliases` typed `(null | undefined)[]` (an `aliases: []`
|
|
2590
|
+
// table) flowing into a `string[]` slot.
|
|
2591
|
+
const lift = this.widthLiftPlan(expr.type, expected);
|
|
2592
|
+
if (lift?.how !== "emptyArr")
|
|
2463
2593
|
return null;
|
|
2464
|
-
return
|
|
2594
|
+
return this.applyWidthLift(lift, expr, expected, expr.loc);
|
|
2465
2595
|
}
|
|
2466
2596
|
// A TUPLE flowing into an array slot (`const NAMES = [...] as const`
|
|
2467
2597
|
// assigned to a `readonly T[]` — the const-table pattern): TS erases
|
|
@@ -2547,10 +2677,39 @@ export class Lowerer {
|
|
|
2547
2677
|
return this.recordToClassPlan(src.shapeId, dst.className) !== null ? { how: "clsWidth" } : null;
|
|
2548
2678
|
}
|
|
2549
2679
|
if (dst.kind === "array" && src.kind === "array") {
|
|
2550
|
-
|
|
2680
|
+
if (this.widthLiftPlan(src.elem, dst.elem) !== null)
|
|
2681
|
+
return { how: "arr" };
|
|
2682
|
+
// The EMPTY-array lift: a unit-only element type (`readonly []`
|
|
2683
|
+
// mapped as the unit-element array, `(null | undefined)[]`) has no
|
|
2684
|
+
// per-element conversion into a data element — but the only value
|
|
2685
|
+
// such a slot honestly holds in the width family is EMPTY, so the
|
|
2686
|
+
// lift is a fresh empty array of the target type, guarded by a
|
|
2687
|
+
// runtime non-empty trap (the checked-extraction stance).
|
|
2688
|
+
if (this.unitOnlyElem(src.elem) && dst.elem.kind !== "jsval" && !this.unitOnlyElem(dst.elem)) {
|
|
2689
|
+
return { how: "emptyArr" };
|
|
2690
|
+
}
|
|
2691
|
+
return null;
|
|
2692
|
+
}
|
|
2693
|
+
// A TUPLE flowing into an array FIELD/ELEMENT (`aliases: ["ls"]` into
|
|
2694
|
+
// an `aliases: string[]` slot): per-position lifts, the top-level
|
|
2695
|
+
// tuple-into-array coercion applied recursively.
|
|
2696
|
+
if (dst.kind === "array" && src.kind === "record" && dst.elem.kind !== "jsval") {
|
|
2697
|
+
const from = this.shapes.get(src.shapeId);
|
|
2698
|
+
if (from?.tuple && from.fields.every((f) => this.widthLiftPlan(f.type, dst.elem) !== null)) {
|
|
2699
|
+
return { how: "tupleArr" };
|
|
2700
|
+
}
|
|
2701
|
+
return null;
|
|
2551
2702
|
}
|
|
2552
2703
|
return null;
|
|
2553
2704
|
}
|
|
2705
|
+
/** True for the unit-only element types (`(null | undefined)[]`, the
|
|
2706
|
+
* `readonly []` mapping): a union whose every arm is a unit. */
|
|
2707
|
+
unitOnlyElem(t) {
|
|
2708
|
+
if (t.kind !== "union")
|
|
2709
|
+
return false;
|
|
2710
|
+
const def = this.unions.get(t.unionId);
|
|
2711
|
+
return def !== undefined && def.arms.every((a) => isUnitType(a));
|
|
2712
|
+
}
|
|
2554
2713
|
/** The build side of widthLiftPlan: the IrExpr converting `value` into
|
|
2555
2714
|
* `dst` under a plan the caller validated. Interns whatever helpers the
|
|
2556
2715
|
* lift needs (planned first, so the interns cannot fail — a failure here
|
|
@@ -2597,6 +2756,20 @@ export class Lowerer {
|
|
|
2597
2756
|
throw new Error("lowerer bug: planned arr lift failed to intern");
|
|
2598
2757
|
return { kind: "call", callee: helper, args: [value], type: dst, loc };
|
|
2599
2758
|
}
|
|
2759
|
+
case "tupleArr": {
|
|
2760
|
+
if (dst.kind !== "array" || value.type.kind !== "record")
|
|
2761
|
+
throw new Error("lowerer bug: tupleArr lift shape");
|
|
2762
|
+
const helper = this.tupleArrayWidthHelper(value.type.shapeId, dst, loc);
|
|
2763
|
+
if (!helper)
|
|
2764
|
+
throw new Error("lowerer bug: planned tupleArr lift failed to intern");
|
|
2765
|
+
return { kind: "call", callee: helper, args: [value], type: dst, loc };
|
|
2766
|
+
}
|
|
2767
|
+
case "emptyArr": {
|
|
2768
|
+
if (dst.kind !== "array" || value.type.kind !== "array")
|
|
2769
|
+
throw new Error("lowerer bug: emptyArr lift shape");
|
|
2770
|
+
const helper = this.emptyArrayLiftHelper(value.type, dst, loc);
|
|
2771
|
+
return { kind: "call", callee: helper, args: [value], type: dst, loc };
|
|
2772
|
+
}
|
|
2600
2773
|
case "objWidth": {
|
|
2601
2774
|
if (dst.kind !== "record" || value.type.kind !== "object")
|
|
2602
2775
|
throw new Error("lowerer bug: objWidth lift shape");
|
|
@@ -2660,10 +2833,19 @@ export class Lowerer {
|
|
|
2660
2833
|
if (!ff) {
|
|
2661
2834
|
// A target field MISSING on the source: legal exactly when it is
|
|
2662
2835
|
// optional-flavored (an undefined-armed union) — the unset field
|
|
2663
|
-
// IS the undefined arm, the same rule literal completion applies
|
|
2664
|
-
//
|
|
2665
|
-
//
|
|
2666
|
-
|
|
2836
|
+
// IS the undefined arm, the same rule literal completion applies
|
|
2837
|
+
// — or 'unknown' (a dyn slot holds the DOM undefined, exactly
|
|
2838
|
+
// the absent-property read: the options-record call shape
|
|
2839
|
+
// against `{ plugins: unknown, ... }`). Never for tuples: a
|
|
2840
|
+
// completed position would change .length and JSON where Node
|
|
2841
|
+
// keeps the source arity.
|
|
2842
|
+
if (from.tuple)
|
|
2843
|
+
return null;
|
|
2844
|
+
if (tf.type.kind === "dyn") {
|
|
2845
|
+
plan.set(tf.name, { absentDyn: true });
|
|
2846
|
+
continue;
|
|
2847
|
+
}
|
|
2848
|
+
if (tf.type.kind !== "union")
|
|
2667
2849
|
return null;
|
|
2668
2850
|
const def = this.unions.get(tf.type.unionId);
|
|
2669
2851
|
const utag = def ? def.arms.findIndex((a) => a.kind === "undefinedT") : -1;
|
|
@@ -2717,6 +2899,11 @@ export class Lowerer {
|
|
|
2717
2899
|
kind: "recordLit",
|
|
2718
2900
|
fields: to.fields.map((f) => {
|
|
2719
2901
|
const lift = plan.get(f.name);
|
|
2902
|
+
if ("absentDyn" in lift) {
|
|
2903
|
+
// The unset 'unknown' field: the DOM undefined — exactly
|
|
2904
|
+
// the absent-property read's answer.
|
|
2905
|
+
return { name: f.name, value: dynUndefinedExpr(loc) };
|
|
2906
|
+
}
|
|
2720
2907
|
if ("absent" in lift) {
|
|
2721
2908
|
if (f.type.kind !== "union")
|
|
2722
2909
|
throw new Error("lowerer bug: absent lift against a non-union field");
|
|
@@ -2796,6 +2983,57 @@ export class Lowerer {
|
|
|
2796
2983
|
});
|
|
2797
2984
|
return name;
|
|
2798
2985
|
}
|
|
2986
|
+
/** Interned `%arr.empty.<n>(a)` — the EMPTY-array lift's build side: a
|
|
2987
|
+
* unit-only-element array reshapes into any data-element array by
|
|
2988
|
+
* answering a FRESH empty array, after a runtime non-empty trap (a
|
|
2989
|
+
* genuinely inhabited `(null | undefined)[]` cannot reshape — the
|
|
2990
|
+
* catchable-TypeError stance every checked extraction takes). */
|
|
2991
|
+
emptyArrayLiftHelper(fromT, toT, loc) {
|
|
2992
|
+
const key = `emptyarr:${typeKey(fromT.elem)}:${typeKey(toT.elem)}`;
|
|
2993
|
+
const existing = this.widthHelpers.get(key);
|
|
2994
|
+
if (existing)
|
|
2995
|
+
return existing;
|
|
2996
|
+
const name = `%arr.empty.${this.widthHelpers.size}`;
|
|
2997
|
+
this.widthHelpers.set(key, name);
|
|
2998
|
+
const a = { kind: "varRef", localId: "a.0", type: fromT, loc };
|
|
2999
|
+
this.liftedFns.push({
|
|
3000
|
+
name,
|
|
3001
|
+
params: [{ localId: "a.0", name: "a", type: fromT }],
|
|
3002
|
+
returnType: toT,
|
|
3003
|
+
locals: [{ id: "a.0", name: "a", type: fromT, mutable: true }],
|
|
3004
|
+
body: [
|
|
3005
|
+
{
|
|
3006
|
+
kind: "if",
|
|
3007
|
+
cond: {
|
|
3008
|
+
kind: "bin",
|
|
3009
|
+
op: "!==",
|
|
3010
|
+
left: { kind: "arrIntrinsic", method: "length", receiver: a, args: [], type: F64, loc },
|
|
3011
|
+
right: { kind: "numLit", value: 0, type: F64, loc },
|
|
3012
|
+
type: BOOL,
|
|
3013
|
+
loc,
|
|
3014
|
+
},
|
|
3015
|
+
then: [
|
|
3016
|
+
{
|
|
3017
|
+
kind: "throw",
|
|
3018
|
+
value: {
|
|
3019
|
+
kind: "libCall",
|
|
3020
|
+
fn: "error.new",
|
|
3021
|
+
args: [{ kind: "strLit", value: `expected ${this.fmt(toT)} (a non-empty ${this.fmt(fromT)} has no elements the target can hold)`, type: STRING, loc }],
|
|
3022
|
+
type: { kind: "object", className: "%TypeError" },
|
|
3023
|
+
loc,
|
|
3024
|
+
},
|
|
3025
|
+
loc,
|
|
3026
|
+
},
|
|
3027
|
+
],
|
|
3028
|
+
else_: [],
|
|
3029
|
+
loc,
|
|
3030
|
+
},
|
|
3031
|
+
{ kind: "return", value: { kind: "arrayLit", elems: [], type: toT, loc }, loc },
|
|
3032
|
+
],
|
|
3033
|
+
loc,
|
|
3034
|
+
});
|
|
3035
|
+
return name;
|
|
3036
|
+
}
|
|
2799
3037
|
arrayWidthHelper(fromT, toT, loc) {
|
|
2800
3038
|
const fromElem = fromT.elem;
|
|
2801
3039
|
const toElem = toT.elem;
|
|
@@ -3932,6 +4170,59 @@ export class Lowerer {
|
|
|
3932
4170
|
});
|
|
3933
4171
|
return name;
|
|
3934
4172
|
}
|
|
4173
|
+
/** The DEFERRED-INIT field read (`stream!: T` assigned past the
|
|
4174
|
+
* constructor's top level — the slot is `T | undefined`): interned
|
|
4175
|
+
* `%deferred.read.<n>(u)` extracting the declared type. SCALAR arms
|
|
4176
|
+
* whose JS-undefined behavior a unit default reproduces read that
|
|
4177
|
+
* default — bool false (conditions are exact: undefined and false are
|
|
4178
|
+
* both falsy; only printing/strict-equality could tell) and f64 NaN
|
|
4179
|
+
* (arithmetic and conditions exact) — while string and REF arms keep
|
|
4180
|
+
* the checked-extraction TRAP: JS itself TypeErrors the first member
|
|
4181
|
+
* use of such an undefined, so the catchable TypeError at the read is
|
|
4182
|
+
* the same failure, named earlier (SEMANTICS.md). */
|
|
4183
|
+
deferredReadHelper(fromId, target, loc) {
|
|
4184
|
+
if (target.kind !== "bool" && target.kind !== "f64") {
|
|
4185
|
+
return this.narrowedArmHelper(fromId, target, loc);
|
|
4186
|
+
}
|
|
4187
|
+
const from = this.unions.get(fromId);
|
|
4188
|
+
const tag = this.armTag(fromId, target);
|
|
4189
|
+
const utag = from ? from.arms.findIndex((a) => a.kind === "undefinedT") : -1;
|
|
4190
|
+
if (!from || tag < 0 || utag < 0)
|
|
4191
|
+
return null;
|
|
4192
|
+
const key = `deferred:${fromId}:${tag}`;
|
|
4193
|
+
const existing = this.narrowHelpers.get(key);
|
|
4194
|
+
if (existing)
|
|
4195
|
+
return existing;
|
|
4196
|
+
const name = `%deferred.read.${this.narrowHelpers.size}`;
|
|
4197
|
+
this.narrowHelpers.set(key, name);
|
|
4198
|
+
const fromT = { kind: "union", unionId: fromId };
|
|
4199
|
+
const u = { kind: "varRef", localId: "u.0", type: fromT, loc };
|
|
4200
|
+
const dflt = target.kind === "bool"
|
|
4201
|
+
? { kind: "boolLit", value: false, type: BOOL, loc }
|
|
4202
|
+
: { kind: "numLit", value: NaN, type: F64, loc };
|
|
4203
|
+
this.liftedFns.push({
|
|
4204
|
+
name,
|
|
4205
|
+
params: [{ localId: "u.0", name: "u", type: fromT }],
|
|
4206
|
+
returnType: target,
|
|
4207
|
+
locals: [{ id: "u.0", name: "u", type: fromT, mutable: true }],
|
|
4208
|
+
body: [
|
|
4209
|
+
{
|
|
4210
|
+
kind: "if",
|
|
4211
|
+
cond: { kind: "unionIsTag", unionId: fromId, tag: utag, negated: false, value: u, type: BOOL, loc },
|
|
4212
|
+
then: [{ kind: "return", value: dflt, loc }],
|
|
4213
|
+
else_: null,
|
|
4214
|
+
loc,
|
|
4215
|
+
},
|
|
4216
|
+
{
|
|
4217
|
+
kind: "return",
|
|
4218
|
+
value: { kind: "unionNarrow", unionId: fromId, tag, value: u, type: target, loc },
|
|
4219
|
+
loc,
|
|
4220
|
+
},
|
|
4221
|
+
],
|
|
4222
|
+
loc,
|
|
4223
|
+
});
|
|
4224
|
+
return name;
|
|
4225
|
+
}
|
|
3935
4226
|
/** True when a static value can become ONE island value: jsval itself,
|
|
3936
4227
|
* anything boundary-safe (the deep JSON marshal), a record whose fields
|
|
3937
4228
|
* all can (built as an island OBJECT literal, field by field), or an
|
|
@@ -4851,7 +5142,7 @@ export class Lowerer {
|
|
|
4851
5142
|
// unresolvable ones never collected, so they skip here too.
|
|
4852
5143
|
const baseName = ts.isMethodDeclaration(fnLike)
|
|
4853
5144
|
? classMemberNameOf(this, fnLike.name)
|
|
4854
|
-
: ts.isIdentifier(fnLike.name)
|
|
5145
|
+
: ts.isIdentifier(fnLike.name) || ts.isPrivateIdentifier(fnLike.name)
|
|
4855
5146
|
? fnLike.name.text
|
|
4856
5147
|
: null;
|
|
4857
5148
|
if (baseName === null)
|
|
@@ -5192,6 +5483,27 @@ export class Lowerer {
|
|
|
5192
5483
|
resolveThis() {
|
|
5193
5484
|
return this.resolveKey(THIS_BINDING);
|
|
5194
5485
|
}
|
|
5486
|
+
/** READ-ONLY twin of resolveLocal for PROBES (isIslandExpr): answers
|
|
5487
|
+
* the nearest binding entry without boxing, threading, or predeclaring.
|
|
5488
|
+
* resolveKey mutates capture state as a side effect, and a speculative
|
|
5489
|
+
* island-ness query through a context that takes no captures (a plain
|
|
5490
|
+
* declared function between the origin and the reference) was an ICE —
|
|
5491
|
+
* the REAL lowering path still resolves (and diagnoses) the reference
|
|
5492
|
+
* itself. */
|
|
5493
|
+
peekLocal(ident) {
|
|
5494
|
+
let symbol = this.checker.getSymbolAtLocation(ident);
|
|
5495
|
+
if (ident.parent && ts.isShorthandPropertyAssignment(ident.parent) && ident.parent.name === ident) {
|
|
5496
|
+
symbol = this.checker.getShorthandAssignmentValueSymbol(ident.parent) ?? symbol;
|
|
5497
|
+
}
|
|
5498
|
+
if (!symbol)
|
|
5499
|
+
return null;
|
|
5500
|
+
for (let depth = this.fnStack.length - 1; depth >= 0; depth--) {
|
|
5501
|
+
const hit = this.bindingIn(this.fnStack[depth], symbol);
|
|
5502
|
+
if (hit)
|
|
5503
|
+
return hit;
|
|
5504
|
+
}
|
|
5505
|
+
return null;
|
|
5506
|
+
}
|
|
5195
5507
|
resolveKey(symbol, blame) {
|
|
5196
5508
|
const direct = this.bindingIn(this.ctx, symbol);
|
|
5197
5509
|
if (direct)
|
|
@@ -5225,6 +5537,17 @@ export class Lowerer {
|
|
|
5225
5537
|
const ctx = this.fnStack[j];
|
|
5226
5538
|
let entry = ctx.captureBySymbol.get(symbol);
|
|
5227
5539
|
if (!entry) {
|
|
5540
|
+
// A context that takes NO captures (a plain declared function —
|
|
5541
|
+
// monomorphized/implicit instances lower this way) cannot carry
|
|
5542
|
+
// the binding through: the shape is a module binding whose only
|
|
5543
|
+
// storage is the init function's LOCAL (a typed-but-unmappable
|
|
5544
|
+
// const — the file-scope `new Map()` ledger idiom) read from
|
|
5545
|
+
// inside a nested instance. Fence it — in JS the statement
|
|
5546
|
+
// defers to its runtime trap like every collection failure;
|
|
5547
|
+
// asserting here was an ICE on ordinary npm-static JS.
|
|
5548
|
+
if (ctx.captures === null) {
|
|
5549
|
+
this.unsupported("SC1090", blame ?? this.checker.declarationsOf(symbol)[0] ?? this.entry, `the binding '${origin.name}' captured through a plain nested function (the declaration has no static storage a capture can thread — bind the value through a typed const, or read it in the declaring scope)`);
|
|
5550
|
+
}
|
|
5228
5551
|
const count = ctx.localCounters.get(origin.name) ?? 0;
|
|
5229
5552
|
ctx.localCounters.set(origin.name, count + 1);
|
|
5230
5553
|
entry = {
|
|
@@ -5239,9 +5562,6 @@ export class Lowerer {
|
|
|
5239
5562
|
};
|
|
5240
5563
|
ctx.locals.push(entry);
|
|
5241
5564
|
ctx.captureBySymbol.set(symbol, entry);
|
|
5242
|
-
if (ctx.captures === null) {
|
|
5243
|
-
throw new Error("lowerer bug: capture threading through a non-lifted function");
|
|
5244
|
-
}
|
|
5245
5565
|
ctx.captures.push({ localId: entry.id, name: entry.name, type: entry.type });
|
|
5246
5566
|
ctx.captureSources.push(parentEntry.id);
|
|
5247
5567
|
}
|
|
@@ -5947,7 +6267,7 @@ export class Lowerer {
|
|
|
5947
6267
|
if (module !== null)
|
|
5948
6268
|
return { module, member: access.name.text };
|
|
5949
6269
|
// The RE-EXPORT FACADE's namespace spelling: `import * as assert from
|
|
5950
|
-
// "./facade.js"` over `export { ok } from "node:assert"` (
|
|
6270
|
+
// "./facade.js"` over `export { ok } from "node:assert"` (the formatter idiom's
|
|
5951
6271
|
// universal/assert). The member's symbol is the facade's
|
|
5952
6272
|
// ExportSpecifier, and builtinImportOf's alias chase answers the
|
|
5953
6273
|
// builtin's own module/member — the same tables as a direct import;
|