@scriptc/compiler 0.0.33 → 0.0.35
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/dist/backend/cc-cache.test.js +1091 -7
- package/dist/backend/cc-cache.test.js.map +1 -1
- package/dist/backend/cc.d.ts +109 -18
- package/dist/backend/cc.js +1057 -289
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +26 -1
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-shapes.js +37 -4
- package/dist/backend/emission/emit-shapes.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +11 -2
- package/dist/backend/emission/emitter.js +11 -5
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/library-identity.d.ts +22 -0
- package/dist/backend/library-identity.js +103 -0
- package/dist/backend/library-identity.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +35 -5
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.d.ts +1 -0
- package/dist/backend/llvm/shapes.js +25 -1
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/split.d.ts +43 -0
- package/dist/backend/llvm/split.js +227 -0
- package/dist/backend/llvm/split.js.map +1 -0
- package/dist/backend/llvm/split.test.d.ts +1 -0
- package/dist/backend/llvm/split.test.js +102 -0
- package/dist/backend/llvm/split.test.js.map +1 -0
- package/dist/backend/mangle.d.ts +1 -0
- package/dist/backend/mangle.js +3 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/object-localize.d.ts +5 -5
- package/dist/backend/object-localize.js +19 -16
- package/dist/backend/object-localize.js.map +1 -1
- package/dist/backend/object-localize.test.js +57 -13
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/executable/early-cache.d.ts +86 -0
- package/dist/executable/early-cache.js +460 -0
- package/dist/executable/early-cache.js.map +1 -0
- package/dist/executable/early-cache.test.d.ts +1 -0
- package/dist/executable/early-cache.test.js +300 -0
- package/dist/executable/early-cache.test.js.map +1 -0
- package/dist/ffi/profile.d.ts +7 -2
- package/dist/ffi/profile.js +1 -0
- package/dist/ffi/profile.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +82 -0
- package/dist/frontend/input-tracker.js +380 -0
- package/dist/frontend/input-tracker.js.map +1 -0
- package/dist/frontend/input-tracker.test.d.ts +1 -0
- package/dist/frontend/input-tracker.test.js +137 -0
- package/dist/frontend/input-tracker.test.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.js +4 -10
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +169 -144
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +66 -32
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +89 -11
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +45 -26
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +3 -3
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.js +1 -1
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +1 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +49 -4
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +73 -29
- package/dist/frontend/lowering/lowerer.js +588 -116
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +1 -1
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.js +25 -26
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.js +29 -30
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm-static.test.d.ts +1 -0
- package/dist/frontend/npm-static.test.js +15 -0
- package/dist/frontend/npm-static.test.js.map +1 -0
- package/dist/frontend/npm.js +5 -17
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.js +44 -19
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.js +8 -29
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +40 -2
- package/dist/frontend/ts7/checker.js +194 -32
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/program.js +17 -18
- package/dist/frontend/ts7/program.js.map +1 -1
- package/dist/frontend/types.d.ts +33 -0
- package/dist/frontend/types.js +134 -45
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +458 -141
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +27 -4
- package/dist/ir/nodes.js +28 -0
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +37 -1
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/early-cache.d.ts +65 -0
- package/dist/library/early-cache.js +361 -0
- package/dist/library/early-cache.js.map +1 -0
- package/dist/library/early-cache.test.d.ts +1 -0
- package/dist/library/early-cache.test.js +528 -0
- package/dist/library/early-cache.test.js.map +1 -0
- package/dist/library/implementation-identity.d.ts +23 -0
- package/dist/library/implementation-identity.js +84 -0
- package/dist/library/implementation-identity.js.map +1 -0
- package/dist/library/int-infer.js +14 -0
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +21 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/semantic-source.d.ts +13 -0
- package/dist/library/semantic-source.js +257 -0
- package/dist/library/semantic-source.js.map +1 -0
- package/dist/library/semantic-source.test.d.ts +1 -0
- package/dist/library/semantic-source.test.js +113 -0
- package/dist/library/semantic-source.test.js.map +1 -0
- package/dist/library/sidecar.d.ts +6 -0
- package/dist/library/sidecar.js +11 -0
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.d.ts +6 -0
- package/dist/startup-cache.js +6 -0
- package/dist/startup-cache.js.map +1 -0
- package/package.json +3 -2
- package/surface-manifest.json +1 -1
|
@@ -24,7 +24,7 @@ import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtin
|
|
|
24
24
|
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
|
|
25
25
|
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";
|
|
26
26
|
import { mixinCallClassInfoOf, mixinIntersectionInstanceType } from "./lower-mixins.js";
|
|
27
|
-
import { bindingNeverReassigned, bodyReadsArguments, isThisParameter, paramShape, paramShapes, checkDefaultParamBodyType, completeArgs, wrappedUndefined, undefinedArgFor, requireExactArityValue, bodyReturnType, declaredReturnType, collectSignature, collectSignatureInner, collectGenericSignature, genericFnOf, lowerGenericCall, lowerGenericFnValue, inferTypeParamBindings, lowerGenericInstance, lowerCall, lowerFfiCall, lowerTimersMemberCall, lowerPromiseMethodCall, lowerFilterNarrowCall, isTopLevelFnSymbol, lowerNestedFunctionDecl, lambdaSignature, lowerLambda, lowerFunction, validateFfiImports } from "./lower-calls.js";
|
|
27
|
+
import { bindingNeverReassigned, bodyReadsArguments, implicitMonoFile, isThisParameter, paramShape, paramShapes, checkDefaultParamBodyType, completeArgs, wrappedUndefined, undefinedArgFor, requireExactArityValue, bodyReturnType, declaredReturnType, collectSignature, collectSignatureInner, collectGenericSignature, genericFnOf, lowerGenericCall, lowerGenericFnValue, inferTypeParamBindings, lowerGenericInstance, lowerCall, lowerFfiCall, lowerTimersMemberCall, lowerPromiseMethodCall, lowerFilterNarrowCall, isTopLevelFnSymbol, lowerNestedFunctionDecl, lambdaSignature, lowerLambda, lowerFunction, validateFfiImports } from "./lower-calls.js";
|
|
28
28
|
import { lowerArrayMethodCall, lowerBufferStaticCall, lowerBytesMethodCall, lowerBytesNew, lowerMapMethodCall, lowerMapForEachCall, buildMapForEachFn, lowerRecordOvfCaptureHelper, lowerEnvToPairsHelper, lowerSetMethodCall, lowerSetForEachCall, buildSetForEachFn, lowerRegexMethodCall, lowerStringMethodCall } from "./lower-containers.js";
|
|
29
29
|
import { lowerStreamModuleCall } from "./lower-stream.js";
|
|
30
30
|
import { lowerEmitOverrideSpec } from "./lower-emitter.js";
|
|
@@ -139,26 +139,39 @@ function directExternalTypeSpecifiersByFile(externalTypes) {
|
|
|
139
139
|
}
|
|
140
140
|
return out;
|
|
141
141
|
}
|
|
142
|
-
/** Build lowering runs
|
|
142
|
+
/** Build lowering runs as a reachability worklist over the ts.Program:
|
|
143
143
|
*
|
|
144
|
-
* 1.
|
|
144
|
+
* 1. REACHABLE EMIT — a worklist computes the set of reachable bodies.
|
|
145
|
+
* Seeds are
|
|
145
146
|
* the per-file init bodies (module top-level statements always run, in
|
|
146
147
|
* import order); lowering a body yields IR whose call/closure/new/
|
|
147
|
-
* virtualCall nodes are the edges that enqueue further bodies. The
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* pass did not mark. Keeping the emit order (and lambda/instance
|
|
154
|
-
* numbering) identical to the pre-reachability compiler means a fully
|
|
155
|
-
* reachable program emits byte-identical C.
|
|
148
|
+
* virtualCall nodes are the edges that enqueue further bodies. The pass
|
|
149
|
+
* retains that IR. Once the graph closes, those functions are assembled in
|
|
150
|
+
* deterministic declaration order beside the already-lowered init,
|
|
151
|
+
* generic-instance, and lifted bodies. Checker-backed IR construction
|
|
152
|
+
* therefore happens once instead of once for discovery and again for
|
|
153
|
+
* emission.
|
|
156
154
|
*
|
|
157
|
-
* `coverage: true` adds a
|
|
158
|
-
* the bodies
|
|
159
|
-
* nothing flushed), reported separately: whole-program analysis without
|
|
155
|
+
* `coverage: true` adds a second pass — the REMAINDER — that lowers only
|
|
156
|
+
* the bodies reachable emit did NOT mark (plus deferred collection
|
|
157
|
+
* diagnostics nothing flushed), reported separately: whole-program analysis without
|
|
160
158
|
* letting unreached code fail builds. */
|
|
161
159
|
export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
160
|
+
const phaseTiming = process.env["SCRIPTC_TIMING"] === "1";
|
|
161
|
+
const phaseStarted = performance.now();
|
|
162
|
+
let phaseLast = phaseStarted;
|
|
163
|
+
const timing = (phase, detail = {}) => {
|
|
164
|
+
if (!phaseTiming)
|
|
165
|
+
return;
|
|
166
|
+
const now = performance.now();
|
|
167
|
+
process.stderr.write(`scriptc lowering ${JSON.stringify({
|
|
168
|
+
phase,
|
|
169
|
+
phase_ms: Math.round((now - phaseLast) * 10) / 10,
|
|
170
|
+
total_ms: Math.round((now - phaseStarted) * 10) / 10,
|
|
171
|
+
...detail,
|
|
172
|
+
})}\n`);
|
|
173
|
+
phaseLast = now;
|
|
174
|
+
};
|
|
162
175
|
const dynamic = options.dynamic ?? false;
|
|
163
176
|
const targetPlatform = options.targetPlatform ?? process.platform;
|
|
164
177
|
const startupCrash = options.startupCrash ?? null;
|
|
@@ -167,9 +180,8 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
167
180
|
// pass constructs (nothing calls their %init at startup — the import()
|
|
168
181
|
// site's namespace builder does, on the engine microtask, Node's
|
|
169
182
|
// evaluation point for them). Inadmissible static cycles inside the
|
|
170
|
-
// added subgraph are minted here and handed to
|
|
171
|
-
//
|
|
172
|
-
// extension of the shared array no later pass re-walks the subgraph.
|
|
183
|
+
// added subgraph are minted here and handed to reachable emit after this
|
|
184
|
+
// extension of the shared array; no later pass re-walks the subgraph.
|
|
173
185
|
const dynamicCycleDiags = [];
|
|
174
186
|
if (dynamic) {
|
|
175
187
|
appendDynamicImportModules(program, moduleOrder, (cycle, reason) => {
|
|
@@ -183,50 +195,71 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
183
195
|
directExternalTypeSpecifiersByFile(externalTypes);
|
|
184
196
|
const validation = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
185
197
|
targetPlatform,
|
|
198
|
+
startupCrash,
|
|
186
199
|
ffiImports,
|
|
187
200
|
libraryCallbacks,
|
|
188
201
|
externalTypes,
|
|
189
202
|
externalTypeSpecifiersByFile,
|
|
190
203
|
});
|
|
191
204
|
const ffiValidation = validateFfiImports(validation);
|
|
192
|
-
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
const
|
|
205
|
+
timing("ffi-validate");
|
|
206
|
+
// Reachability must use the same exact-symbol ownership as FFI validation.
|
|
207
|
+
// Otherwise a local function shadowing a configured ambient name is mistaken for FFI
|
|
208
|
+
// while computing reachability, even though ordinary lowering would
|
|
209
|
+
// correctly handle it as TypeScript. FFI-free builds reuse the validation
|
|
210
|
+
// lowerer because validation is an immediate no-op there.
|
|
211
|
+
const reachableEmit = ffiImports.length === 0
|
|
199
212
|
? validation
|
|
200
213
|
: new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
201
214
|
targetPlatform,
|
|
215
|
+
startupCrash,
|
|
202
216
|
ffiImports,
|
|
203
217
|
libraryCallbacks,
|
|
204
218
|
externalTypes,
|
|
205
219
|
externalTypeSpecifiersByFile,
|
|
206
220
|
ffiBindingSymbols: ffiValidation.symbolsByName,
|
|
207
221
|
});
|
|
208
|
-
const reachable = discovery.discover(options.libRoots);
|
|
209
|
-
const emit = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
210
|
-
reachable,
|
|
211
|
-
targetPlatform,
|
|
212
|
-
startupCrash,
|
|
213
|
-
ffiImports,
|
|
214
|
-
libraryCallbacks,
|
|
215
|
-
ffiBindingSymbols: ffiValidation.symbolsByName,
|
|
216
|
-
externalTypes,
|
|
217
|
-
externalTypeSpecifiersByFile,
|
|
218
|
-
});
|
|
219
222
|
for (const d of dynamicCycleDiags)
|
|
220
|
-
|
|
223
|
+
reachableEmit.pushDiag(d);
|
|
221
224
|
for (const d of ffiValidation.diagnostics)
|
|
222
|
-
|
|
223
|
-
const
|
|
225
|
+
reachableEmit.pushDiag(d);
|
|
226
|
+
const emitted = reachableEmit.emitReachable(options.libRoots);
|
|
227
|
+
const { reachable } = emitted;
|
|
228
|
+
let result = emitted.result;
|
|
229
|
+
let resultLowerer = reachableEmit;
|
|
230
|
+
timing("reachable-emit", { reachable: reachable.size });
|
|
231
|
+
// A generic class-rest support decision can depend on record metadata
|
|
232
|
+
// whose historical owner is discovered only while retained bodies lower.
|
|
233
|
+
// If the settled answer is a fence, rerun the ordinary reachable emit so
|
|
234
|
+
// the PoisonError occurs in its original statement window: later
|
|
235
|
+
// declarators stay unvisited, bindings block, cascades and stats match the
|
|
236
|
+
// historical compiler. This is a rare compatibility fallback; programs
|
|
237
|
+
// without such a settled fence retain checker-backed IR exactly once.
|
|
238
|
+
if (reachableEmit.requiresHistoricalOrderRelower) {
|
|
239
|
+
const emit = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
240
|
+
reachable,
|
|
241
|
+
targetPlatform,
|
|
242
|
+
startupCrash,
|
|
243
|
+
ffiImports,
|
|
244
|
+
libraryCallbacks,
|
|
245
|
+
ffiBindingSymbols: ffiValidation.symbolsByName,
|
|
246
|
+
externalTypes,
|
|
247
|
+
externalTypeSpecifiersByFile,
|
|
248
|
+
});
|
|
249
|
+
for (const d of dynamicCycleDiags)
|
|
250
|
+
emit.pushDiag(d);
|
|
251
|
+
for (const d of ffiValidation.diagnostics)
|
|
252
|
+
emit.pushDiag(d);
|
|
253
|
+
result = emit.run();
|
|
254
|
+
resultLowerer = emit;
|
|
255
|
+
timing("historical-order-relower");
|
|
256
|
+
}
|
|
224
257
|
if (options.coverage !== true)
|
|
225
258
|
return result;
|
|
226
259
|
const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
227
260
|
reachable,
|
|
228
261
|
remainder: true,
|
|
229
|
-
alreadyFlushed:
|
|
262
|
+
alreadyFlushed: resultLowerer.flushedSymbols,
|
|
230
263
|
targetPlatform,
|
|
231
264
|
ffiImports,
|
|
232
265
|
libraryCallbacks,
|
|
@@ -330,7 +363,7 @@ export function neverTaintedJsType(L, node, t) {
|
|
|
330
363
|
if (depth === 0)
|
|
331
364
|
return false;
|
|
332
365
|
if (x.isUnionType())
|
|
333
|
-
return
|
|
366
|
+
return ts.constituentTypes(x).some((a) => walk(a, depth - 1));
|
|
334
367
|
if (L.checker.isArrayType(x) || L.checker.isTupleType(x)) {
|
|
335
368
|
return L.checker
|
|
336
369
|
.getTypeArguments(x)
|
|
@@ -602,6 +635,155 @@ export class Lowerer {
|
|
|
602
635
|
/** Monomorphization worklist: instances queued by call sites, drained in
|
|
603
636
|
* run() (processing an instance body can queue more). */
|
|
604
637
|
instantiationQueue = [];
|
|
638
|
+
/** Historical emit rank of the retained declaration/init body currently
|
|
639
|
+
* lowering, and the earliest such owner that demanded each generic
|
|
640
|
+
* instance. Reachability can encounter a later caller first; the minimum
|
|
641
|
+
* rank recovers the old emitter's source-order monomorphization queue. */
|
|
642
|
+
genericDemandOwner = null;
|
|
643
|
+
genericDemandRoots = [];
|
|
644
|
+
genericFunctionDemandOwner = new Map();
|
|
645
|
+
genericClassDemandOwner = new Map();
|
|
646
|
+
genericDemandPriority = new Map();
|
|
647
|
+
genericClassDemandPriority = new Map();
|
|
648
|
+
withGenericDemandOwner(owner, fn) {
|
|
649
|
+
const previous = this.genericDemandOwner;
|
|
650
|
+
this.genericDemandOwner = owner;
|
|
651
|
+
try {
|
|
652
|
+
return fn();
|
|
653
|
+
}
|
|
654
|
+
finally {
|
|
655
|
+
this.genericDemandOwner = previous;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
noteGenericInstanceDemand(inst) {
|
|
659
|
+
const owner = this.genericDemandOwner;
|
|
660
|
+
owner?.functionDemands.push(inst);
|
|
661
|
+
const ref = this.genericDemandPriority.get(inst) ?? { rank: [4, Number.MAX_SAFE_INTEGER] };
|
|
662
|
+
const currentRank = owner?.priority;
|
|
663
|
+
if (currentRank && (currentRank[0] < ref.rank[0] ||
|
|
664
|
+
(currentRank[0] === ref.rank[0] && currentRank[1] < (ref.rank[1] ?? 0)))) {
|
|
665
|
+
ref.rank = currentRank;
|
|
666
|
+
}
|
|
667
|
+
this.genericDemandPriority.set(inst, ref);
|
|
668
|
+
}
|
|
669
|
+
noteGenericClassInstanceDemand(info) {
|
|
670
|
+
const owner = this.genericDemandOwner;
|
|
671
|
+
owner?.classDemands.push(info);
|
|
672
|
+
const ref = this.genericClassDemandPriority.get(info) ?? { rank: [4, Number.MAX_SAFE_INTEGER] };
|
|
673
|
+
const currentRank = owner?.priority;
|
|
674
|
+
if (currentRank && (currentRank[0] < ref.rank[0] ||
|
|
675
|
+
(currentRank[0] === ref.rank[0] && currentRank[1] < (ref.rank[1] ?? 0)))) {
|
|
676
|
+
ref.rank = currentRank;
|
|
677
|
+
}
|
|
678
|
+
this.genericClassDemandPriority.set(info, ref);
|
|
679
|
+
}
|
|
680
|
+
/** The old emitter lowered all reachable declarations in source order,
|
|
681
|
+
* then every init, before draining generic instances FIFO. Reorder the
|
|
682
|
+
* retained queue from those recorded demands before any generic body
|
|
683
|
+
* lowers, so immediate support decisions see the same shape metadata too. */
|
|
684
|
+
restoreGenericInstanceOrder(from = 0) {
|
|
685
|
+
const tail = this.instantiationQueue.slice(from);
|
|
686
|
+
const discoveryOrder = new Map(tail.map((entry, index) => [entry.inst, index]));
|
|
687
|
+
tail.sort((left, right) => {
|
|
688
|
+
const a = this.genericDemandPriority.get(left.inst)?.rank;
|
|
689
|
+
const b = this.genericDemandPriority.get(right.inst)?.rank;
|
|
690
|
+
if (a !== undefined && b !== undefined) {
|
|
691
|
+
const phase = a[0] - b[0];
|
|
692
|
+
if (phase !== 0)
|
|
693
|
+
return phase;
|
|
694
|
+
const order = a[1] - b[1];
|
|
695
|
+
if (order !== 0)
|
|
696
|
+
return order;
|
|
697
|
+
}
|
|
698
|
+
else if (a !== undefined) {
|
|
699
|
+
return -1;
|
|
700
|
+
}
|
|
701
|
+
else if (b !== undefined) {
|
|
702
|
+
return 1;
|
|
703
|
+
}
|
|
704
|
+
return discoveryOrder.get(left.inst) - discoveryOrder.get(right.inst);
|
|
705
|
+
});
|
|
706
|
+
this.instantiationQueue.splice(from, tail.length, ...tail);
|
|
707
|
+
}
|
|
708
|
+
restoreGenericClassInstanceOrder(from = 0) {
|
|
709
|
+
const tail = this.genericClassInstances.slice(from);
|
|
710
|
+
const discoveryOrder = new Map(tail.map((info, index) => [info, index]));
|
|
711
|
+
tail.sort((left, right) => {
|
|
712
|
+
const a = this.genericClassDemandPriority.get(left)?.rank;
|
|
713
|
+
const b = this.genericClassDemandPriority.get(right)?.rank;
|
|
714
|
+
if (a !== undefined && b !== undefined) {
|
|
715
|
+
const phase = a[0] - b[0];
|
|
716
|
+
if (phase !== 0)
|
|
717
|
+
return phase;
|
|
718
|
+
const order = a[1] - b[1];
|
|
719
|
+
if (order !== 0)
|
|
720
|
+
return order;
|
|
721
|
+
}
|
|
722
|
+
else if (a !== undefined) {
|
|
723
|
+
return -1;
|
|
724
|
+
}
|
|
725
|
+
else if (b !== undefined) {
|
|
726
|
+
return 1;
|
|
727
|
+
}
|
|
728
|
+
return discoveryOrder.get(left) - discoveryOrder.get(right);
|
|
729
|
+
});
|
|
730
|
+
this.genericClassInstances.splice(from, tail.length, ...tail);
|
|
731
|
+
}
|
|
732
|
+
settleGenericDemandPriorities() {
|
|
733
|
+
const functionQueue = [];
|
|
734
|
+
const classQueue = [];
|
|
735
|
+
const seenFunctions = new Set();
|
|
736
|
+
const seenClasses = new Set();
|
|
737
|
+
const enqueue = (owner) => {
|
|
738
|
+
for (const info of owner.classDemands) {
|
|
739
|
+
if (seenClasses.has(info))
|
|
740
|
+
continue;
|
|
741
|
+
seenClasses.add(info);
|
|
742
|
+
classQueue.push(info);
|
|
743
|
+
}
|
|
744
|
+
for (const inst of owner.functionDemands) {
|
|
745
|
+
if (seenFunctions.has(inst))
|
|
746
|
+
continue;
|
|
747
|
+
seenFunctions.add(inst);
|
|
748
|
+
functionQueue.push(inst);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
for (const root of [...this.genericDemandRoots].sort((a, b) => {
|
|
752
|
+
const left = a.priority;
|
|
753
|
+
const right = b.priority;
|
|
754
|
+
return left[0] - right[0] || left[1] - right[1];
|
|
755
|
+
}))
|
|
756
|
+
enqueue(root);
|
|
757
|
+
let classIndex = 0;
|
|
758
|
+
let functionIndex = 0;
|
|
759
|
+
let order = 0;
|
|
760
|
+
while (classIndex < classQueue.length || functionIndex < functionQueue.length) {
|
|
761
|
+
while (classIndex < classQueue.length) {
|
|
762
|
+
const info = classQueue[classIndex++];
|
|
763
|
+
this.genericClassDemandPriority.get(info).rank = [4, order++];
|
|
764
|
+
const owner = this.genericClassDemandOwner.get(info);
|
|
765
|
+
if (owner)
|
|
766
|
+
enqueue(owner);
|
|
767
|
+
}
|
|
768
|
+
while (functionIndex < functionQueue.length) {
|
|
769
|
+
const inst = functionQueue[functionIndex++];
|
|
770
|
+
this.genericDemandPriority.get(inst).rank = [4, order++];
|
|
771
|
+
const owner = this.genericFunctionDemandOwner.get(inst);
|
|
772
|
+
if (owner)
|
|
773
|
+
enqueue(owner);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
for (const info of this.genericClassInstances) {
|
|
777
|
+
const ref = this.genericClassDemandPriority.get(info);
|
|
778
|
+
if (ref && ref.rank[1] === Number.MAX_SAFE_INTEGER)
|
|
779
|
+
ref.rank = [4, order++];
|
|
780
|
+
}
|
|
781
|
+
for (const { inst } of this.instantiationQueue) {
|
|
782
|
+
const ref = this.genericDemandPriority.get(inst);
|
|
783
|
+
if (ref && ref.rank[1] === Number.MAX_SAFE_INTEGER)
|
|
784
|
+
ref.rank = [4, order++];
|
|
785
|
+
}
|
|
786
|
+
}
|
|
605
787
|
/** Non-null while an instance body lowers: type-parameter symbol →
|
|
606
788
|
* concrete IR type, consulted inside mapType's recursion. */
|
|
607
789
|
typeParamBindings = null;
|
|
@@ -671,6 +853,16 @@ export class Lowerer {
|
|
|
671
853
|
/** Synthetic array-HOF loop functions (map/filter/forEach desugar),
|
|
672
854
|
* interned per method + element/callback-result type: key → fn name. */
|
|
673
855
|
arrHofHelpers = new Map();
|
|
856
|
+
/** Derived shape metadata that depends on another shape's declaration
|
|
857
|
+
* order. These settle before helper bodies rebuild from that metadata. */
|
|
858
|
+
shapeOrderMetadataFinalizers = [];
|
|
859
|
+
/** Settled generic class-rest metadata requires the historical emit
|
|
860
|
+
* fallback so its fence can poison the original statement atomically. */
|
|
861
|
+
requiresHistoricalOrderRelower = false;
|
|
862
|
+
/** Helpers that snapshot shape declaration order into their bodies.
|
|
863
|
+
* Reachability lowers inits before the declarations they discover, so
|
|
864
|
+
* these rebuild after the worklist restores historical shape metadata. */
|
|
865
|
+
shapeOrderHelperFinalizers = [];
|
|
674
866
|
/** Emit-override specializations (`%C.emit:<event>` — lower-emitter.ts's
|
|
675
867
|
* emit-overrides block): interned names, the drive-loop queue, and the
|
|
676
868
|
* currently-lowering specialization's context (the super-forward
|
|
@@ -900,7 +1092,7 @@ export class Lowerer {
|
|
|
900
1092
|
statementsIsland: 0,
|
|
901
1093
|
functionsSkipped: 0,
|
|
902
1094
|
};
|
|
903
|
-
/**
|
|
1095
|
+
/** Reachability edge sink: every resolution of
|
|
904
1096
|
* a reference to a lowerable body reports its name here — recorded even
|
|
905
1097
|
* when the enclosing statement later poisons. */
|
|
906
1098
|
onEdge = null;
|
|
@@ -944,9 +1136,12 @@ export class Lowerer {
|
|
|
944
1136
|
get scopes() {
|
|
945
1137
|
return this.ctx.scopes;
|
|
946
1138
|
}
|
|
947
|
-
/** Names of bodies
|
|
948
|
-
* (the discovery pass itself). */
|
|
1139
|
+
/** Names of bodies a prior reachability pass reached; null lowers everything. */
|
|
949
1140
|
reachable;
|
|
1141
|
+
/** Reachability computed by this same Lowerer when retained worklist IR
|
|
1142
|
+
* is assembled directly. The configured reachable set remains null so
|
|
1143
|
+
* demand-driven instance lowering keeps its existing gates. */
|
|
1144
|
+
reachableForArtifacts = null;
|
|
950
1145
|
/** Coverage remainder mode: the reachability gate inverts (see wantBody)
|
|
951
1146
|
* and no module is built. */
|
|
952
1147
|
remainder;
|
|
@@ -1061,9 +1256,7 @@ export class Lowerer {
|
|
|
1061
1256
|
// --dynamic: modules reachable only through dynamic import() joined
|
|
1062
1257
|
// moduleOrder BEFORE any pass constructed — lowerToIr runs
|
|
1063
1258
|
// appendDynamicImportModules once on the shared array (a per-pass run
|
|
1064
|
-
// here
|
|
1065
|
-
// diagnostics are discarded by design, and the extended order left
|
|
1066
|
-
// nothing for the emit pass to re-detect).
|
|
1259
|
+
// here would repeatedly extend the graph and duplicate cycle reports).
|
|
1067
1260
|
this.moduleOrder.forEach((sf, i) => {
|
|
1068
1261
|
this.fileTag.set(sf, sf === entry ? "" : `%m${i}.`);
|
|
1069
1262
|
});
|
|
@@ -1094,7 +1287,7 @@ export class Lowerer {
|
|
|
1094
1287
|
* namespace path (nsPathPrefix), so `namespace A { export class C }`
|
|
1095
1288
|
* and a top-level `class C` never collide. Class EXPRESSIONS name by
|
|
1096
1289
|
* SOURCE POSITION (`%cx<start>.<name>`): deterministic across the
|
|
1097
|
-
*
|
|
1290
|
+
* builds (no counter can drift between invocations),
|
|
1098
1291
|
* program-unique through the file qualifier, and collision-free with
|
|
1099
1292
|
* user identifiers ('%'). */
|
|
1100
1293
|
classNamer = (decl) => ts.isClassExpression(decl)
|
|
@@ -1776,8 +1969,8 @@ export class Lowerer {
|
|
|
1776
1969
|
loc,
|
|
1777
1970
|
};
|
|
1778
1971
|
}
|
|
1779
|
-
/** True when this body should lower: everything with no reachable set
|
|
1780
|
-
*
|
|
1972
|
+
/** True when this body should lower: everything with no reachable set,
|
|
1973
|
+
* the marked bodies in an externally-gated pass, and exactly the
|
|
1781
1974
|
* UNMARKED bodies in the coverage remainder. */
|
|
1782
1975
|
wantBody(name) {
|
|
1783
1976
|
if (this.reachable === null)
|
|
@@ -1792,6 +1985,16 @@ export class Lowerer {
|
|
|
1792
1985
|
}
|
|
1793
1986
|
run() {
|
|
1794
1987
|
const parts = this.splitFiles();
|
|
1988
|
+
// The coverage remainder deliberately visits every body that reachable
|
|
1989
|
+
// emit skipped. Those files are already phase-managed, so an ordinary
|
|
1990
|
+
// checker miss would stay a one-node IPC query instead of falling back
|
|
1991
|
+
// to the facade's whole-file batch. Coverage has no dead-body boundary
|
|
1992
|
+
// to preserve: prime each complete file now, reusing the answers the
|
|
1993
|
+
// reachable pass already memoized and batching only the cold remainder.
|
|
1994
|
+
if (this.remainder) {
|
|
1995
|
+
for (const fp of parts)
|
|
1996
|
+
this.checker.prefetchSourceFile(fp.sf);
|
|
1997
|
+
}
|
|
1795
1998
|
this.collectProgram(parts);
|
|
1796
1999
|
// Decorated classes analyze AFTER the whole collection pass: a
|
|
1797
2000
|
// decorator's return type may name a subclass declared below the
|
|
@@ -1842,7 +2045,7 @@ export class Lowerer {
|
|
|
1842
2045
|
// inline require statements call theirs mid-body, and the guards make
|
|
1843
2046
|
// revisits cache hits — Node's evaluation order over the WHOLE graph
|
|
1844
2047
|
// falls out of the nesting. The coverage remainder skips them — they
|
|
1845
|
-
// are reachable by definition, already counted by
|
|
2048
|
+
// are reachable by definition, already counted by reachable emit.
|
|
1846
2049
|
if (!this.remainder) {
|
|
1847
2050
|
for (const fp of parts) {
|
|
1848
2051
|
functions.push(this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)));
|
|
@@ -1931,6 +2134,11 @@ export class Lowerer {
|
|
|
1931
2134
|
...(this.npmLazyTraps ? { npmLazyTraps: this.npmLazyTraps } : {}),
|
|
1932
2135
|
};
|
|
1933
2136
|
}
|
|
2137
|
+
return this.finishModule(functions);
|
|
2138
|
+
}
|
|
2139
|
+
/** Final retention, pruning, and module assembly shared by ordinary emit
|
|
2140
|
+
* and the retained reachability worklist. */
|
|
2141
|
+
finishModule(functions) {
|
|
1934
2142
|
// Globals typed by a class that never REGISTERED (a JS class whose
|
|
1935
2143
|
// collection fenced — Symbol-keyed fields, an unsupported base): the
|
|
1936
2144
|
// declaration statement and every use compiled to runtime fences, but
|
|
@@ -2016,7 +2224,7 @@ export class Lowerer {
|
|
|
2016
2224
|
const module = this.diags.length > 0
|
|
2017
2225
|
? null
|
|
2018
2226
|
: {
|
|
2019
|
-
irVersion:
|
|
2227
|
+
irVersion: 6,
|
|
2020
2228
|
sourceFile: this.entry.fileName,
|
|
2021
2229
|
functions,
|
|
2022
2230
|
classes: artifacts.classes,
|
|
@@ -2120,15 +2328,16 @@ export class Lowerer {
|
|
|
2120
2328
|
}
|
|
2121
2329
|
/** Whether run() counts a signature-blocked declaration in
|
|
2122
2330
|
* stats.functionsSkipped: whole-program passes and the coverage
|
|
2123
|
-
* remainder do;
|
|
2331
|
+
* remainder do; an externally-gated emit pass leaves the counting to the
|
|
2124
2332
|
* remainder (the declaration was never reached). */
|
|
2125
2333
|
countsSkips() {
|
|
2126
2334
|
return this.reachable === null || this.remainder;
|
|
2127
2335
|
}
|
|
2128
2336
|
/* ── reachability ─────────────────────────────────────────────────── */
|
|
2129
|
-
/**
|
|
2130
|
-
* reaches. Seeds are the per-file
|
|
2131
|
-
* run); edges fire from
|
|
2337
|
+
/** Reachable emit: computes the set of body names the program's entry
|
|
2338
|
+
* reaches and retains each body IR as it lowers. Seeds are the per-file
|
|
2339
|
+
* init bodies (top-level statements always run); edges fire from
|
|
2340
|
+
* RESOLUTION sites while a body lowers (noteEdge /
|
|
2132
2341
|
* noteVirtualEdge) — direct calls, closure creation (a taken closure may
|
|
2133
2342
|
* be called indirectly), `new`, super calls, accessor invocations, and
|
|
2134
2343
|
* virtual dispatch. Recording at resolution time (not off the produced
|
|
@@ -2138,19 +2347,45 @@ export class Lowerer {
|
|
|
2138
2347
|
* demand-driven) and lifted lambdas lower inline with their enclosing
|
|
2139
2348
|
* body; both fire edges through the same hooks and are not units
|
|
2140
2349
|
* themselves. */
|
|
2141
|
-
|
|
2350
|
+
emitReachable(extraRoots) {
|
|
2142
2351
|
const parts = this.splitFiles();
|
|
2352
|
+
// Direct lowering callers do not necessarily run program preflight.
|
|
2353
|
+
// Establish the same managed header/top-level batch here before
|
|
2354
|
+
// collection, while the production path simply finds warm memos.
|
|
2355
|
+
this.checker.prefetchSourceFileStructures(parts.map((fp) => fp.sf));
|
|
2356
|
+
// Signature collection may read the initializer type of a default whose
|
|
2357
|
+
// body type still admits undefined (for example `value =
|
|
2358
|
+
// process.env.VALUE`). The expression executes only when reached, but
|
|
2359
|
+
// that type query is mandatory now; batch hot defaults across ordinary
|
|
2360
|
+
// declarations before collectProgram visits their signatures.
|
|
2361
|
+
this.checker.prefetchCollectionTypes(parts.flatMap((fp) => fp.fnDecls
|
|
2362
|
+
.filter((decl) => decl.body !== undefined && decl.typeParameters === undefined)
|
|
2363
|
+
.flatMap((decl) => decl.parameters.flatMap((param) => param.initializer ? [param.initializer] : []))));
|
|
2364
|
+
// JavaScript class shapes are partly declared by constructor-body
|
|
2365
|
+
// assignments. Batch those collection-time queries across declarations
|
|
2366
|
+
// before collectProgram visits them one by one; reached body lowering
|
|
2367
|
+
// will reuse the same answers later.
|
|
2368
|
+
this.prefetchClassCollection(parts.flatMap((fp) => fp.classDecls));
|
|
2143
2369
|
this.collectProgram(parts);
|
|
2144
2370
|
// Decorated classes analyze post-collection here too: the %init seeds
|
|
2145
2371
|
// lower the decoration calls, whose edges (decorator bodies, construct
|
|
2146
|
-
// thunks)
|
|
2372
|
+
// thunks) reachable emit must see.
|
|
2147
2373
|
for (const info of this.classes.values())
|
|
2148
2374
|
analyzeClassDecoration(this, info);
|
|
2149
2375
|
this.prepareModuleInits(parts);
|
|
2150
2376
|
// Every lowerable body, by emitted-function name. The names double as
|
|
2151
|
-
//
|
|
2152
|
-
//
|
|
2377
|
+
// retained-function keys and are deterministic by construction
|
|
2378
|
+
// (qualified declaration names).
|
|
2153
2379
|
const units = new Map();
|
|
2380
|
+
const bodyRoots = (...roots) => roots.filter((root) => root !== undefined && root !== null);
|
|
2381
|
+
const functionRoots = (decl) => bodyRoots(...decl.parameters.map((param) => param.initializer), decl.body);
|
|
2382
|
+
const classCtorRoots = (info) => bodyRoots(...(info.ctor?.parameters ?? []).map((param) => param.initializer), info.ctor?.body, ...info.fieldOrder.map((field) => field.initializer));
|
|
2383
|
+
const classMemberRoots = (info) => [
|
|
2384
|
+
...classCtorRoots(info),
|
|
2385
|
+
...[...this.classMethodMembers(info)].flatMap(({ member }) => functionRoots(member)),
|
|
2386
|
+
...[...(info.staticMethods?.values() ?? [])].flatMap(({ member }) => functionRoots(member)),
|
|
2387
|
+
];
|
|
2388
|
+
let unitOrder = 0;
|
|
2154
2389
|
for (const fp of parts) {
|
|
2155
2390
|
for (const decl of fp.fnDecls) {
|
|
2156
2391
|
// Overload signatures share the implementation's symbol (and so
|
|
@@ -2162,8 +2397,13 @@ export class Lowerer {
|
|
|
2162
2397
|
if (!declSymbol || this.genericFnsBySymbol.has(declSymbol))
|
|
2163
2398
|
continue;
|
|
2164
2399
|
const sig = this.fnSigsBySymbol.get(declSymbol);
|
|
2165
|
-
if (sig)
|
|
2166
|
-
units.set(sig.name,
|
|
2400
|
+
if (sig) {
|
|
2401
|
+
units.set(sig.name, {
|
|
2402
|
+
order: unitOrder++,
|
|
2403
|
+
roots: functionRoots(decl),
|
|
2404
|
+
lower: () => this.lowerFunction(decl),
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2167
2407
|
}
|
|
2168
2408
|
}
|
|
2169
2409
|
for (const info of this.classes.values()) {
|
|
@@ -2178,20 +2418,93 @@ export class Lowerer {
|
|
|
2178
2418
|
// A FAMILY has no constructor function and no instance members —
|
|
2179
2419
|
// only its statics are units.
|
|
2180
2420
|
if (!info.generic) {
|
|
2181
|
-
units.set(`%${cName}.constructor`,
|
|
2421
|
+
units.set(`%${cName}.constructor`, {
|
|
2422
|
+
order: unitOrder++,
|
|
2423
|
+
roots: classCtorRoots(info),
|
|
2424
|
+
lower: () => this.lowerClassCtor(info),
|
|
2425
|
+
});
|
|
2182
2426
|
for (const { mName, member } of this.classMethodMembers(info)) {
|
|
2183
|
-
units.set(`%${cName}.${mName}`,
|
|
2427
|
+
units.set(`%${cName}.${mName}`, {
|
|
2428
|
+
order: unitOrder++,
|
|
2429
|
+
roots: functionRoots(member),
|
|
2430
|
+
lower: () => this.lowerClassMethodMember(info, member),
|
|
2431
|
+
});
|
|
2184
2432
|
}
|
|
2185
2433
|
for (const prop of info.throwingSetters) {
|
|
2186
|
-
units.set(`%${cName}.set:${prop}`,
|
|
2434
|
+
units.set(`%${cName}.set:${prop}`, {
|
|
2435
|
+
order: unitOrder++,
|
|
2436
|
+
roots: [],
|
|
2437
|
+
lower: () => this.throwingSetterFn(info, prop),
|
|
2438
|
+
});
|
|
2187
2439
|
}
|
|
2188
2440
|
}
|
|
2189
|
-
for (const name of info.staticMethods
|
|
2190
|
-
units.set(`%${cName}.static:${name}`,
|
|
2441
|
+
for (const [name, entry] of info.staticMethods ?? []) {
|
|
2442
|
+
units.set(`%${cName}.static:${name}`, {
|
|
2443
|
+
order: unitOrder++,
|
|
2444
|
+
roots: functionRoots(entry.member),
|
|
2445
|
+
lower: () => lowerStaticMethod(this, info, name),
|
|
2446
|
+
});
|
|
2191
2447
|
}
|
|
2192
2448
|
}
|
|
2449
|
+
// The old emit pass visited each file's functions and then its classes,
|
|
2450
|
+
// before every module init. Retained reachability discovers those
|
|
2451
|
+
// bodies from the inits, but first-seen record metadata still has to
|
|
2452
|
+
// follow that old order: Object.keys/JSON/inspect observe a shape's
|
|
2453
|
+
// declaredOrder. Keep output sorting separate — this rank controls only
|
|
2454
|
+
// that observable metadata.
|
|
2455
|
+
const metadataPriority = new Map();
|
|
2456
|
+
let declarationMetadataOrder = 0;
|
|
2457
|
+
const rank = (name) => {
|
|
2458
|
+
if (units.has(name) && !metadataPriority.has(name)) {
|
|
2459
|
+
metadataPriority.set(name, [1, declarationMetadataOrder++]);
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
for (const fp of parts) {
|
|
2463
|
+
for (const decl of fp.fnDecls) {
|
|
2464
|
+
if (!decl.body)
|
|
2465
|
+
continue;
|
|
2466
|
+
const symbol = declSymbolOf(this, decl);
|
|
2467
|
+
if (symbol && !this.genericFnsBySymbol.has(symbol)) {
|
|
2468
|
+
const sig = this.fnSigsBySymbol.get(symbol);
|
|
2469
|
+
if (sig)
|
|
2470
|
+
rank(sig.name);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
for (const decl of fp.classDecls) {
|
|
2474
|
+
const info = this.classes.get(this.classNamer(decl));
|
|
2475
|
+
if (!info)
|
|
2476
|
+
continue;
|
|
2477
|
+
const cName = info.def.name;
|
|
2478
|
+
rank(`%${cName}.constructor`);
|
|
2479
|
+
for (const { mName } of this.classMethodMembers(info))
|
|
2480
|
+
rank(`%${cName}.${mName}`);
|
|
2481
|
+
for (const name of info.staticMethods?.keys() ?? [])
|
|
2482
|
+
rank(`%${cName}.static:${name}`);
|
|
2483
|
+
for (const prop of info.throwingSetters)
|
|
2484
|
+
rank(`%${cName}.set:${prop}`);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
// Defensive fallback for declaration-like units registered outside
|
|
2488
|
+
// FileParts; they still precede init bodies in the old emit pass.
|
|
2489
|
+
for (const name of units.keys())
|
|
2490
|
+
rank(name);
|
|
2491
|
+
let expressionMetadataOrder = 0;
|
|
2492
|
+
let instanceMetadataOrder = 0;
|
|
2493
|
+
const demandOwner = (priority) => {
|
|
2494
|
+
const owner = {
|
|
2495
|
+
...(priority ? { priority } : {}),
|
|
2496
|
+
functionDemands: [],
|
|
2497
|
+
classDemands: [],
|
|
2498
|
+
};
|
|
2499
|
+
if (priority)
|
|
2500
|
+
this.genericDemandRoots.push(owner);
|
|
2501
|
+
return owner;
|
|
2502
|
+
};
|
|
2193
2503
|
const reachable = new Set();
|
|
2194
2504
|
const queue = [];
|
|
2505
|
+
const loweredUnits = new Map();
|
|
2506
|
+
const initFunctions = [];
|
|
2507
|
+
const instanceFunctions = [];
|
|
2195
2508
|
this.onEdge = (name) => {
|
|
2196
2509
|
if (reachable.has(name))
|
|
2197
2510
|
return;
|
|
@@ -2204,15 +2517,19 @@ export class Lowerer {
|
|
|
2204
2517
|
// edge to them can fire (references require the collected class).
|
|
2205
2518
|
this.onExprClassCollected = (info) => {
|
|
2206
2519
|
const cName = info.def.name;
|
|
2207
|
-
|
|
2520
|
+
const register = (name, roots, lower) => {
|
|
2521
|
+
units.set(name, { order: unitOrder++, roots, lower });
|
|
2522
|
+
metadataPriority.set(name, [3, expressionMetadataOrder++]);
|
|
2523
|
+
};
|
|
2524
|
+
register(`%${cName}.constructor`, classCtorRoots(info), () => this.lowerClassCtor(info));
|
|
2208
2525
|
for (const { mName, member } of this.classMethodMembers(info)) {
|
|
2209
|
-
|
|
2526
|
+
register(`%${cName}.${mName}`, functionRoots(member), () => this.lowerClassMethodMember(info, member));
|
|
2210
2527
|
}
|
|
2211
|
-
for (const name of info.staticMethods
|
|
2212
|
-
|
|
2528
|
+
for (const [name, entry] of info.staticMethods ?? []) {
|
|
2529
|
+
register(`%${cName}.static:${name}`, functionRoots(entry.member), () => lowerStaticMethod(this, info, name));
|
|
2213
2530
|
}
|
|
2214
2531
|
for (const prop of info.throwingSetters) {
|
|
2215
|
-
|
|
2532
|
+
register(`%${cName}.set:${prop}`, [], () => this.throwingSetterFn(info, prop));
|
|
2216
2533
|
}
|
|
2217
2534
|
};
|
|
2218
2535
|
// Generic instances queued by the bodies above lower here (an instance
|
|
@@ -2226,39 +2543,78 @@ export class Lowerer {
|
|
|
2226
2543
|
while (instLowered < this.instantiationQueue.length ||
|
|
2227
2544
|
clsInstLowered < this.genericClassInstances.length ||
|
|
2228
2545
|
specLowered < this.emitSpecQueue.length) {
|
|
2229
|
-
while (
|
|
2230
|
-
const
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
this.
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
throw e;
|
|
2546
|
+
while (clsInstLowered < this.genericClassInstances.length) {
|
|
2547
|
+
const waveEnd = this.genericClassInstances.length;
|
|
2548
|
+
this.checker.prefetchRoots(this.genericClassInstances.slice(clsInstLowered, waveEnd).flatMap(classMemberRoots));
|
|
2549
|
+
while (clsInstLowered < waveEnd) {
|
|
2550
|
+
const info = this.genericClassInstances[clsInstLowered++];
|
|
2551
|
+
const owner = demandOwner();
|
|
2552
|
+
this.genericClassDemandOwner.set(info, owner);
|
|
2553
|
+
const ref = this.genericClassDemandPriority.get(info) ?? { rank: [4, instanceMetadataOrder++] };
|
|
2554
|
+
this.genericClassDemandPriority.set(info, ref);
|
|
2555
|
+
instanceFunctions.push(...this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(ref, () => this.lowerClassMembers(info))));
|
|
2240
2556
|
}
|
|
2241
2557
|
}
|
|
2242
|
-
while (
|
|
2243
|
-
this.
|
|
2558
|
+
while (instLowered < this.instantiationQueue.length) {
|
|
2559
|
+
const waveEnd = this.instantiationQueue.length;
|
|
2560
|
+
this.checker.prefetchRoots(this.instantiationQueue
|
|
2561
|
+
.slice(instLowered, waveEnd)
|
|
2562
|
+
.flatMap(({ info }) => functionRoots(info.decl)));
|
|
2563
|
+
while (instLowered < waveEnd) {
|
|
2564
|
+
const { info, inst } = this.instantiationQueue[instLowered++];
|
|
2565
|
+
const owner = demandOwner();
|
|
2566
|
+
this.genericFunctionDemandOwner.set(inst, owner);
|
|
2567
|
+
const ref = this.genericDemandPriority.get(inst) ?? { rank: [4, instanceMetadataOrder++] };
|
|
2568
|
+
this.genericDemandPriority.set(inst, ref);
|
|
2569
|
+
// Body-level poisons skip the instance after retaining the
|
|
2570
|
+
// diagnostic and every edge fired before poisoning.
|
|
2571
|
+
try {
|
|
2572
|
+
instanceFunctions.push(this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(ref, () => this.lowerGenericInstance(info, inst))));
|
|
2573
|
+
}
|
|
2574
|
+
catch (e) {
|
|
2575
|
+
if (!(e instanceof PoisonError))
|
|
2576
|
+
throw e;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2244
2579
|
}
|
|
2245
2580
|
// Emit-override specialization bodies fire edges of their own
|
|
2246
2581
|
// (the super-forward chain, closures, generic calls) — lower them
|
|
2247
|
-
//
|
|
2582
|
+
// exactly like generic instances.
|
|
2248
2583
|
while (specLowered < this.emitSpecQueue.length) {
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2584
|
+
const waveEnd = this.emitSpecQueue.length;
|
|
2585
|
+
this.checker.prefetchRoots(this.emitSpecQueue
|
|
2586
|
+
.slice(specLowered, waveEnd)
|
|
2587
|
+
.flatMap(({ info }) => info.emitOverride ? functionRoots(info.emitOverride.decl) : []));
|
|
2588
|
+
while (specLowered < waveEnd) {
|
|
2589
|
+
try {
|
|
2590
|
+
const fn = this.shapes.withDeclaredOrderPriority([4, instanceMetadataOrder++], () => lowerEmitOverrideSpec(this, this.emitSpecQueue[specLowered++]));
|
|
2591
|
+
if (fn)
|
|
2592
|
+
instanceFunctions.push(fn);
|
|
2593
|
+
}
|
|
2594
|
+
catch (e) {
|
|
2595
|
+
if (!(e instanceof PoisonError))
|
|
2596
|
+
throw e;
|
|
2597
|
+
}
|
|
2255
2598
|
}
|
|
2256
2599
|
}
|
|
2257
2600
|
}
|
|
2258
2601
|
};
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2602
|
+
// Module inits are the unconditional root wave. Structure prefetch
|
|
2603
|
+
// already covered ordinary top-level expressions, but this full-root
|
|
2604
|
+
// pass also picks up nested/lifted function bodies and the class
|
|
2605
|
+
// declaration-time code that splitFiles hoists out of topStmts.
|
|
2606
|
+
this.checker.prefetchRoots([
|
|
2607
|
+
...parts.flatMap((fp) => fp.topStmts),
|
|
2608
|
+
...[...this.classes.values()].flatMap((info) => [
|
|
2609
|
+
...info.staticFields.map((field) => field.initializer),
|
|
2610
|
+
...(info.staticBlocks ?? []),
|
|
2611
|
+
...(info.classDecorators?.nodes ?? []),
|
|
2612
|
+
]),
|
|
2613
|
+
]);
|
|
2614
|
+
parts.forEach((fp, index) => {
|
|
2615
|
+
const priority = [2, index];
|
|
2616
|
+
const owner = demandOwner(priority);
|
|
2617
|
+
initFunctions.push(this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(priority, () => this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)))));
|
|
2262
2618
|
});
|
|
2263
2619
|
// LIBRARY mode's extra reachability roots (LowerOptions.libRoots): the
|
|
2264
2620
|
// profile-mapped exports are called from outside the graph, so they
|
|
@@ -2266,24 +2622,67 @@ export class Lowerer {
|
|
|
2266
2622
|
// (the export-map resolution reports them as SC4002 later).
|
|
2267
2623
|
for (const root of extraRoots ?? [])
|
|
2268
2624
|
this.onEdge?.(root);
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2625
|
+
const drainUnits = () => {
|
|
2626
|
+
while (queue.length > 0) {
|
|
2627
|
+
const wave = queue.splice(0);
|
|
2628
|
+
this.checker.prefetchRoots(wave.flatMap((name) => units.get(name).roots));
|
|
2629
|
+
for (const name of wave) {
|
|
2630
|
+
// A body-level poison outside the per-statement catches (a fenced
|
|
2631
|
+
// constructor/method parameter default lowered by declareParams):
|
|
2632
|
+
// every edge fired before poisoning remains retained; the diagnostic
|
|
2633
|
+
// stays recorded and the member stays omitted.
|
|
2634
|
+
try {
|
|
2635
|
+
const unit = units.get(name);
|
|
2636
|
+
const priority = metadataPriority.get(name) ?? [3, expressionMetadataOrder++];
|
|
2637
|
+
const owner = demandOwner(priority);
|
|
2638
|
+
const fn = this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(priority, unit.lower));
|
|
2639
|
+
if (fn)
|
|
2640
|
+
loweredUnits.set(name, fn);
|
|
2641
|
+
}
|
|
2642
|
+
catch (e) {
|
|
2643
|
+
if (!(e instanceof PoisonError))
|
|
2644
|
+
throw e;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2280
2647
|
}
|
|
2648
|
+
};
|
|
2649
|
+
drainUnits();
|
|
2650
|
+
this.restoreGenericInstanceOrder();
|
|
2651
|
+
this.restoreGenericClassInstanceOrder();
|
|
2652
|
+
// Generic bodies can reach ordinary declarations, whose bodies can in
|
|
2653
|
+
// turn queue more instances. Continue to the joint fixpoint; the initial
|
|
2654
|
+
// queue above is the only portion whose discovery order differed from
|
|
2655
|
+
// historical emit order.
|
|
2656
|
+
for (;;) {
|
|
2281
2657
|
drainInstances();
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2658
|
+
if (queue.length === 0)
|
|
2659
|
+
break;
|
|
2660
|
+
drainUnits();
|
|
2661
|
+
this.restoreGenericInstanceOrder(instLowered);
|
|
2662
|
+
this.restoreGenericClassInstanceOrder(clsInstLowered);
|
|
2663
|
+
}
|
|
2664
|
+
const orderedUnits = [...loweredUnits]
|
|
2665
|
+
.sort(([left], [right]) => units.get(left).order - units.get(right).order)
|
|
2666
|
+
.map(([, fn]) => fn);
|
|
2667
|
+
this.settleGenericDemandPriorities();
|
|
2668
|
+
this.shapes.settleDeclaredOrderPriorities();
|
|
2669
|
+
for (const finalize of this.shapeOrderMetadataFinalizers)
|
|
2670
|
+
finalize();
|
|
2671
|
+
for (const finalize of this.shapeOrderHelperFinalizers)
|
|
2672
|
+
finalize();
|
|
2673
|
+
const functions = [
|
|
2674
|
+
...orderedUnits,
|
|
2675
|
+
...initFunctions,
|
|
2676
|
+
this.buildMain(),
|
|
2677
|
+
...instanceFunctions,
|
|
2678
|
+
...this.liftedFns,
|
|
2679
|
+
...this.implicitFns,
|
|
2680
|
+
];
|
|
2681
|
+
this.reachableForArtifacts = reachable;
|
|
2682
|
+
return { reachable, result: this.finishModule(functions) };
|
|
2683
|
+
}
|
|
2684
|
+
/** Reachability hook (see emitReachable): fires when lowering resolves a
|
|
2685
|
+
* reference to a lowerable body. */
|
|
2287
2686
|
noteEdge(name) {
|
|
2288
2687
|
if (this.onEdge)
|
|
2289
2688
|
this.onEdge(name);
|
|
@@ -2458,7 +2857,7 @@ export class Lowerer {
|
|
|
2458
2857
|
// story through its callable arm. After the package check: a
|
|
2459
2858
|
// package-declared generic signature stays the package's story.
|
|
2460
2859
|
{
|
|
2461
|
-
const parts = widened.isUnionType() ?
|
|
2860
|
+
const parts = widened.isUnionType() ? ts.constituentTypes(widened) : [widened];
|
|
2462
2861
|
if (parts.some((p) => this.checker.getCallSignatures(p).some((s) => (s.typeParameters?.length ?? 0) > 0))) {
|
|
2463
2862
|
this.pushDiag(genericSignatureTypeDiag(this.checker.typeToString(type), locOf(node)));
|
|
2464
2863
|
throw new PoisonError();
|
|
@@ -2771,10 +3170,10 @@ export class Lowerer {
|
|
|
2771
3170
|
// when one constituent is; a union is array-proven only when EVERY
|
|
2772
3171
|
// arm is, so an ordinary `any[] | string` never qualifies.
|
|
2773
3172
|
if ((part.flags & ts.TypeFlags.Intersection) !== 0) {
|
|
2774
|
-
return
|
|
3173
|
+
return ts.constituentTypes(part).some(visit);
|
|
2775
3174
|
}
|
|
2776
3175
|
if (part.isUnionType())
|
|
2777
|
-
return
|
|
3176
|
+
return ts.constituentTypes(part).every(visit);
|
|
2778
3177
|
return false;
|
|
2779
3178
|
};
|
|
2780
3179
|
return visit(t);
|
|
@@ -5727,7 +6126,7 @@ export class Lowerer {
|
|
|
5727
6126
|
const t = this.typeOf(d.initializer);
|
|
5728
6127
|
if (!isUnitOnlyTsType(t))
|
|
5729
6128
|
return null;
|
|
5730
|
-
for (const p of t.isUnionType() ?
|
|
6129
|
+
for (const p of t.isUnionType() ? ts.constituentTypes(t) : [t]) {
|
|
5731
6130
|
units.add((p.flags & ts.TypeFlags.Null) !== 0 ? "null" : "undefined");
|
|
5732
6131
|
}
|
|
5733
6132
|
}
|
|
@@ -6091,10 +6490,83 @@ export class Lowerer {
|
|
|
6091
6490
|
return lowerGenericInstance(this, info, inst);
|
|
6092
6491
|
}
|
|
6093
6492
|
/* ── classes ──────────────────────────────────────────────────────── */
|
|
6493
|
+
/** Checker work class SHAPE collection performs inside otherwise
|
|
6494
|
+
* deferred JavaScript bodies. Constructor assignments declare fields,
|
|
6495
|
+
* so collection asks for each `this.x` symbol and RHS type even when the
|
|
6496
|
+
* constructor is unreachable. Keep that mandatory work batched without
|
|
6497
|
+
* sweeping unrelated dead method bodies. */
|
|
6498
|
+
prefetchClassCollection(decls) {
|
|
6499
|
+
const defaultTypeNodes = [];
|
|
6500
|
+
const typeNodes = [];
|
|
6501
|
+
const symbolRoots = [];
|
|
6502
|
+
for (const decl of decls) {
|
|
6503
|
+
for (const member of decl.members) {
|
|
6504
|
+
if ((ts.isConstructorDeclaration(member) ||
|
|
6505
|
+
ts.isMethodDeclaration(member) ||
|
|
6506
|
+
ts.isGetAccessor(member) ||
|
|
6507
|
+
ts.isSetAccessor(member)) &&
|
|
6508
|
+
(!ts.isMethodDeclaration(member) || member.typeParameters === undefined)) {
|
|
6509
|
+
for (const param of member.parameters) {
|
|
6510
|
+
if (param.initializer)
|
|
6511
|
+
defaultTypeNodes.push(param.initializer);
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
}
|
|
6515
|
+
if (!isJsSourceFile(decl.getSourceFile()))
|
|
6516
|
+
continue;
|
|
6517
|
+
for (const member of decl.members) {
|
|
6518
|
+
if (ts.isConstructorDeclaration(member)) {
|
|
6519
|
+
for (const param of member.parameters) {
|
|
6520
|
+
if (param.initializer)
|
|
6521
|
+
typeNodes.push(param.initializer);
|
|
6522
|
+
}
|
|
6523
|
+
for (const stmt of member.body?.statements ?? []) {
|
|
6524
|
+
if (!ts.isExpressionStatement(stmt) || !ts.isBinaryExpression(stmt.expression))
|
|
6525
|
+
continue;
|
|
6526
|
+
if (stmt.expression.operatorToken.kind !== ts.SyntaxKind.EqualsToken)
|
|
6527
|
+
continue;
|
|
6528
|
+
const lhs = stmt.expression.left;
|
|
6529
|
+
if ((ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) &&
|
|
6530
|
+
lhs.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
6531
|
+
symbolRoots.push(lhs);
|
|
6532
|
+
// Field inference normally uses the symbol's type, but its
|
|
6533
|
+
// panic/undefined fallback asks for the assignment node too.
|
|
6534
|
+
typeNodes.push(lhs, stmt.expression.right);
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6537
|
+
continue;
|
|
6538
|
+
}
|
|
6539
|
+
if (ts.isMethodDeclaration(member) ||
|
|
6540
|
+
ts.isGetAccessor(member) ||
|
|
6541
|
+
ts.isSetAccessor(member)) {
|
|
6542
|
+
for (const param of member.parameters) {
|
|
6543
|
+
if (param.initializer)
|
|
6544
|
+
typeNodes.push(param.initializer);
|
|
6545
|
+
}
|
|
6546
|
+
// npm-static implicit-any classification resolves body parameter
|
|
6547
|
+
// references while collecting the method's shape.
|
|
6548
|
+
if (implicitMonoFile(decl.getSourceFile()) && member.body) {
|
|
6549
|
+
symbolRoots.push(member.body);
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
if (defaultTypeNodes.length > 0) {
|
|
6555
|
+
this.checker.prefetchCollectionTypes(defaultTypeNodes);
|
|
6556
|
+
}
|
|
6557
|
+
if (typeNodes.length > 0 || symbolRoots.length > 0) {
|
|
6558
|
+
this.checker.prefetchClassCollection(typeNodes, symbolRoots);
|
|
6559
|
+
}
|
|
6560
|
+
}
|
|
6094
6561
|
collectClassShape(decl) {
|
|
6095
6562
|
return collectClassShape(this, decl);
|
|
6096
6563
|
}
|
|
6097
6564
|
collectClassShapeInner(decl, jsNameOverride, inst, mixin) {
|
|
6565
|
+
// Late class expressions and mixin/generic instances do not participate
|
|
6566
|
+
// in emitReachable's initial declaration wave. Prime their mandatory
|
|
6567
|
+
// shape queries at the collection boundary; initial declarations are
|
|
6568
|
+
// already warm and this is memo-free.
|
|
6569
|
+
this.prefetchClassCollection([decl]);
|
|
6098
6570
|
return collectClassShapeInner(this, decl, jsNameOverride, inst, mixin);
|
|
6099
6571
|
}
|
|
6100
6572
|
lowerClassExpressionInfo(expr) {
|
|
@@ -6746,7 +7218,7 @@ export class Lowerer {
|
|
|
6746
7218
|
}
|
|
6747
7219
|
if (d && ts.isVariableDeclaration(d) && d.initializer === undefined) {
|
|
6748
7220
|
const t = this.checker.getTypeOfSymbol(symbol);
|
|
6749
|
-
const parts = t.isUnionType() ?
|
|
7221
|
+
const parts = t.isUnionType() ? ts.constituentTypes(t) : [t];
|
|
6750
7222
|
if (parts.some((p) => this.checker.getCallSignatures(p).some((s) => (s.typeParameters?.length ?? 0) > 0))) {
|
|
6751
7223
|
this.unsupported("SC1030", node, `the generic-signature binding '${name}' (its type keeps type parameters and the declaration has no initializer — no function body exists to monomorphize, so nothing can pin a concrete signature)`);
|
|
6752
7224
|
}
|