@scriptc/compiler 0.0.32 → 0.0.34
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 +430 -1
- package/dist/backend/cc-cache.test.js.map +1 -1
- package/dist/backend/cc.d.ts +25 -0
- package/dist/backend/cc.js +818 -88
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +79 -3
- 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 +23 -7
- package/dist/backend/emission/emitter.js +171 -27
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +7 -4
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/ffi-callbacks.d.ts +38 -1
- package/dist/backend/ffi-callbacks.js +76 -3
- package/dist/backend/ffi-callbacks.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 +278 -17
- 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/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 +37 -13
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -0
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/ffi/profile.d.ts +17 -5
- package/dist/ffi/profile.js +169 -22
- 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 +2 -8
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +1 -0
- package/dist/frontend/lowering/lower-calls.js +244 -163
- 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 +113 -28
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +67 -1
- 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-modules.js +1 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +48 -3
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +79 -29
- package/dist/frontend/lowering/lowerer.js +429 -87
- package/dist/frontend/lowering/lowerer.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 +2 -6
- 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 +2 -0
- package/dist/frontend/ts7/checker.js +20 -3
- 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 +41 -0
- package/dist/frontend/types.js +147 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +275 -41
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +42 -12
- package/dist/ir/nodes.js +7 -1
- 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 +104 -1
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/early-cache.d.ts +65 -0
- package/dist/library/early-cache.js +380 -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/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/profile.d.ts +3 -0
- package/dist/library/profile.js +9 -1
- package/dist/library/profile.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/package.json +2 -2
- package/surface-manifest.json +1 -1
|
@@ -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,
|
|
@@ -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
|
|
@@ -700,6 +892,16 @@ export class Lowerer {
|
|
|
700
892
|
/** Union re-tag helpers (%union.retag.N), interned per (from, to)
|
|
701
893
|
* unionId pair — see unionRetagHelper. */
|
|
702
894
|
retagHelpers = new Map();
|
|
895
|
+
/** Callee names of every interned coercion helper whose CALL mints a
|
|
896
|
+
* FRESH closure per evaluation (%fn.width.*, %fn.adapt.*,
|
|
897
|
+
* %fnval.spawnres.*). Registered at the mint site — NOT recovered by
|
|
898
|
+
* name-prefix matching — because retained-FFI release identity is the
|
|
899
|
+
* runtime closure pointer: a coercion adapter allocates a different
|
|
900
|
+
* closure at the registration and release sites, so lowerFfiCall must
|
|
901
|
+
* refuse these forms at compile time (SC5003). Any new closure-minting
|
|
902
|
+
* adapter helper MUST add its name here, or the identity guard silently
|
|
903
|
+
* reopens and the mismatch surfaces as a runtime release trap instead. */
|
|
904
|
+
freshClosureAdapters = new Set();
|
|
703
905
|
/** Symbols bound by `const x = promisify(execFile)` — the one lowered
|
|
704
906
|
* util.promisify shape. Declarations register here and emit nothing;
|
|
705
907
|
* calls through the binding lower (lowerExecFileAsyncCall) and value
|
|
@@ -875,6 +1077,8 @@ export class Lowerer {
|
|
|
875
1077
|
* IrModule.unions. An arm's index in the canonical list is its runtime
|
|
876
1078
|
* tag. */
|
|
877
1079
|
unions = new UnionRegistry();
|
|
1080
|
+
/** Context-free successful type mappings. See TypeMapperCtx.typeMemo. */
|
|
1081
|
+
typeMemo = new Map();
|
|
878
1082
|
ambient = ambientDtsPath();
|
|
879
1083
|
overridesAmbient = overridesDtsPath();
|
|
880
1084
|
fallbackAmbient = fallbackDtsPath();
|
|
@@ -888,7 +1092,7 @@ export class Lowerer {
|
|
|
888
1092
|
statementsIsland: 0,
|
|
889
1093
|
functionsSkipped: 0,
|
|
890
1094
|
};
|
|
891
|
-
/**
|
|
1095
|
+
/** Reachability edge sink: every resolution of
|
|
892
1096
|
* a reference to a lowerable body reports its name here — recorded even
|
|
893
1097
|
* when the enclosing statement later poisons. */
|
|
894
1098
|
onEdge = null;
|
|
@@ -932,9 +1136,12 @@ export class Lowerer {
|
|
|
932
1136
|
get scopes() {
|
|
933
1137
|
return this.ctx.scopes;
|
|
934
1138
|
}
|
|
935
|
-
/** Names of bodies
|
|
936
|
-
* (the discovery pass itself). */
|
|
1139
|
+
/** Names of bodies a prior reachability pass reached; null lowers everything. */
|
|
937
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;
|
|
938
1145
|
/** Coverage remainder mode: the reachability gate inverts (see wantBody)
|
|
939
1146
|
* and no module is built. */
|
|
940
1147
|
remainder;
|
|
@@ -1038,6 +1245,10 @@ export class Lowerer {
|
|
|
1038
1245
|
isNpmFile: this.isNpmFile,
|
|
1039
1246
|
isExternalTypeFile: (sf) => this.externalTypeSpecifiersByFile.has(tsgoPath(resolve(sf.fileName))),
|
|
1040
1247
|
dynamic: this.dynamic,
|
|
1248
|
+
typeMemo: this.typeMemo,
|
|
1249
|
+
canMemoizeType: () => this.typeParamBindings === null &&
|
|
1250
|
+
this.typeParamTsBindings === null &&
|
|
1251
|
+
this.mixinTypeContext === null,
|
|
1041
1252
|
// fileTag is filled just below; the hook is only ever CALLED during
|
|
1042
1253
|
// lowering, long after the constructor completes.
|
|
1043
1254
|
isProgramFile: (sf) => this.fileTag.has(sf),
|
|
@@ -1045,9 +1256,7 @@ export class Lowerer {
|
|
|
1045
1256
|
// --dynamic: modules reachable only through dynamic import() joined
|
|
1046
1257
|
// moduleOrder BEFORE any pass constructed — lowerToIr runs
|
|
1047
1258
|
// appendDynamicImportModules once on the shared array (a per-pass run
|
|
1048
|
-
// here
|
|
1049
|
-
// diagnostics are discarded by design, and the extended order left
|
|
1050
|
-
// nothing for the emit pass to re-detect).
|
|
1259
|
+
// here would repeatedly extend the graph and duplicate cycle reports).
|
|
1051
1260
|
this.moduleOrder.forEach((sf, i) => {
|
|
1052
1261
|
this.fileTag.set(sf, sf === entry ? "" : `%m${i}.`);
|
|
1053
1262
|
});
|
|
@@ -1078,7 +1287,7 @@ export class Lowerer {
|
|
|
1078
1287
|
* namespace path (nsPathPrefix), so `namespace A { export class C }`
|
|
1079
1288
|
* and a top-level `class C` never collide. Class EXPRESSIONS name by
|
|
1080
1289
|
* SOURCE POSITION (`%cx<start>.<name>`): deterministic across the
|
|
1081
|
-
*
|
|
1290
|
+
* builds (no counter can drift between invocations),
|
|
1082
1291
|
* program-unique through the file qualifier, and collision-free with
|
|
1083
1292
|
* user identifiers ('%'). */
|
|
1084
1293
|
classNamer = (decl) => ts.isClassExpression(decl)
|
|
@@ -1760,8 +1969,8 @@ export class Lowerer {
|
|
|
1760
1969
|
loc,
|
|
1761
1970
|
};
|
|
1762
1971
|
}
|
|
1763
|
-
/** True when this body should lower: everything with no reachable set
|
|
1764
|
-
*
|
|
1972
|
+
/** True when this body should lower: everything with no reachable set,
|
|
1973
|
+
* the marked bodies in an externally-gated pass, and exactly the
|
|
1765
1974
|
* UNMARKED bodies in the coverage remainder. */
|
|
1766
1975
|
wantBody(name) {
|
|
1767
1976
|
if (this.reachable === null)
|
|
@@ -1826,7 +2035,7 @@ export class Lowerer {
|
|
|
1826
2035
|
// inline require statements call theirs mid-body, and the guards make
|
|
1827
2036
|
// revisits cache hits — Node's evaluation order over the WHOLE graph
|
|
1828
2037
|
// falls out of the nesting. The coverage remainder skips them — they
|
|
1829
|
-
// are reachable by definition, already counted by
|
|
2038
|
+
// are reachable by definition, already counted by reachable emit.
|
|
1830
2039
|
if (!this.remainder) {
|
|
1831
2040
|
for (const fp of parts) {
|
|
1832
2041
|
functions.push(this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)));
|
|
@@ -1915,6 +2124,11 @@ export class Lowerer {
|
|
|
1915
2124
|
...(this.npmLazyTraps ? { npmLazyTraps: this.npmLazyTraps } : {}),
|
|
1916
2125
|
};
|
|
1917
2126
|
}
|
|
2127
|
+
return this.finishModule(functions);
|
|
2128
|
+
}
|
|
2129
|
+
/** Final retention, pruning, and module assembly shared by ordinary emit
|
|
2130
|
+
* and the retained reachability worklist. */
|
|
2131
|
+
finishModule(functions) {
|
|
1918
2132
|
// Globals typed by a class that never REGISTERED (a JS class whose
|
|
1919
2133
|
// collection fenced — Symbol-keyed fields, an unsupported base): the
|
|
1920
2134
|
// declaration statement and every use compiled to runtime fences, but
|
|
@@ -2000,7 +2214,7 @@ export class Lowerer {
|
|
|
2000
2214
|
const module = this.diags.length > 0
|
|
2001
2215
|
? null
|
|
2002
2216
|
: {
|
|
2003
|
-
irVersion:
|
|
2217
|
+
irVersion: 6,
|
|
2004
2218
|
sourceFile: this.entry.fileName,
|
|
2005
2219
|
functions,
|
|
2006
2220
|
classes: artifacts.classes,
|
|
@@ -2104,15 +2318,16 @@ export class Lowerer {
|
|
|
2104
2318
|
}
|
|
2105
2319
|
/** Whether run() counts a signature-blocked declaration in
|
|
2106
2320
|
* stats.functionsSkipped: whole-program passes and the coverage
|
|
2107
|
-
* remainder do;
|
|
2321
|
+
* remainder do; an externally-gated emit pass leaves the counting to the
|
|
2108
2322
|
* remainder (the declaration was never reached). */
|
|
2109
2323
|
countsSkips() {
|
|
2110
2324
|
return this.reachable === null || this.remainder;
|
|
2111
2325
|
}
|
|
2112
2326
|
/* ── reachability ─────────────────────────────────────────────────── */
|
|
2113
|
-
/**
|
|
2114
|
-
* reaches. Seeds are the per-file
|
|
2115
|
-
* run); edges fire from
|
|
2327
|
+
/** Reachable emit: computes the set of body names the program's entry
|
|
2328
|
+
* reaches and retains each body IR as it lowers. Seeds are the per-file
|
|
2329
|
+
* init bodies (top-level statements always run); edges fire from
|
|
2330
|
+
* RESOLUTION sites while a body lowers (noteEdge /
|
|
2116
2331
|
* noteVirtualEdge) — direct calls, closure creation (a taken closure may
|
|
2117
2332
|
* be called indirectly), `new`, super calls, accessor invocations, and
|
|
2118
2333
|
* virtual dispatch. Recording at resolution time (not off the produced
|
|
@@ -2122,19 +2337,20 @@ export class Lowerer {
|
|
|
2122
2337
|
* demand-driven) and lifted lambdas lower inline with their enclosing
|
|
2123
2338
|
* body; both fire edges through the same hooks and are not units
|
|
2124
2339
|
* themselves. */
|
|
2125
|
-
|
|
2340
|
+
emitReachable(extraRoots) {
|
|
2126
2341
|
const parts = this.splitFiles();
|
|
2127
2342
|
this.collectProgram(parts);
|
|
2128
2343
|
// Decorated classes analyze post-collection here too: the %init seeds
|
|
2129
2344
|
// lower the decoration calls, whose edges (decorator bodies, construct
|
|
2130
|
-
// thunks)
|
|
2345
|
+
// thunks) reachable emit must see.
|
|
2131
2346
|
for (const info of this.classes.values())
|
|
2132
2347
|
analyzeClassDecoration(this, info);
|
|
2133
2348
|
this.prepareModuleInits(parts);
|
|
2134
2349
|
// Every lowerable body, by emitted-function name. The names double as
|
|
2135
|
-
//
|
|
2136
|
-
//
|
|
2350
|
+
// retained-function keys and are deterministic by construction
|
|
2351
|
+
// (qualified declaration names).
|
|
2137
2352
|
const units = new Map();
|
|
2353
|
+
let unitOrder = 0;
|
|
2138
2354
|
for (const fp of parts) {
|
|
2139
2355
|
for (const decl of fp.fnDecls) {
|
|
2140
2356
|
// Overload signatures share the implementation's symbol (and so
|
|
@@ -2147,7 +2363,7 @@ export class Lowerer {
|
|
|
2147
2363
|
continue;
|
|
2148
2364
|
const sig = this.fnSigsBySymbol.get(declSymbol);
|
|
2149
2365
|
if (sig)
|
|
2150
|
-
units.set(sig.name, () => this.lowerFunction(decl));
|
|
2366
|
+
units.set(sig.name, { order: unitOrder++, lower: () => this.lowerFunction(decl) });
|
|
2151
2367
|
}
|
|
2152
2368
|
}
|
|
2153
2369
|
for (const info of this.classes.values()) {
|
|
@@ -2162,20 +2378,77 @@ export class Lowerer {
|
|
|
2162
2378
|
// A FAMILY has no constructor function and no instance members —
|
|
2163
2379
|
// only its statics are units.
|
|
2164
2380
|
if (!info.generic) {
|
|
2165
|
-
units.set(`%${cName}.constructor`, () => this.lowerClassCtor(info));
|
|
2381
|
+
units.set(`%${cName}.constructor`, { order: unitOrder++, lower: () => this.lowerClassCtor(info) });
|
|
2166
2382
|
for (const { mName, member } of this.classMethodMembers(info)) {
|
|
2167
|
-
units.set(`%${cName}.${mName}`, () => this.lowerClassMethodMember(info, member));
|
|
2383
|
+
units.set(`%${cName}.${mName}`, { order: unitOrder++, lower: () => this.lowerClassMethodMember(info, member) });
|
|
2168
2384
|
}
|
|
2169
2385
|
for (const prop of info.throwingSetters) {
|
|
2170
|
-
units.set(`%${cName}.set:${prop}`, () => this.throwingSetterFn(info, prop));
|
|
2386
|
+
units.set(`%${cName}.set:${prop}`, { order: unitOrder++, lower: () => this.throwingSetterFn(info, prop) });
|
|
2171
2387
|
}
|
|
2172
2388
|
}
|
|
2173
2389
|
for (const name of info.staticMethods?.keys() ?? []) {
|
|
2174
|
-
units.set(`%${cName}.static:${name}`, () => lowerStaticMethod(this, info, name));
|
|
2390
|
+
units.set(`%${cName}.static:${name}`, { order: unitOrder++, lower: () => lowerStaticMethod(this, info, name) });
|
|
2175
2391
|
}
|
|
2176
2392
|
}
|
|
2393
|
+
// The old emit pass visited each file's functions and then its classes,
|
|
2394
|
+
// before every module init. Retained reachability discovers those
|
|
2395
|
+
// bodies from the inits, but first-seen record metadata still has to
|
|
2396
|
+
// follow that old order: Object.keys/JSON/inspect observe a shape's
|
|
2397
|
+
// declaredOrder. Keep output sorting separate — this rank controls only
|
|
2398
|
+
// that observable metadata.
|
|
2399
|
+
const metadataPriority = new Map();
|
|
2400
|
+
let declarationMetadataOrder = 0;
|
|
2401
|
+
const rank = (name) => {
|
|
2402
|
+
if (units.has(name) && !metadataPriority.has(name)) {
|
|
2403
|
+
metadataPriority.set(name, [1, declarationMetadataOrder++]);
|
|
2404
|
+
}
|
|
2405
|
+
};
|
|
2406
|
+
for (const fp of parts) {
|
|
2407
|
+
for (const decl of fp.fnDecls) {
|
|
2408
|
+
if (!decl.body)
|
|
2409
|
+
continue;
|
|
2410
|
+
const symbol = declSymbolOf(this, decl);
|
|
2411
|
+
if (symbol && !this.genericFnsBySymbol.has(symbol)) {
|
|
2412
|
+
const sig = this.fnSigsBySymbol.get(symbol);
|
|
2413
|
+
if (sig)
|
|
2414
|
+
rank(sig.name);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
for (const decl of fp.classDecls) {
|
|
2418
|
+
const info = this.classes.get(this.classNamer(decl));
|
|
2419
|
+
if (!info)
|
|
2420
|
+
continue;
|
|
2421
|
+
const cName = info.def.name;
|
|
2422
|
+
rank(`%${cName}.constructor`);
|
|
2423
|
+
for (const { mName } of this.classMethodMembers(info))
|
|
2424
|
+
rank(`%${cName}.${mName}`);
|
|
2425
|
+
for (const name of info.staticMethods?.keys() ?? [])
|
|
2426
|
+
rank(`%${cName}.static:${name}`);
|
|
2427
|
+
for (const prop of info.throwingSetters)
|
|
2428
|
+
rank(`%${cName}.set:${prop}`);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
// Defensive fallback for declaration-like units registered outside
|
|
2432
|
+
// FileParts; they still precede init bodies in the old emit pass.
|
|
2433
|
+
for (const name of units.keys())
|
|
2434
|
+
rank(name);
|
|
2435
|
+
let expressionMetadataOrder = 0;
|
|
2436
|
+
let instanceMetadataOrder = 0;
|
|
2437
|
+
const demandOwner = (priority) => {
|
|
2438
|
+
const owner = {
|
|
2439
|
+
...(priority ? { priority } : {}),
|
|
2440
|
+
functionDemands: [],
|
|
2441
|
+
classDemands: [],
|
|
2442
|
+
};
|
|
2443
|
+
if (priority)
|
|
2444
|
+
this.genericDemandRoots.push(owner);
|
|
2445
|
+
return owner;
|
|
2446
|
+
};
|
|
2177
2447
|
const reachable = new Set();
|
|
2178
2448
|
const queue = [];
|
|
2449
|
+
const loweredUnits = new Map();
|
|
2450
|
+
const initFunctions = [];
|
|
2451
|
+
const instanceFunctions = [];
|
|
2179
2452
|
this.onEdge = (name) => {
|
|
2180
2453
|
if (reachable.has(name))
|
|
2181
2454
|
return;
|
|
@@ -2188,15 +2461,19 @@ export class Lowerer {
|
|
|
2188
2461
|
// edge to them can fire (references require the collected class).
|
|
2189
2462
|
this.onExprClassCollected = (info) => {
|
|
2190
2463
|
const cName = info.def.name;
|
|
2191
|
-
|
|
2464
|
+
const register = (name, lower) => {
|
|
2465
|
+
units.set(name, { order: unitOrder++, lower });
|
|
2466
|
+
metadataPriority.set(name, [3, expressionMetadataOrder++]);
|
|
2467
|
+
};
|
|
2468
|
+
register(`%${cName}.constructor`, () => this.lowerClassCtor(info));
|
|
2192
2469
|
for (const { mName, member } of this.classMethodMembers(info)) {
|
|
2193
|
-
|
|
2470
|
+
register(`%${cName}.${mName}`, () => this.lowerClassMethodMember(info, member));
|
|
2194
2471
|
}
|
|
2195
2472
|
for (const name of info.staticMethods?.keys() ?? []) {
|
|
2196
|
-
|
|
2473
|
+
register(`%${cName}.static:${name}`, () => lowerStaticMethod(this, info, name));
|
|
2197
2474
|
}
|
|
2198
2475
|
for (const prop of info.throwingSetters) {
|
|
2199
|
-
|
|
2476
|
+
register(`%${cName}.set:${prop}`, () => this.throwingSetterFn(info, prop));
|
|
2200
2477
|
}
|
|
2201
2478
|
};
|
|
2202
2479
|
// Generic instances queued by the bodies above lower here (an instance
|
|
@@ -2210,28 +2487,38 @@ export class Lowerer {
|
|
|
2210
2487
|
while (instLowered < this.instantiationQueue.length ||
|
|
2211
2488
|
clsInstLowered < this.genericClassInstances.length ||
|
|
2212
2489
|
specLowered < this.emitSpecQueue.length) {
|
|
2490
|
+
while (clsInstLowered < this.genericClassInstances.length) {
|
|
2491
|
+
const info = this.genericClassInstances[clsInstLowered++];
|
|
2492
|
+
const owner = demandOwner();
|
|
2493
|
+
this.genericClassDemandOwner.set(info, owner);
|
|
2494
|
+
const ref = this.genericClassDemandPriority.get(info) ?? { rank: [4, instanceMetadataOrder++] };
|
|
2495
|
+
this.genericClassDemandPriority.set(info, ref);
|
|
2496
|
+
instanceFunctions.push(...this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(ref, () => this.lowerClassMembers(info))));
|
|
2497
|
+
}
|
|
2213
2498
|
while (instLowered < this.instantiationQueue.length) {
|
|
2214
2499
|
const { info, inst } = this.instantiationQueue[instLowered++];
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2500
|
+
const owner = demandOwner();
|
|
2501
|
+
this.genericFunctionDemandOwner.set(inst, owner);
|
|
2502
|
+
const ref = this.genericDemandPriority.get(inst) ?? { rank: [4, instanceMetadataOrder++] };
|
|
2503
|
+
this.genericDemandPriority.set(inst, ref);
|
|
2504
|
+
// Body-level poisons skip the instance after retaining the
|
|
2505
|
+
// diagnostic and every edge fired before poisoning.
|
|
2218
2506
|
try {
|
|
2219
|
-
this.lowerGenericInstance(info, inst);
|
|
2507
|
+
instanceFunctions.push(this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(ref, () => this.lowerGenericInstance(info, inst))));
|
|
2220
2508
|
}
|
|
2221
2509
|
catch (e) {
|
|
2222
2510
|
if (!(e instanceof PoisonError))
|
|
2223
2511
|
throw e;
|
|
2224
2512
|
}
|
|
2225
2513
|
}
|
|
2226
|
-
while (clsInstLowered < this.genericClassInstances.length) {
|
|
2227
|
-
this.lowerClassMembers(this.genericClassInstances[clsInstLowered++]);
|
|
2228
|
-
}
|
|
2229
2514
|
// Emit-override specialization bodies fire edges of their own
|
|
2230
2515
|
// (the super-forward chain, closures, generic calls) — lower them
|
|
2231
|
-
//
|
|
2516
|
+
// exactly like generic instances.
|
|
2232
2517
|
while (specLowered < this.emitSpecQueue.length) {
|
|
2233
2518
|
try {
|
|
2234
|
-
lowerEmitOverrideSpec(this, this.emitSpecQueue[specLowered++]);
|
|
2519
|
+
const fn = this.shapes.withDeclaredOrderPriority([4, instanceMetadataOrder++], () => lowerEmitOverrideSpec(this, this.emitSpecQueue[specLowered++]));
|
|
2520
|
+
if (fn)
|
|
2521
|
+
instanceFunctions.push(fn);
|
|
2235
2522
|
}
|
|
2236
2523
|
catch (e) {
|
|
2237
2524
|
if (!(e instanceof PoisonError))
|
|
@@ -2240,9 +2527,10 @@ export class Lowerer {
|
|
|
2240
2527
|
}
|
|
2241
2528
|
}
|
|
2242
2529
|
};
|
|
2243
|
-
parts.forEach((fp) => {
|
|
2244
|
-
|
|
2245
|
-
|
|
2530
|
+
parts.forEach((fp, index) => {
|
|
2531
|
+
const priority = [2, index];
|
|
2532
|
+
const owner = demandOwner(priority);
|
|
2533
|
+
initFunctions.push(this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(priority, () => this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)))));
|
|
2246
2534
|
});
|
|
2247
2535
|
// LIBRARY mode's extra reachability roots (LowerOptions.libRoots): the
|
|
2248
2536
|
// profile-mapped exports are called from outside the graph, so they
|
|
@@ -2253,21 +2541,72 @@ export class Lowerer {
|
|
|
2253
2541
|
while (queue.length > 0) {
|
|
2254
2542
|
// A body-level poison outside the per-statement catches (a fenced
|
|
2255
2543
|
// constructor/method parameter default lowered by declareParams):
|
|
2256
|
-
//
|
|
2257
|
-
//
|
|
2544
|
+
// every edge fired before poisoning remains retained; the diagnostic
|
|
2545
|
+
// stays recorded and the member stays omitted.
|
|
2258
2546
|
try {
|
|
2259
|
-
|
|
2547
|
+
const name = queue.shift();
|
|
2548
|
+
const unit = units.get(name);
|
|
2549
|
+
const priority = metadataPriority.get(name) ?? [3, expressionMetadataOrder++];
|
|
2550
|
+
const owner = demandOwner(priority);
|
|
2551
|
+
const fn = this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(priority, unit.lower));
|
|
2552
|
+
if (fn)
|
|
2553
|
+
loweredUnits.set(name, fn);
|
|
2260
2554
|
}
|
|
2261
2555
|
catch (e) {
|
|
2262
2556
|
if (!(e instanceof PoisonError))
|
|
2263
2557
|
throw e;
|
|
2264
2558
|
}
|
|
2265
|
-
drainInstances();
|
|
2266
2559
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2560
|
+
this.restoreGenericInstanceOrder();
|
|
2561
|
+
this.restoreGenericClassInstanceOrder();
|
|
2562
|
+
// Generic bodies can reach ordinary declarations, whose bodies can in
|
|
2563
|
+
// turn queue more instances. Continue to the joint fixpoint; the initial
|
|
2564
|
+
// queue above is the only portion whose discovery order differed from
|
|
2565
|
+
// historical emit order.
|
|
2566
|
+
for (;;) {
|
|
2567
|
+
drainInstances();
|
|
2568
|
+
if (queue.length === 0)
|
|
2569
|
+
break;
|
|
2570
|
+
while (queue.length > 0) {
|
|
2571
|
+
try {
|
|
2572
|
+
const name = queue.shift();
|
|
2573
|
+
const unit = units.get(name);
|
|
2574
|
+
const priority = metadataPriority.get(name) ?? [3, expressionMetadataOrder++];
|
|
2575
|
+
const owner = demandOwner(priority);
|
|
2576
|
+
const fn = this.withGenericDemandOwner(owner, () => this.shapes.withDeclaredOrderPriority(priority, unit.lower));
|
|
2577
|
+
if (fn)
|
|
2578
|
+
loweredUnits.set(name, fn);
|
|
2579
|
+
}
|
|
2580
|
+
catch (e) {
|
|
2581
|
+
if (!(e instanceof PoisonError))
|
|
2582
|
+
throw e;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
this.restoreGenericInstanceOrder(instLowered);
|
|
2586
|
+
this.restoreGenericClassInstanceOrder(clsInstLowered);
|
|
2587
|
+
}
|
|
2588
|
+
const orderedUnits = [...loweredUnits]
|
|
2589
|
+
.sort(([left], [right]) => units.get(left).order - units.get(right).order)
|
|
2590
|
+
.map(([, fn]) => fn);
|
|
2591
|
+
this.settleGenericDemandPriorities();
|
|
2592
|
+
this.shapes.settleDeclaredOrderPriorities();
|
|
2593
|
+
for (const finalize of this.shapeOrderMetadataFinalizers)
|
|
2594
|
+
finalize();
|
|
2595
|
+
for (const finalize of this.shapeOrderHelperFinalizers)
|
|
2596
|
+
finalize();
|
|
2597
|
+
const functions = [
|
|
2598
|
+
...orderedUnits,
|
|
2599
|
+
...initFunctions,
|
|
2600
|
+
this.buildMain(),
|
|
2601
|
+
...instanceFunctions,
|
|
2602
|
+
...this.liftedFns,
|
|
2603
|
+
...this.implicitFns,
|
|
2604
|
+
];
|
|
2605
|
+
this.reachableForArtifacts = reachable;
|
|
2606
|
+
return { reachable, result: this.finishModule(functions) };
|
|
2607
|
+
}
|
|
2608
|
+
/** Reachability hook (see emitReachable): fires when lowering resolves a
|
|
2609
|
+
* reference to a lowerable body. */
|
|
2271
2610
|
noteEdge(name) {
|
|
2272
2611
|
if (this.onEdge)
|
|
2273
2612
|
this.onEdge(name);
|
|
@@ -4368,6 +4707,7 @@ export class Lowerer {
|
|
|
4368
4707
|
return existing;
|
|
4369
4708
|
const name = `%fn.width.${this.widthHelpers.size}`;
|
|
4370
4709
|
this.widthHelpers.set(key, name);
|
|
4710
|
+
this.freshClosureAdapters.add(name); // wraps `f` in a new closure per call
|
|
4371
4711
|
const impl = `${name}.impl`;
|
|
4372
4712
|
// The returned closure's body: call the captured original, width-map.
|
|
4373
4713
|
this.liftedFns.push({
|
|
@@ -4545,6 +4885,7 @@ export class Lowerer {
|
|
|
4545
4885
|
return existing;
|
|
4546
4886
|
const name = `%fn.adapt.${this.retagHelpers.size}`;
|
|
4547
4887
|
this.retagHelpers.set(key, name);
|
|
4888
|
+
this.freshClosureAdapters.add(name); // wraps `f` in a new closure per call
|
|
4548
4889
|
const impl = `${name}.impl`;
|
|
4549
4890
|
const params = toT.params.map((t, i) => ({ localId: `a.${i}`, name: `a${i}`, type: t }));
|
|
4550
4891
|
const strandThrow = (why) => ({
|
|
@@ -4715,6 +5056,7 @@ export class Lowerer {
|
|
|
4715
5056
|
return existing;
|
|
4716
5057
|
const name = `%fnval.spawnres.${this.widthHelpers.size}`;
|
|
4717
5058
|
this.widthHelpers.set(key, name);
|
|
5059
|
+
this.freshClosureAdapters.add(name); // wraps `f` in a new closure per call
|
|
4718
5060
|
const impl = `${name}.impl`;
|
|
4719
5061
|
const params = toT.params.map((p, i) => ({ localId: `p${i}.0`, name: `p${i}`, type: p }));
|
|
4720
5062
|
const rRef = { kind: "varRef", localId: "r.0", type: fromT.ret, loc };
|