@scriptc/compiler 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +4 -4
- package/dist/backend/cc.d.ts +5 -0
- package/dist/backend/cc.js +2 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.js +35 -2
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +40 -4
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emitter.js +69 -11
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.d.ts +3 -3
- package/dist/backend/emission/may-throw.js +15 -4
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/llvm/emitter.js +199 -21
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.js +4 -7
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +17 -9
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +22 -17
- package/dist/frontend/lowering/lower-containers.js +628 -51
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +19 -1
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +55 -7
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +144 -10
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +8 -0
- package/dist/frontend/lowering/lower-stmts.js +408 -21
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +38 -0
- package/dist/frontend/lowering/lowerer.js +149 -2
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +4 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +46 -12
- package/dist/ir/nodes.js +41 -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 +98 -39
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/int-infer.js +191 -25
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +206 -1
- package/dist/library/int-infer.test.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +25 -1
|
@@ -549,6 +549,20 @@ export declare class Lowerer {
|
|
|
549
549
|
idxsLocalId: string;
|
|
550
550
|
ctx: FnCtx;
|
|
551
551
|
}>;
|
|
552
|
+
/** STORED numeric value iterators: `const it = numbers.values()` (and
|
|
553
|
+
* the equivalent `[Symbol.iterator]()` spelling) over number[] or a
|
|
554
|
+
* represented typed array has no first-class IR value, so its statically
|
|
555
|
+
* known protocol state lives in hidden source/cursor/done locals. A
|
|
556
|
+
* later for-of in the SAME function reads and advances them.
|
|
557
|
+
* `doneLocalId` is sticky: once next() observes the end, later source
|
|
558
|
+
* changes do not revive the exhausted iterator, exactly like Node. */
|
|
559
|
+
readonly numericIterators: Map<ts.Symbol, {
|
|
560
|
+
sourceLocalId: string;
|
|
561
|
+
sourceType: IrType;
|
|
562
|
+
indexLocalId: string;
|
|
563
|
+
doneLocalId: string;
|
|
564
|
+
ctx: FnCtx;
|
|
565
|
+
}>;
|
|
552
566
|
chainCounter: number;
|
|
553
567
|
/** Keyed by program-wide qualified class name (what IR object types carry). */
|
|
554
568
|
readonly classes: Map<string, ClassInfo>;
|
|
@@ -628,6 +642,30 @@ export declare class Lowerer {
|
|
|
628
642
|
* none — %main calls it exactly once (a dependency edge back to the
|
|
629
643
|
* entry would be a fenced cycle). */
|
|
630
644
|
readonly moduleGuardOf: Map<ts.SourceFile, string>;
|
|
645
|
+
/** Files whose module evaluation is asynchronous: direct top-level
|
|
646
|
+
* await/for-await modules plus their static ESM importers. Their %init
|
|
647
|
+
* bodies run on fibers and every async dependency edge awaits the
|
|
648
|
+
* dependency promise before the importer body starts. Synchronous files
|
|
649
|
+
* stay synchronous — adding even an already-settled await would insert
|
|
650
|
+
* an observable microtask hop. */
|
|
651
|
+
readonly asyncInitFiles: Set<ts.SourceFile>;
|
|
652
|
+
/** Async module → its cached evaluation-promise global. The emitted
|
|
653
|
+
* spawn wrapper fills this on first evaluation and returns a retained
|
|
654
|
+
* reference on cache hits, matching Node's one ModuleJob promise per
|
|
655
|
+
* module even across diamonds and concurrent dynamic imports. */
|
|
656
|
+
readonly modulePromiseOf: Map<ts.SourceFile, string>;
|
|
657
|
+
/** Async import-cycle member → the cycle's deterministic graph
|
|
658
|
+
* representative. Used to recognize internal SCC edges; this is NOT
|
|
659
|
+
* necessarily the runtime evaluation root, because a dynamically-only
|
|
660
|
+
* cycle can first be entered through any member. */
|
|
661
|
+
readonly asyncCycleRepresentativeOf: Map<ts.SourceFile, ts.SourceFile>;
|
|
662
|
+
/** Async import-cycle member → the shared completion-promise global for
|
|
663
|
+
* its SCC. Every member's spawn wrapper temporarily publishes its own
|
|
664
|
+
* promise while eager recursive evaluation unwinds; the outermost
|
|
665
|
+
* wrapper (the member actually requested first at runtime) writes last
|
|
666
|
+
* and therefore becomes the cycle's evaluation root. Dynamic imports
|
|
667
|
+
* wait on this shared verdict rather than a build-time-selected member. */
|
|
668
|
+
readonly asyncCyclePromiseOf: Map<ts.SourceFile, string>;
|
|
631
669
|
/** Record-shape interner: canonical (name-sorted) field list → shapeId.
|
|
632
670
|
* Threaded into every mapType call; its `shapes` array becomes
|
|
633
671
|
* IrModule.records. */
|
|
@@ -17,7 +17,7 @@ import * as ts from "../ts7/adapter.js";
|
|
|
17
17
|
import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, componentTypeDiag, isCheckerPanic, genericSignatureTypeDiag, indexSignatureTypeDiag, intersectionTypeDiag, noLoweringDiag, overloadedSignatureTypeDiag, recordShapeMismatchDiag, requiresDynamicApiDiag, requiresDynamicPackageDiag, requiresDynamicTypeDiag, unionMismatchDiag, unsupportedDiag, unsupportedTypeDiag, } from "../../diagnostics/diagnostic.js";
|
|
18
18
|
import { arrayOf, BOOL, canAdaptDynFuncTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, F64, isJsonSafeType, isUndefinedArmedUnion, isUnitType, JSVAL, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID } from "../../ir/nodes.js";
|
|
19
19
|
import { provenanceActive } from "../provenance-registry.js";
|
|
20
|
-
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
20
|
+
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, orderedImportsOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
21
21
|
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../types.js";
|
|
22
22
|
import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalMember, nodeTypesOnlySymbol } from "./surfaces.js";
|
|
23
23
|
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
|
|
@@ -716,6 +716,14 @@ export class Lowerer {
|
|
|
716
716
|
* (the ctx guard keeps hidden locals out of closures — a cross-function
|
|
717
717
|
* walk falls back to the plain array walk and the fence). */
|
|
718
718
|
matchAllDrainIndexes = new Map();
|
|
719
|
+
/** STORED numeric value iterators: `const it = numbers.values()` (and
|
|
720
|
+
* the equivalent `[Symbol.iterator]()` spelling) over number[] or a
|
|
721
|
+
* represented typed array has no first-class IR value, so its statically
|
|
722
|
+
* known protocol state lives in hidden source/cursor/done locals. A
|
|
723
|
+
* later for-of in the SAME function reads and advances them.
|
|
724
|
+
* `doneLocalId` is sticky: once next() observes the end, later source
|
|
725
|
+
* changes do not revive the exhausted iterator, exactly like Node. */
|
|
726
|
+
numericIterators = new Map();
|
|
719
727
|
chainCounter = 0;
|
|
720
728
|
/** Keyed by program-wide qualified class name (what IR object types carry). */
|
|
721
729
|
classes = new Map();
|
|
@@ -792,6 +800,30 @@ export class Lowerer {
|
|
|
792
800
|
* none — %main calls it exactly once (a dependency edge back to the
|
|
793
801
|
* entry would be a fenced cycle). */
|
|
794
802
|
moduleGuardOf = new Map();
|
|
803
|
+
/** Files whose module evaluation is asynchronous: direct top-level
|
|
804
|
+
* await/for-await modules plus their static ESM importers. Their %init
|
|
805
|
+
* bodies run on fibers and every async dependency edge awaits the
|
|
806
|
+
* dependency promise before the importer body starts. Synchronous files
|
|
807
|
+
* stay synchronous — adding even an already-settled await would insert
|
|
808
|
+
* an observable microtask hop. */
|
|
809
|
+
asyncInitFiles = new Set();
|
|
810
|
+
/** Async module → its cached evaluation-promise global. The emitted
|
|
811
|
+
* spawn wrapper fills this on first evaluation and returns a retained
|
|
812
|
+
* reference on cache hits, matching Node's one ModuleJob promise per
|
|
813
|
+
* module even across diamonds and concurrent dynamic imports. */
|
|
814
|
+
modulePromiseOf = new Map();
|
|
815
|
+
/** Async import-cycle member → the cycle's deterministic graph
|
|
816
|
+
* representative. Used to recognize internal SCC edges; this is NOT
|
|
817
|
+
* necessarily the runtime evaluation root, because a dynamically-only
|
|
818
|
+
* cycle can first be entered through any member. */
|
|
819
|
+
asyncCycleRepresentativeOf = new Map();
|
|
820
|
+
/** Async import-cycle member → the shared completion-promise global for
|
|
821
|
+
* its SCC. Every member's spawn wrapper temporarily publishes its own
|
|
822
|
+
* promise while eager recursive evaluation unwinds; the outermost
|
|
823
|
+
* wrapper (the member actually requested first at runtime) writes last
|
|
824
|
+
* and therefore becomes the cycle's evaluation root. Dynamic imports
|
|
825
|
+
* wait on this shared verdict rather than a build-time-selected member. */
|
|
826
|
+
asyncCyclePromiseOf = new Map();
|
|
795
827
|
/** Record-shape interner: canonical (name-sorted) field list → shapeId.
|
|
796
828
|
* Threaded into every mapType call; its `shapes` array becomes
|
|
797
829
|
* IrModule.records. */
|
|
@@ -1353,6 +1385,118 @@ export class Lowerer {
|
|
|
1353
1385
|
this.moduleGuardOf.set(fp.sf, id);
|
|
1354
1386
|
this.globalsList.push({ id, name: "%loaded", type: BOOL, mutable: true });
|
|
1355
1387
|
}
|
|
1388
|
+
// A module is intrinsically async when an await/for-await occurs
|
|
1389
|
+
// outside every nested function-like boundary. Then propagate that
|
|
1390
|
+
// status backwards through STATIC ESM edges: Node does not start an
|
|
1391
|
+
// importer's body until each async dependency has completed. CJS
|
|
1392
|
+
// import/require edges deliberately do not propagate — Node refuses
|
|
1393
|
+
// require(esm) when the graph contains top-level await, and the call
|
|
1394
|
+
// sites below keep that as a named unsupported boundary.
|
|
1395
|
+
for (const fp of parts) {
|
|
1396
|
+
let found = false;
|
|
1397
|
+
ts.walkPreorder(fp.sf, (node) => {
|
|
1398
|
+
if (node !== fp.sf && ts.isFunctionLike(node))
|
|
1399
|
+
return "skip";
|
|
1400
|
+
if (ts.isAwaitExpression(node) ||
|
|
1401
|
+
(ts.isForOfStatement(node) && node.awaitModifier !== undefined)) {
|
|
1402
|
+
found = true;
|
|
1403
|
+
return "stop";
|
|
1404
|
+
}
|
|
1405
|
+
return undefined;
|
|
1406
|
+
});
|
|
1407
|
+
if (found)
|
|
1408
|
+
this.asyncInitFiles.add(fp.sf);
|
|
1409
|
+
}
|
|
1410
|
+
let changed = true;
|
|
1411
|
+
while (changed) {
|
|
1412
|
+
changed = false;
|
|
1413
|
+
for (const fp of parts) {
|
|
1414
|
+
if (this.asyncInitFiles.has(fp.sf) || !isNodeEsmFile(fp.sf))
|
|
1415
|
+
continue;
|
|
1416
|
+
if (orderedImportsOf(this.program, fp.sf).some(({ dep }) => dep !== null && this.asyncInitFiles.has(dep))) {
|
|
1417
|
+
this.asyncInitFiles.add(fp.sf);
|
|
1418
|
+
changed = true;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
for (const fp of parts) {
|
|
1423
|
+
if (!this.asyncInitFiles.has(fp.sf))
|
|
1424
|
+
continue;
|
|
1425
|
+
const rawTag = this.fileTag.get(fp.sf) ?? "";
|
|
1426
|
+
const tag = rawTag === "" ? "e." : rawTag.replace(/^%/, "");
|
|
1427
|
+
const id = `%g.${tag}%initPromise`;
|
|
1428
|
+
this.modulePromiseOf.set(fp.sf, id);
|
|
1429
|
+
this.globalsList.push({
|
|
1430
|
+
id,
|
|
1431
|
+
name: "%initPromise",
|
|
1432
|
+
type: { kind: "promise", inner: VOID },
|
|
1433
|
+
mutable: true,
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
const orderIndex = new Map(parts.map((fp, i) => [fp.sf, i]));
|
|
1437
|
+
const partSet = new Set(parts.map((fp) => fp.sf));
|
|
1438
|
+
const staticDeps = (sf) => orderedImportsOf(this.program, sf)
|
|
1439
|
+
.map(({ dep }) => dep)
|
|
1440
|
+
.filter((dep) => dep !== null && dep !== sf && partSet.has(dep));
|
|
1441
|
+
// Tarjan SCCs over the same static graph. The last postorder member is
|
|
1442
|
+
// a deterministic COMPONENT representative for internal-edge tests
|
|
1443
|
+
// and global naming. The runtime evaluation root can differ: a cycle
|
|
1444
|
+
// reached only through import() starts at whichever member is actually
|
|
1445
|
+
// requested first, not whichever import() site preflight discovered
|
|
1446
|
+
// first. The shared cycle-promise slot below is filled by the emitted
|
|
1447
|
+
// spawn wrappers so it records that runtime choice.
|
|
1448
|
+
let nextIndex = 0;
|
|
1449
|
+
const indexOf = new Map();
|
|
1450
|
+
const lowOf = new Map();
|
|
1451
|
+
const stack = [];
|
|
1452
|
+
const onStack = new Set();
|
|
1453
|
+
const visit = (sf) => {
|
|
1454
|
+
const at = nextIndex++;
|
|
1455
|
+
indexOf.set(sf, at);
|
|
1456
|
+
lowOf.set(sf, at);
|
|
1457
|
+
stack.push(sf);
|
|
1458
|
+
onStack.add(sf);
|
|
1459
|
+
for (const dep of staticDeps(sf)) {
|
|
1460
|
+
if (!indexOf.has(dep)) {
|
|
1461
|
+
visit(dep);
|
|
1462
|
+
lowOf.set(sf, Math.min(lowOf.get(sf), lowOf.get(dep)));
|
|
1463
|
+
}
|
|
1464
|
+
else if (onStack.has(dep)) {
|
|
1465
|
+
lowOf.set(sf, Math.min(lowOf.get(sf), indexOf.get(dep)));
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (lowOf.get(sf) !== indexOf.get(sf))
|
|
1469
|
+
return;
|
|
1470
|
+
const component = [];
|
|
1471
|
+
for (;;) {
|
|
1472
|
+
const member = stack.pop();
|
|
1473
|
+
onStack.delete(member);
|
|
1474
|
+
component.push(member);
|
|
1475
|
+
if (member === sf)
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
if (component.length < 2 || !component.some((member) => this.asyncInitFiles.has(member)))
|
|
1479
|
+
return;
|
|
1480
|
+
const root = component.reduce((a, b) => orderIndex.get(a) > orderIndex.get(b) ? a : b);
|
|
1481
|
+
const rawTag = this.fileTag.get(root) ?? "";
|
|
1482
|
+
const tag = rawTag === "" ? "e." : rawTag.replace(/^%/, "");
|
|
1483
|
+
const cyclePromiseId = `%g.${tag}%cyclePromise`;
|
|
1484
|
+
this.globalsList.push({
|
|
1485
|
+
id: cyclePromiseId,
|
|
1486
|
+
name: "%cyclePromise",
|
|
1487
|
+
type: { kind: "promise", inner: VOID },
|
|
1488
|
+
mutable: true,
|
|
1489
|
+
});
|
|
1490
|
+
for (const member of component) {
|
|
1491
|
+
if (this.asyncInitFiles.has(member)) {
|
|
1492
|
+
this.asyncCycleRepresentativeOf.set(member, root);
|
|
1493
|
+
this.asyncCyclePromiseOf.set(member, cyclePromiseId);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
for (const fp of parts)
|
|
1498
|
+
if (!indexOf.has(fp.sf))
|
|
1499
|
+
visit(fp.sf);
|
|
1356
1500
|
}
|
|
1357
1501
|
/** The lowering of a CommonJS `require("./local")` occurrence: a call of
|
|
1358
1502
|
* the required module's run-once %init at exactly this statement's
|
|
@@ -1375,6 +1519,9 @@ export class Lowerer {
|
|
|
1375
1519
|
: npmStaticDepSf7(this.program, node.getSourceFile(), spec);
|
|
1376
1520
|
if (!dep || dep.fileName.endsWith(".json"))
|
|
1377
1521
|
return null;
|
|
1522
|
+
if (this.asyncInitFiles.has(dep)) {
|
|
1523
|
+
this.unsupported("SC1090", node, `require() of '${spec}' (its ES-module graph uses top-level await; use import() instead)`);
|
|
1524
|
+
}
|
|
1378
1525
|
const initName = this.initNameOf.get(dep);
|
|
1379
1526
|
if (initName === undefined)
|
|
1380
1527
|
return null;
|
|
@@ -1625,7 +1772,7 @@ export class Lowerer {
|
|
|
1625
1772
|
const module = this.diags.length > 0
|
|
1626
1773
|
? null
|
|
1627
1774
|
: {
|
|
1628
|
-
irVersion:
|
|
1775
|
+
irVersion: 3,
|
|
1629
1776
|
sourceFile: this.entry.fileName,
|
|
1630
1777
|
functions,
|
|
1631
1778
|
classes: artifacts.classes,
|