@scriptc/compiler 0.0.16 → 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.
Files changed (64) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +4 -4
  2. package/dist/backend/cc.d.ts +5 -0
  3. package/dist/backend/cc.js +2 -0
  4. package/dist/backend/cc.js.map +1 -1
  5. package/dist/backend/emission/emit-async.js +35 -2
  6. package/dist/backend/emission/emit-async.js.map +1 -1
  7. package/dist/backend/emission/emit-exprs.js +44 -6
  8. package/dist/backend/emission/emit-exprs.js.map +1 -1
  9. package/dist/backend/emission/emitter.js +69 -11
  10. package/dist/backend/emission/emitter.js.map +1 -1
  11. package/dist/backend/emission/may-throw.d.ts +3 -3
  12. package/dist/backend/emission/may-throw.js +15 -4
  13. package/dist/backend/emission/may-throw.js.map +1 -1
  14. package/dist/backend/llvm/emitter.js +200 -21
  15. package/dist/backend/llvm/emitter.js.map +1 -1
  16. package/dist/coverage/surface-manifest.js +27 -9
  17. package/dist/coverage/surface-manifest.js.map +1 -1
  18. package/dist/frontend/lowering/lower-builtins.js +4 -7
  19. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  20. package/dist/frontend/lowering/lower-calls.d.ts +4 -4
  21. package/dist/frontend/lowering/lower-calls.js +44 -11
  22. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  23. package/dist/frontend/lowering/lower-containers.d.ts +22 -17
  24. package/dist/frontend/lowering/lower-containers.js +628 -51
  25. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  26. package/dist/frontend/lowering/lower-exprs.js +19 -1
  27. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  28. package/dist/frontend/lowering/lower-island.d.ts +2 -2
  29. package/dist/frontend/lowering/lower-island.js +57 -9
  30. package/dist/frontend/lowering/lower-island.js.map +1 -1
  31. package/dist/frontend/lowering/lower-modules.js +144 -10
  32. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  33. package/dist/frontend/lowering/lower-stmts.d.ts +8 -0
  34. package/dist/frontend/lowering/lower-stmts.js +408 -21
  35. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  36. package/dist/frontend/lowering/lowerer.d.ts +38 -0
  37. package/dist/frontend/lowering/lowerer.js +149 -2
  38. package/dist/frontend/lowering/lowerer.js.map +1 -1
  39. package/dist/frontend/lowering/surfaces.d.ts +8 -0
  40. package/dist/frontend/lowering/surfaces.js +10 -0
  41. package/dist/frontend/lowering/surfaces.js.map +1 -1
  42. package/dist/frontend/shared.d.ts +4 -0
  43. package/dist/frontend/shared.js +9 -3
  44. package/dist/frontend/shared.js.map +1 -1
  45. package/dist/frontend/ts7/program.js +5 -4
  46. package/dist/frontend/ts7/program.js.map +1 -1
  47. package/dist/index.d.ts +7 -0
  48. package/dist/index.js +7 -3
  49. package/dist/index.js.map +1 -1
  50. package/dist/ir/nodes.d.ts +53 -22
  51. package/dist/ir/nodes.js +42 -0
  52. package/dist/ir/nodes.js.map +1 -1
  53. package/dist/ir/serialize.d.ts +1 -1
  54. package/dist/ir/serialize.js +1 -1
  55. package/dist/ir/validate.js +99 -39
  56. package/dist/ir/validate.js.map +1 -1
  57. package/dist/library/fence-eval.js +9 -1
  58. package/dist/library/fence-eval.js.map +1 -1
  59. package/dist/library/int-infer.js +191 -25
  60. package/dist/library/int-infer.js.map +1 -1
  61. package/dist/library/int-infer.test.js +206 -1
  62. package/dist/library/int-infer.test.js.map +1 -1
  63. package/package.json +2 -2
  64. package/surface-manifest.json +27 -3
@@ -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: 2,
1775
+ irVersion: 3,
1629
1776
  sourceFile: this.entry.fileName,
1630
1777
  functions,
1631
1778
  classes: artifacts.classes,