@scriptc/compiler 0.0.10 → 0.0.12

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 (111) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +63 -15
  2. package/ambient/scriptc.d.ts +2 -2
  3. package/dist/backend/cc.d.ts +2 -2
  4. package/dist/backend/emission/emit-async.js +1 -1
  5. package/dist/backend/emission/emit-exprs.js +131 -54
  6. package/dist/backend/emission/emit-exprs.js.map +1 -1
  7. package/dist/backend/emission/emit-island.js +1 -1
  8. package/dist/backend/emission/emit-shapes.js +1 -1
  9. package/dist/backend/emission/emit-walkers.d.ts +14 -14
  10. package/dist/backend/emission/emit-walkers.js +44 -44
  11. package/dist/backend/emission/emit-walkers.js.map +1 -1
  12. package/dist/backend/emission/emitter.d.ts +3 -3
  13. package/dist/backend/emission/emitter.js +33 -9
  14. package/dist/backend/emission/emitter.js.map +1 -1
  15. package/dist/backend/emission/may-throw.js +1 -1
  16. package/dist/backend/llvm/dyn.d.ts +10 -10
  17. package/dist/backend/llvm/dyn.js +26 -26
  18. package/dist/backend/llvm/dyn.js.map +1 -1
  19. package/dist/backend/llvm/emitter.js +156 -47
  20. package/dist/backend/llvm/emitter.js.map +1 -1
  21. package/dist/backend/llvm/shapes.js +1 -1
  22. package/dist/backend/llvm/walkers.js +2 -2
  23. package/dist/backend/llvm/walkers.js.map +1 -1
  24. package/dist/coverage/surface-manifest.js +16 -2
  25. package/dist/coverage/surface-manifest.js.map +1 -1
  26. package/dist/diagnostics/diagnostic.d.ts +21 -8
  27. package/dist/diagnostics/diagnostic.js +48 -14
  28. package/dist/diagnostics/diagnostic.js.map +1 -1
  29. package/dist/frontend/lowering/lib-boundary.js +5 -5
  30. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  31. package/dist/frontend/lowering/lower-assert.js +12 -12
  32. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  33. package/dist/frontend/lowering/lower-builtins.d.ts +63 -7
  34. package/dist/frontend/lowering/lower-builtins.js +327 -35
  35. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  36. package/dist/frontend/lowering/lower-calls.d.ts +4 -4
  37. package/dist/frontend/lowering/lower-calls.js +104 -70
  38. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  39. package/dist/frontend/lowering/lower-classes.d.ts +7 -0
  40. package/dist/frontend/lowering/lower-classes.js +82 -6
  41. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  42. package/dist/frontend/lowering/lower-containers.d.ts +9 -5
  43. package/dist/frontend/lowering/lower-containers.js +88 -26
  44. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  45. package/dist/frontend/lowering/lower-emitter.d.ts +70 -3
  46. package/dist/frontend/lowering/lower-emitter.js +438 -31
  47. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  48. package/dist/frontend/lowering/lower-exprs.d.ts +87 -12
  49. package/dist/frontend/lowering/lower-exprs.js +630 -125
  50. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  51. package/dist/frontend/lowering/lower-inspect.js +5 -5
  52. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  53. package/dist/frontend/lowering/lower-island.js +12 -3
  54. package/dist/frontend/lowering/lower-island.js.map +1 -1
  55. package/dist/frontend/lowering/lower-modules.d.ts +8 -5
  56. package/dist/frontend/lowering/lower-modules.js +157 -63
  57. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  58. package/dist/frontend/lowering/lower-namespaces.d.ts +24 -0
  59. package/dist/frontend/lowering/lower-namespaces.js +158 -16
  60. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  61. package/dist/frontend/lowering/lower-server.js +11 -11
  62. package/dist/frontend/lowering/lower-server.js.map +1 -1
  63. package/dist/frontend/lowering/lower-stmts.d.ts +1 -1
  64. package/dist/frontend/lowering/lower-stmts.js +271 -36
  65. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  66. package/dist/frontend/lowering/lower-stream.js +171 -20
  67. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  68. package/dist/frontend/lowering/lowerer.d.ts +27 -10
  69. package/dist/frontend/lowering/lowerer.js +156 -54
  70. package/dist/frontend/lowering/lowerer.js.map +1 -1
  71. package/dist/frontend/lowering/surfaces.d.ts +44 -0
  72. package/dist/frontend/lowering/surfaces.js +236 -5
  73. package/dist/frontend/lowering/surfaces.js.map +1 -1
  74. package/dist/frontend/npm.d.ts +12 -0
  75. package/dist/frontend/npm.js +30 -0
  76. package/dist/frontend/npm.js.map +1 -1
  77. package/dist/frontend/program.d.ts +25 -0
  78. package/dist/frontend/program.js +145 -136
  79. package/dist/frontend/program.js.map +1 -1
  80. package/dist/frontend/shared.d.ts +1 -1
  81. package/dist/frontend/shared.js +8 -8
  82. package/dist/frontend/shared.js.map +1 -1
  83. package/dist/frontend/types.d.ts +2 -2
  84. package/dist/frontend/types.js +15 -15
  85. package/dist/frontend/types.js.map +1 -1
  86. package/dist/index.d.ts +2 -1
  87. package/dist/index.js +169 -5
  88. package/dist/index.js.map +1 -1
  89. package/dist/ir/nodes.d.ts +174 -106
  90. package/dist/ir/nodes.js +69 -24
  91. package/dist/ir/nodes.js.map +1 -1
  92. package/dist/ir/validate.js +55 -36
  93. package/dist/ir/validate.js.map +1 -1
  94. package/dist/library/fence-eval.js +12 -2
  95. package/dist/library/fence-eval.js.map +1 -1
  96. package/dist/library/int-infer.d.ts +119 -0
  97. package/dist/library/int-infer.js +1314 -0
  98. package/dist/library/int-infer.js.map +1 -0
  99. package/dist/library/int-infer.test.d.ts +1 -0
  100. package/dist/library/int-infer.test.js +279 -0
  101. package/dist/library/int-infer.test.js.map +1 -0
  102. package/dist/library/profile.d.ts +34 -5
  103. package/dist/library/profile.js +77 -13
  104. package/dist/library/profile.js.map +1 -1
  105. package/dist/library/sidecar-validate.js +1 -1
  106. package/dist/library/sidecar-validate.js.map +1 -1
  107. package/dist/library/sidecar.d.ts +24 -1
  108. package/dist/library/sidecar.js +163 -16
  109. package/dist/library/sidecar.js.map +1 -1
  110. package/package.json +2 -2
  111. package/surface-manifest.json +210 -12
@@ -1,10 +1,77 @@
1
1
  import * as ts from "../ts7/adapter.js";
2
2
  import type { Lowerer } from "./lowerer.js";
3
3
  import type { ClassInfo } from "./lower-classes.js";
4
- import { IrExpr } from "../../ir/nodes.js";
4
+ import { IrExpr, IrFunction, IrLocal, IrType } from "../../ir/nodes.js";
5
5
  /** True when the class descends from (or is) the runtime emitter. */
6
6
  export declare function emitterRooted(L: Lowerer, info: ClassInfo | undefined | null): boolean;
7
7
  /** `recv.<member>(...)` over an emitter-rooted receiver — the whole
8
8
  * EventEmitter method surface. Returns null only for members this spoke
9
- * does not own (the caller falls through to its own fences). */
10
- export declare function lowerEmitterMethodCall(L: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression, info: ClassInfo): IrExpr | null;
9
+ * does not own (the caller falls through to its own fences).
10
+ *
11
+ * `superRecv` is the `super.<member>(...)` spelling (lowerSuperMethodCall
12
+ * routes here): the receiver is the current method's `this`, and emit
13
+ * NEVER dispatches through an override at-or-below the lexical class —
14
+ * JS's static super dispatch (the nearest override STRICTLY ABOVE the
15
+ * lexical class still answers, exactly like a prototype-chain walk). */
16
+ export declare function lowerEmitterMethodCall(L: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression, info: ClassInfo, superRecv?: {
17
+ thisRef: IrExpr;
18
+ cls: ClassInfo;
19
+ }): IrExpr | null;
20
+ export interface EmitOverrideRec {
21
+ decl: ts.MethodDeclaration;
22
+ eventSym: ts.Symbol;
23
+ restSym: ts.Symbol;
24
+ }
25
+ /** The lowering context of one specialization body (lowerEmitOverrideSpec
26
+ * sets it; lowerSuperMethodCall's forward interception reads it). */
27
+ export interface EmitSpecCtx {
28
+ info: ClassInfo;
29
+ event: string;
30
+ tuple: IrType[];
31
+ eventSym: ts.Symbol;
32
+ restSym: ts.Symbol;
33
+ tupleParams: IrLocal[];
34
+ }
35
+ /** One queued specialization: `%<class>.emit:<event>`, lowered in the
36
+ * drive loop's fixpoint (its body can queue further specializations —
37
+ * the super-forward chain — and generic instances). */
38
+ export interface EmitSpecRequest {
39
+ info: ClassInfo;
40
+ event: string;
41
+ tuple: IrType[];
42
+ /** Per-class body ordinal: bodies past the first suppress coverage
43
+ * stats (one source method, many monomorphized copies). */
44
+ ordinal: number;
45
+ }
46
+ /** Why a subclass `emit` declaration is NOT the forwarding shape, or null
47
+ * when it conforms. Checked at class collection; the recorded override
48
+ * then lowers per event with no further shape questions. */
49
+ export declare function emitOverrideShapeReason(L: Lowerer, member: ts.MethodDeclaration): string | null;
50
+ /** The nearest class at-or-above `info` declaring an emit override. */
51
+ export declare function emitOverrideAtOrAbove(info: ClassInfo | null | undefined): ClassInfo | null;
52
+ /** An emit-override class COMPARABLE to `info` (ancestor-or-self or
53
+ * descendant — a dynamic instance under this static type could dispatch
54
+ * through it), or null. The meta-registration fence's test. */
55
+ export declare function emitOverrideComparable(info: ClassInfo): ClassInfo | null;
56
+ /** Interns the specialization `%<class>.emit:<event>`: registers the
57
+ * method on the class (vtable participation — the ABI is `(this, ...tuple)
58
+ * => bool` for every declarer of one event, override-exact by
59
+ * construction) and queues the body for the drive loop's fixpoint. */
60
+ export declare function ensureEmitSpec(L: Lowerer, info: ClassInfo, event: string, tuple: IrType[]): void;
61
+ /** One specialization body: the override method's statements lowered with
62
+ * `event` bound to the name and hidden tuple parameters standing in for
63
+ * the rest parameter (readable ONLY through the super-forward, which the
64
+ * collection shape check guaranteed). */
65
+ export declare function lowerEmitOverrideSpec(L: Lowerer, req: EmitSpecRequest): IrFunction | null;
66
+ /** The specialization body's `super.emit(event, ...args)` — matched
67
+ * BEFORE the general super lowering (the event name is this
68
+ * specialization's, not a source literal). The target is the nearest
69
+ * override ancestor's specialization (JS's super chain) or the runtime
70
+ * emit. Null when the call is not the forward (a literal-name super.emit
71
+ * rides the general super-emitter route). */
72
+ export declare function emitSpecSuperForward(L: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression): IrExpr | null;
73
+ /** `super.<member>(...)` over an emitter-rooted base — the emitter spoke
74
+ * with the current method's `this` as the receiver and STATIC dispatch
75
+ * (an emit override at-or-below the lexical class never answers a super
76
+ * call — JS's prototype-chain rule). */
77
+ export declare function lowerEmitterSuperCall(L: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression, cls: ClassInfo): IrExpr | null;
@@ -30,10 +30,15 @@
30
30
  * routes through emitter.emitError — no listener means the payload
31
31
  * THROWS, Node's contract — and subclass payloads upcast to the root);
32
32
  * 'newListener'/'removeListener' are forced to the one-string tuple (the
33
- * runtime emits them internally with the affected event's name). */
33
+ * runtime emits them internally with the affected event's name).
34
+ *
35
+ * One member is overridable: `emit`, in the forwarding shape — see the
36
+ * emit-overrides block at the bottom of this file (per-event
37
+ * monomorphization of the override body, dispatch on the dynamic class,
38
+ * super.emit as the prototype chain). */
34
39
  import * as ts from "../ts7/adapter.js";
35
- import { dynFallbackType } from "./lowerer.js";
36
- import { locOf } from "../program.js";
40
+ import { dynFallbackType, newFnCtx } from "./lowerer.js";
41
+ import { isJsSourceFile, locOf } from "../program.js";
37
42
  import { arrayOf, BOOL, canBoxFuncIntoDyn, canConvertToDyn, DYN, F64, isUnitType, STRING, typeEquals, typeKey, VOID } from "../../ir/nodes.js";
38
43
  import { streamForcedTuple, streamSidesOf } from "./lower-stream.js";
39
44
  /** True when the class descends from (or is) the runtime emitter. */
@@ -434,11 +439,18 @@ function lowerListenerArg(L, member, name, node, tuple) {
434
439
  }
435
440
  /** `recv.<member>(...)` over an emitter-rooted receiver — the whole
436
441
  * EventEmitter method surface. Returns null only for members this spoke
437
- * does not own (the caller falls through to its own fences). */
438
- export function lowerEmitterMethodCall(L, call, access, info) {
442
+ * does not own (the caller falls through to its own fences).
443
+ *
444
+ * `superRecv` is the `super.<member>(...)` spelling (lowerSuperMethodCall
445
+ * routes here): the receiver is the current method's `this`, and emit
446
+ * NEVER dispatches through an override at-or-below the lexical class —
447
+ * JS's static super dispatch (the nearest override STRICTLY ABOVE the
448
+ * lexical class still answers, exactly like a prototype-chain walk). */
449
+ export function lowerEmitterMethodCall(L, call, access, info, superRecv) {
439
450
  const member = access.name.text;
440
451
  const loc = locOf(call);
441
452
  const args = call.arguments;
453
+ const lowerReceiver = () => superRecv?.thisRef ?? L.lowerExpr(access.expression);
442
454
  // The class VALUE, not an instance (`EventEmitter.setMaxListeners(n)` —
443
455
  // the receiver's checker type carries construct signatures; mapType
444
456
  // answers the instance object for every EventEmitter-symbol type, so the
@@ -447,10 +459,10 @@ export function lowerEmitterMethodCall(L, call, access, info) {
447
459
  // are a DIFFERENT surface: setMaxListeners(n) writes the process-wide
448
460
  // defaultMaxListeners (validated at runtime — Node's ERR_OUT_OF_RANGE);
449
461
  // everything else fences by its static name.
450
- if (L.checker.getConstructSignatures(L.typeOf(access.expression)).length > 0) {
462
+ if (superRecv === undefined && L.checker.getConstructSignatures(L.typeOf(access.expression)).length > 0) {
451
463
  if (member === "setMaxListeners" && args.length === 1) {
452
464
  if (L.mapTypeOf(L.typeOf(args[0]))?.kind !== "f64") {
453
- // Node's runtime ladder over the DOM value ("setMaxListeners" is
465
+ // Node's runtime ladder over the dyn value ("setMaxListeners" is
454
466
  // the message's slot for the static form).
455
467
  const raw = L.lowerExpr(args[0]);
456
468
  if (raw.type.kind === "dyn" || raw.kind === "unitLit" || L.dynConvertible(raw.type)) {
@@ -475,7 +487,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
475
487
  // emitter/EventTarget (the invalid-input probes): Node validates n
476
488
  // first, then throws ERR_INVALID_ARG_TYPE on the target. Claimed
477
489
  // when n is a pure read (identifier/literal — nothing to evaluate)
478
- // and the target crosses into the DOM for the Received tail.
490
+ // and the target crosses into the checked-dynamic tree for the Received tail.
479
491
  if (args.length === 2 && !args.some(ts.isSpreadElement)) {
480
492
  const nT = L.mapTypeOf(L.typeOf(args[0]));
481
493
  const tT = L.mapTypeOf(L.typeOf(args[1]));
@@ -506,12 +518,27 @@ export function lowerEmitterMethodCall(L, call, access, info) {
506
518
  }
507
519
  const table = emitterEvents(L);
508
520
  if (REGISTER_MEMBERS.has(member) || member === "off" || member === "removeListener") {
509
- if (args.length !== 2) {
521
+ // Node's (type, listener) signature IGNORES extra arguments — admit
522
+ // pure-read extras (literals/identifiers: nothing to evaluate) and
523
+ // drop them; anything with effects keeps the fence.
524
+ const extrasPure = args.slice(2).every((a) => ts.isIdentifier(a) || ts.isLiteralExpression(a));
525
+ if (args.length < 2 || !extrasPure) {
510
526
  L.noLowering(`${member} with ${args.length} arguments`, call, "the supported form is (eventName, listener)");
511
527
  }
512
528
  const name = eventNameOf(L, member, args[0]);
513
- const receiver = L.lowerExpr(access.expression);
514
529
  const registering = REGISTER_MEMBERS.has(member);
530
+ // The runtime fires the meta events INTERNALLY (scr_ee_emit_meta —
531
+ // Node calls this.emit, which would dispatch through an emit
532
+ // override): with an override anywhere the receiver's instances could
533
+ // be, a registered meta listener is the one observer of that
534
+ // divergence, so the registration is the honest fence site.
535
+ if (registering && META_EVENTS.has(name)) {
536
+ const ov = emitOverrideComparable(info);
537
+ if (ov) {
538
+ L.noLowering(`'${member}' of '${name}' while '${ov.def.jsName || ov.def.name}' overrides emit`, call, "the runtime fires meta events internally, which cannot route through an emit override");
539
+ }
540
+ }
541
+ const receiver = lowerReceiver();
515
542
  const once = member === "once" || member === "prependOnceListener";
516
543
  const prepend = member === "prependListener" || member === "prependOnceListener";
517
544
  // Stream 'data' rides its own two-slot payload ABI (bytes + string —
@@ -577,7 +604,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
577
604
  L.noLowering("emit without an event name", call);
578
605
  }
579
606
  const name = eventNameOf(L, member, args[0]);
580
- const receiver = L.lowerExpr(access.expression);
607
+ const receiver = lowerReceiver();
581
608
  if (name === "error") {
582
609
  // The special event: exactly one %Error-rooted payload; no listener
583
610
  // means the runtime THROWS it (emitter.emitError, may-throw).
@@ -597,13 +624,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
597
624
  if (!rootsAtError(err.type)) {
598
625
  L.noLowering(`emit('error') with a '${L.fmt(err.type)}' payload`, args[1], "the supported payload is an Error-hierarchy instance");
599
626
  }
600
- return {
601
- kind: "libCall",
602
- fn: "emitter.emitError",
603
- args: [receiver, strLit(name, loc), L.upcastTo(err, "%Error")],
604
- type: BOOL,
605
- loc,
606
- };
627
+ return emitDispatchExpr(L, info, name, [{ kind: "object", className: "%Error" }], receiver, [L.upcastTo(err, "%Error")], superRecv?.cls, loc);
607
628
  }
608
629
  // Stream 'data' rides the two-slot payload ABI: a user emit fills
609
630
  // the chunk's slot (bytes or string), NULLs the other.
@@ -629,13 +650,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
629
650
  L.noLowering(`emit('${name}') with ${args.length - 1} arguments where the event's tuple has ${tuple.length}`, call, "every emit site of one event name must supply the same argument tuple (listeners may declare a prefix)");
630
651
  }
631
652
  const payload = args.slice(1).map((a, i) => L.lowerExprExpecting(a, tuple[i]));
632
- return {
633
- kind: "libCall",
634
- fn: "emitter.emit",
635
- args: [receiver, strLit(name, loc), ...payload],
636
- type: BOOL,
637
- loc,
638
- };
653
+ return emitDispatchExpr(L, info, name, tuple, receiver, payload, superRecv?.cls, loc);
639
654
  }
640
655
  // The pure-introspection members take ANY string-typed name — no tuple
641
656
  // is involved, so the literal rule has nothing to protect (a meta
@@ -645,7 +660,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
645
660
  L.noLowering(`removeAllListeners with ${args.length} arguments`, call);
646
661
  }
647
662
  const all = args.length === 0;
648
- const receiver = L.lowerExpr(access.expression);
663
+ const receiver = lowerReceiver();
649
664
  const name = all ? strLit("", loc) : L.lowerExprExpecting(args[0], STRING);
650
665
  if (name.type.kind !== "string") {
651
666
  L.noLowering(`removeAllListeners with a '${L.fmt(name.type)}' event name`, args[0] ?? call);
@@ -662,7 +677,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
662
677
  if (args.length !== 1 && args.length !== 2) {
663
678
  L.noLowering(`listenerCount with ${args.length} arguments`, call);
664
679
  }
665
- const receiver = L.lowerExpr(access.expression);
680
+ const receiver = lowerReceiver();
666
681
  const name = L.lowerExprExpecting(args[0], STRING);
667
682
  if (name.type.kind !== "string") {
668
683
  L.noLowering(`listenerCount with a '${L.fmt(name.type)}' event name`, args[0]);
@@ -699,7 +714,7 @@ export function lowerEmitterMethodCall(L, call, access, info) {
699
714
  L.noLowering(`${member} of the event '${name}'`, call, "a listener of this event is checked-dynamic (unannotated parameters), so the listener array has no one static element type — listenerCount(name) counts");
700
715
  }
701
716
  const tuple = tupleOf(L, table, name, call);
702
- const receiver = L.lowerExpr(access.expression);
717
+ const receiver = lowerReceiver();
703
718
  return {
704
719
  kind: "libCall",
705
720
  fn: "emitter.listeners",
@@ -711,13 +726,13 @@ export function lowerEmitterMethodCall(L, call, access, info) {
711
726
  if (member === "eventNames") {
712
727
  if (args.length !== 0)
713
728
  L.noLowering(`eventNames with ${args.length} arguments`, call);
714
- const receiver = L.lowerExpr(access.expression);
729
+ const receiver = lowerReceiver();
715
730
  return { kind: "libCall", fn: "emitter.names", args: [receiver], type: arrayOf(STRING), loc };
716
731
  }
717
732
  if (member === "setMaxListeners") {
718
733
  if (args.length !== 1)
719
734
  L.noLowering(`setMaxListeners with ${args.length} arguments`, call);
720
- const receiver = L.lowerExpr(access.expression);
735
+ const receiver = lowerReceiver();
721
736
  if (L.mapTypeOf(L.typeOf(args[0]))?.kind !== "f64") {
722
737
  // The invalid-input probes (string n, dyn helpers): Node's ladder
723
738
  // runs at runtime — ERR_INVALID_ARG_TYPE for non-numbers,
@@ -734,9 +749,401 @@ export function lowerEmitterMethodCall(L, call, access, info) {
734
749
  if (member === "getMaxListeners") {
735
750
  if (args.length !== 0)
736
751
  L.noLowering(`getMaxListeners with ${args.length} arguments`, call);
737
- const receiver = L.lowerExpr(access.expression);
752
+ const receiver = lowerReceiver();
738
753
  return { kind: "libCall", fn: "emitter.getMax", args: [receiver], type: F64, loc };
739
754
  }
740
755
  return null;
741
756
  }
757
+ /** Why a subclass `emit` declaration is NOT the forwarding shape, or null
758
+ * when it conforms. Checked at class collection; the recorded override
759
+ * then lowers per event with no further shape questions. */
760
+ export function emitOverrideShapeReason(L, member) {
761
+ if (!member.body)
762
+ return "the declaration has no body";
763
+ if (member.asteriskToken !== undefined)
764
+ return "generator methods cannot answer emit's boolean";
765
+ if (member.questionToken !== undefined)
766
+ return "optional method declarations have no lowering";
767
+ if (member.typeParameters !== undefined)
768
+ return "the compiled shape declares no type parameters";
769
+ for (const m of member.modifiers ?? []) {
770
+ if (m.kind === ts.SyntaxKind.AsyncKeyword)
771
+ return "async methods answer a Promise, not emit's boolean";
772
+ if (m.kind === ts.SyntaxKind.AbstractKeyword)
773
+ return "abstract declarations have no body to lower";
774
+ if (m.kind === ts.SyntaxKind.Decorator)
775
+ return "decorated methods have no lowering";
776
+ }
777
+ if (member.parameters.length !== 2)
778
+ return "it must declare exactly (event, ...args)";
779
+ const p0 = member.parameters[0];
780
+ const p1 = member.parameters[1];
781
+ if (!ts.isIdentifier(p0.name) || p0.dotDotDotToken !== undefined ||
782
+ p0.questionToken !== undefined || p0.initializer !== undefined ||
783
+ (p0.modifiers?.length ?? 0) > 0) {
784
+ return "the event parameter must be a plain identifier";
785
+ }
786
+ if (!p1.dotDotDotToken || !ts.isIdentifier(p1.name) ||
787
+ p1.initializer !== undefined || (p1.modifiers?.length ?? 0) > 0) {
788
+ return "the second parameter must be a plain rest parameter (...args)";
789
+ }
790
+ try {
791
+ // TS: the event parameter must be annotated `string` (bivariance
792
+ // admits it under the `string | symbol` base — symbols have no
793
+ // lowering anyway). JS: unannotated is the only spelling (checker
794
+ // `any`); the specialization binds it as the string it always is —
795
+ // event names are compile-time literals program-wide.
796
+ const p0T = L.mapTypeOf(L.typeOf(p0.name));
797
+ const jsUnannotated = p0.type === undefined && isJsSourceFile(member.getSourceFile()) &&
798
+ (p0T === null || p0T.kind === "dyn" || p0T.kind === "jsval");
799
+ if (p0T?.kind !== "string" && !jsUnannotated) {
800
+ return "the event parameter must be typed 'string' (symbol event names have no lowering)";
801
+ }
802
+ const sig = L.checker.getSignatureFromDeclaration(member);
803
+ const ret = sig ? L.mapTypeOf(L.checker.getReturnTypeOfSignature(sig)) : null;
804
+ if (ret?.kind !== "bool") {
805
+ return "every code path must return a boolean (emit's contract)";
806
+ }
807
+ }
808
+ catch {
809
+ return "its signature does not resolve statically";
810
+ }
811
+ const eventSym = L.checker.getSymbolAtLocation(p0.name);
812
+ const restSym = L.checker.getSymbolAtLocation(p1.name);
813
+ if (!eventSym || !restSym)
814
+ return "its parameters do not resolve statically";
815
+ let reason = null;
816
+ const visit = (n) => {
817
+ if (reason !== null)
818
+ return;
819
+ if (ts.isIdentifier(n)) {
820
+ let s;
821
+ try {
822
+ s = L.checker.getSymbolAtLocation(n) ?? undefined;
823
+ }
824
+ catch {
825
+ s = undefined;
826
+ }
827
+ if (s === restSym && !isEmitForwardSpread(L, member, n, eventSym)) {
828
+ reason = "the rest parameter may only forward through super.emit(event, ...args) in the method's own body";
829
+ }
830
+ else if (s === eventSym && n !== p0.name && isWriteTarget(n)) {
831
+ reason = "the event parameter cannot be reassigned (the forward's event name must stay pinned)";
832
+ }
833
+ }
834
+ n.forEachChild(visit);
835
+ };
836
+ member.body.forEachChild(visit);
837
+ return reason;
838
+ }
839
+ /** True when this rest-parameter reference is exactly the forward spread:
840
+ * `super.emit(<event param>, ...<this identifier>)`, two arguments, sitting
841
+ * in the override method's OWN body (a nested function could not reach the
842
+ * specialization's tuple parameters). */
843
+ function isEmitForwardSpread(L, method, id, eventSym) {
844
+ const sp = id.parent;
845
+ if (!ts.isSpreadElement(sp) || sp.expression !== id)
846
+ return false;
847
+ const call = sp.parent;
848
+ if (!ts.isCallExpression(call) || call.arguments.length !== 2 || call.arguments[1] !== sp)
849
+ return false;
850
+ const callee = call.expression;
851
+ if (!ts.isPropertyAccessExpression(callee) ||
852
+ callee.expression.kind !== ts.SyntaxKind.SuperKeyword ||
853
+ callee.name.text !== "emit") {
854
+ return false;
855
+ }
856
+ const a0 = call.arguments[0];
857
+ if (!ts.isIdentifier(a0))
858
+ return false;
859
+ try {
860
+ if (L.checker.getSymbolAtLocation(a0) !== eventSym)
861
+ return false;
862
+ }
863
+ catch {
864
+ return false;
865
+ }
866
+ for (let p = call.parent; p; p = p.parent) {
867
+ if (p === method)
868
+ return true;
869
+ if (ts.isArrowFunction(p) || ts.isFunctionExpression(p) || ts.isFunctionDeclaration(p) ||
870
+ ts.isMethodDeclaration(p) || ts.isConstructorDeclaration(p) ||
871
+ ts.isGetAccessor(p) || ts.isSetAccessor(p) ||
872
+ ts.isClassDeclaration(p) || ts.isClassExpression(p)) {
873
+ return false;
874
+ }
875
+ }
876
+ return false;
877
+ }
878
+ /** True when the identifier sits in a WRITE position: assignment target
879
+ * (destructuring patterns included — the climb crosses array/object
880
+ * literal layers), ++/--, or a for-in/of initializer. */
881
+ function isWriteTarget(id) {
882
+ let n = id;
883
+ for (;;) {
884
+ const p = n.parent;
885
+ if (ts.isBinaryExpression(p)) {
886
+ const k = p.operatorToken.kind;
887
+ return p.left === n && k >= ts.SyntaxKind.FirstAssignment && k <= ts.SyntaxKind.LastAssignment;
888
+ }
889
+ if (ts.isPostfixUnaryExpression(p) || ts.isPrefixUnaryExpression(p)) {
890
+ return ((p.operator === ts.SyntaxKind.PlusPlusToken || p.operator === ts.SyntaxKind.MinusMinusToken) &&
891
+ p.operand === n);
892
+ }
893
+ if (ts.isForOfStatement(p) || ts.isForInStatement(p))
894
+ return p.initializer === n;
895
+ if (ts.isParenthesizedExpression(p) || ts.isArrayLiteralExpression(p) ||
896
+ ts.isSpreadElement(p) || ts.isSpreadAssignment(p) ||
897
+ ts.isShorthandPropertyAssignment(p) || ts.isObjectLiteralExpression(p) ||
898
+ (ts.isPropertyAssignment(p) && p.initializer === n)) {
899
+ n = p;
900
+ continue;
901
+ }
902
+ return false;
903
+ }
904
+ }
905
+ /** The nearest class at-or-above `info` declaring an emit override. */
906
+ export function emitOverrideAtOrAbove(info) {
907
+ for (let c = info ?? null; c; c = c.base) {
908
+ if (c.emitOverride)
909
+ return c;
910
+ }
911
+ return null;
912
+ }
913
+ /** Every STRICT descendant of `info` declaring an emit override. */
914
+ function collectEmitOverridesBelow(info, out) {
915
+ for (const s of info.subclasses) {
916
+ if (s.emitOverride)
917
+ out.push(s);
918
+ collectEmitOverridesBelow(s, out);
919
+ }
920
+ }
921
+ /** The topmost override classes strictly below `info` (deeper overrides
922
+ * intercept these through the vtable — one interval test each covers a
923
+ * whole override subtree). */
924
+ function topmostEmitOverridesBelow(info) {
925
+ const out = [];
926
+ const walk = (c) => {
927
+ for (const s of c.subclasses) {
928
+ if (s.emitOverride)
929
+ out.push(s);
930
+ else
931
+ walk(s);
932
+ }
933
+ };
934
+ walk(info);
935
+ return out;
936
+ }
937
+ /** An emit-override class COMPARABLE to `info` (ancestor-or-self or
938
+ * descendant — a dynamic instance under this static type could dispatch
939
+ * through it), or null. The meta-registration fence's test. */
940
+ export function emitOverrideComparable(info) {
941
+ const above = emitOverrideAtOrAbove(info);
942
+ if (above)
943
+ return above;
944
+ const below = [];
945
+ collectEmitOverridesBelow(info, below);
946
+ return below[0] ?? null;
947
+ }
948
+ /** Interns the specialization `%<class>.emit:<event>`: registers the
949
+ * method on the class (vtable participation — the ABI is `(this, ...tuple)
950
+ * => bool` for every declarer of one event, override-exact by
951
+ * construction) and queues the body for the drive loop's fixpoint. */
952
+ export function ensureEmitSpec(L, info, event, tuple) {
953
+ const mName = `emit:${event}`;
954
+ const key = `%${info.def.name}.${mName}`;
955
+ if (L.emitSpecDone.has(key))
956
+ return;
957
+ L.emitSpecDone.add(key);
958
+ if (!info.methods.has(mName)) {
959
+ info.methods.set(mName, { params: tuple.map((t) => ({ type: t, mode: "required" })), ret: BOOL });
960
+ if (info.def.methods)
961
+ info.def.methods.push(mName);
962
+ else
963
+ info.def.methods = [mName];
964
+ }
965
+ const ordinal = L.emitSpecQueue.filter((q) => q.info === info).length;
966
+ L.emitSpecQueue.push({ info, event, tuple, ordinal });
967
+ }
968
+ /** One specialization body: the override method's statements lowered with
969
+ * `event` bound to the name and hidden tuple parameters standing in for
970
+ * the rest parameter (readable ONLY through the super-forward, which the
971
+ * collection shape check guaranteed). */
972
+ export function lowerEmitOverrideSpec(L, req) {
973
+ const { info, event, tuple, ordinal } = req;
974
+ const ov = info.emitOverride;
975
+ if (!ov?.decl.body)
976
+ return null;
977
+ const className = info.def.name;
978
+ const thisType = { kind: "object", className };
979
+ const prevClass = L.currentClass;
980
+ const prevSpec = L.emitSpecCtx;
981
+ const prevSuppress = L.suppressStats;
982
+ L.currentClass = info;
983
+ L.suppressStats = prevSuppress || ordinal > 0;
984
+ L.fnStack.push(newFnCtx(false, null, null, BOOL));
985
+ try {
986
+ const loc = locOf(ov.decl);
987
+ const thisLocal = L.declareThis(thisType);
988
+ const params = [{ localId: thisLocal.id, name: "this", type: thisType }];
989
+ const tupleParams = tuple.map((t, i) => {
990
+ const p = L.declareHiddenLocal(`%ee${i}`, t);
991
+ params.push({ localId: p.id, name: `%ee${i}`, type: t });
992
+ return p;
993
+ });
994
+ const eventDecl = ov.decl.parameters[0];
995
+ const eventLocal = L.declareLocal(eventDecl.name, ts.isIdentifier(eventDecl.name) ? eventDecl.name.text : "event", STRING, true);
996
+ const prologue = [
997
+ { kind: "varDecl", localId: eventLocal.id, init: strLit(event, loc), loc },
998
+ ];
999
+ L.emitSpecCtx = { info, event, tuple, eventSym: ov.eventSym, restSym: ov.restSym, tupleParams };
1000
+ const body = [...prologue, ...L.lowerStmts(ov.decl.body.statements)];
1001
+ return {
1002
+ name: `%${className}.emit:${event}`,
1003
+ params,
1004
+ returnType: BOOL,
1005
+ locals: L.ctx.locals,
1006
+ body,
1007
+ loc,
1008
+ };
1009
+ }
1010
+ finally {
1011
+ L.emitSpecCtx = prevSpec;
1012
+ L.fnStack.pop();
1013
+ L.currentClass = prevClass;
1014
+ L.suppressStats = prevSuppress;
1015
+ }
1016
+ }
1017
+ /** The specialization body's `super.emit(event, ...args)` — matched
1018
+ * BEFORE the general super lowering (the event name is this
1019
+ * specialization's, not a source literal). The target is the nearest
1020
+ * override ancestor's specialization (JS's super chain) or the runtime
1021
+ * emit. Null when the call is not the forward (a literal-name super.emit
1022
+ * rides the general super-emitter route). */
1023
+ export function emitSpecSuperForward(L, call, access) {
1024
+ const ctx = L.emitSpecCtx;
1025
+ if (!ctx || access.name.text !== "emit" || call.arguments.length !== 2)
1026
+ return null;
1027
+ const a0 = call.arguments[0];
1028
+ const a1 = call.arguments[1];
1029
+ if (!ts.isIdentifier(a0) || !ts.isSpreadElement(a1) || !ts.isIdentifier(a1.expression))
1030
+ return null;
1031
+ if (L.checker.getSymbolAtLocation(a0) !== ctx.eventSym)
1032
+ return null;
1033
+ if (L.checker.getSymbolAtLocation(a1.expression) !== ctx.restSym)
1034
+ return null;
1035
+ const loc = locOf(call);
1036
+ const thisLocal = L.resolveThis();
1037
+ if (!thisLocal)
1038
+ return null;
1039
+ const thisRef = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
1040
+ const payload = ctx.tupleParams.map((p) => ({ kind: "varRef", localId: p.id, type: p.type, loc }));
1041
+ const anc = emitOverrideAtOrAbove(ctx.info.base);
1042
+ if (anc) {
1043
+ ensureEmitSpec(L, anc, ctx.event, ctx.tuple);
1044
+ const callee = `%${anc.def.name}.emit:${ctx.event}`;
1045
+ L.noteEdge(callee);
1046
+ return { kind: "call", callee, args: [L.upcastTo(thisRef, anc.def.name), ...payload], type: BOOL, loc };
1047
+ }
1048
+ if (ctx.event === "error") {
1049
+ return { kind: "libCall", fn: "emitter.emitError", args: [thisRef, strLit(ctx.event, loc), payload[0]], type: BOOL, loc };
1050
+ }
1051
+ return { kind: "libCall", fn: "emitter.emit", args: [thisRef, strLit(ctx.event, loc), ...payload], type: BOOL, loc };
1052
+ }
1053
+ /** `super.<member>(...)` over an emitter-rooted base — the emitter spoke
1054
+ * with the current method's `this` as the receiver and STATIC dispatch
1055
+ * (an emit override at-or-below the lexical class never answers a super
1056
+ * call — JS's prototype-chain rule). */
1057
+ export function lowerEmitterSuperCall(L, call, access, cls) {
1058
+ const thisLocal = L.resolveThis();
1059
+ if (!thisLocal)
1060
+ return null;
1061
+ const loc = locOf(call);
1062
+ const thisRef = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
1063
+ return lowerEmitterMethodCall(L, call, access, cls, { thisRef, cls });
1064
+ }
1065
+ /** One emit site's dispatch: the plain runtime libCall when no override
1066
+ * is comparable to the receiver's static class; a direct or virtual
1067
+ * specialization call when an override sits at-or-above it; an interned
1068
+ * instanceOf-branch helper when overrides exist only strictly below
1069
+ * (evaluating receiver and payload exactly once as its arguments). */
1070
+ function emitDispatchExpr(L, info, name, tuple, receiver, payload, superOf, loc) {
1071
+ const method = `emit:${name}`;
1072
+ const runtime = (recv, args) => name === "error"
1073
+ ? { kind: "libCall", fn: "emitter.emitError", args: [recv, strLit(name, loc), args[0]], type: BOOL, loc }
1074
+ : { kind: "libCall", fn: "emitter.emit", args: [recv, strLit(name, loc), ...args], type: BOOL, loc };
1075
+ if (superOf !== undefined) {
1076
+ // super.emit: the nearest override STRICTLY ABOVE the lexical class.
1077
+ const anc = emitOverrideAtOrAbove(superOf.base);
1078
+ if (!anc)
1079
+ return runtime(receiver, payload);
1080
+ ensureEmitSpec(L, anc, name, tuple);
1081
+ const callee = `%${anc.def.name}.${method}`;
1082
+ L.noteEdge(callee);
1083
+ return { kind: "call", callee, args: [L.upcastTo(receiver, anc.def.name), ...payload], type: BOOL, loc };
1084
+ }
1085
+ const above = emitOverrideAtOrAbove(info);
1086
+ const below = [];
1087
+ collectEmitOverridesBelow(info, below);
1088
+ if (!above && below.length === 0)
1089
+ return runtime(receiver, payload);
1090
+ // Every override class an instance at this site could dispatch through
1091
+ // gets the event's specialization (descendants intercept via the vtable;
1092
+ // the super-forward chain above `above` is ensured as each body lowers).
1093
+ if (above)
1094
+ ensureEmitSpec(L, above, name, tuple);
1095
+ for (const c of below)
1096
+ ensureEmitSpec(L, c, name, tuple);
1097
+ if (above) {
1098
+ if (below.length > 0) {
1099
+ L.noteVirtualEdge(info, method);
1100
+ return { kind: "virtualCall", className: info.def.name, method, args: [receiver, ...payload], type: BOOL, loc };
1101
+ }
1102
+ const callee = `%${above.def.name}.${method}`;
1103
+ L.noteEdge(callee);
1104
+ return { kind: "call", callee, args: [L.upcastTo(receiver, above.def.name), ...payload], type: BOOL, loc };
1105
+ }
1106
+ // Overrides only strictly below: the interned dispatch helper.
1107
+ const recvT = receiver.type;
1108
+ const key = `emitter.emitDispatch:${typeKey(recvT)}:${name}`;
1109
+ let helper = L.arrHofHelpers.get(key);
1110
+ if (!helper) {
1111
+ helper = `%emitter.emitDispatch.${L.arrHofHelpers.size}`;
1112
+ L.arrHofHelpers.set(key, helper);
1113
+ const params = [
1114
+ { localId: "r.0", name: "r", type: recvT },
1115
+ ...tuple.map((t, i) => ({ localId: `a${i}.0`, name: `a${i}`, type: t })),
1116
+ ];
1117
+ const locals = params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false }));
1118
+ const rRef = () => ({ kind: "varRef", localId: "r.0", type: recvT, loc });
1119
+ const argRefs = () => tuple.map((t, i) => ({ kind: "varRef", localId: `a${i}.0`, type: t, loc }));
1120
+ const body = [];
1121
+ for (const o of topmostEmitOverridesBelow(info)) {
1122
+ const oT = { kind: "object", className: o.def.name };
1123
+ const down = { kind: "downcast", value: rRef(), type: oT, loc };
1124
+ const oBelow = [];
1125
+ collectEmitOverridesBelow(o, oBelow);
1126
+ let target;
1127
+ if (oBelow.length > 0) {
1128
+ L.noteVirtualEdge(o, method);
1129
+ target = { kind: "virtualCall", className: o.def.name, method, args: [down, ...argRefs()], type: BOOL, loc };
1130
+ }
1131
+ else {
1132
+ const callee = `%${o.def.name}.${method}`;
1133
+ L.noteEdge(callee);
1134
+ target = { kind: "call", callee, args: [down, ...argRefs()], type: BOOL, loc };
1135
+ }
1136
+ body.push({
1137
+ kind: "if",
1138
+ cond: { kind: "instanceOf", value: rRef(), className: o.def.name, type: BOOL, loc },
1139
+ then: [{ kind: "return", value: target, loc }],
1140
+ else_: null,
1141
+ loc,
1142
+ });
1143
+ }
1144
+ body.push({ kind: "return", value: runtime(rRef(), argRefs()), loc });
1145
+ L.liftedFns.push({ name: helper, params, returnType: BOOL, locals, body, loc });
1146
+ }
1147
+ return { kind: "call", callee: helper, args: [receiver, ...payload], type: BOOL, loc };
1148
+ }
742
1149
  //# sourceMappingURL=lower-emitter.js.map