@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.
- package/ambient/scriptc-node-fallback.d.ts +63 -15
- package/ambient/scriptc.d.ts +2 -2
- package/dist/backend/cc.d.ts +2 -2
- package/dist/backend/emission/emit-async.js +1 -1
- package/dist/backend/emission/emit-exprs.js +131 -54
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-island.js +1 -1
- package/dist/backend/emission/emit-shapes.js +1 -1
- package/dist/backend/emission/emit-walkers.d.ts +14 -14
- package/dist/backend/emission/emit-walkers.js +44 -44
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +3 -3
- package/dist/backend/emission/emitter.js +33 -9
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +1 -1
- package/dist/backend/llvm/dyn.d.ts +10 -10
- package/dist/backend/llvm/dyn.js +26 -26
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +156 -47
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +1 -1
- package/dist/backend/llvm/walkers.js +2 -2
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/coverage/surface-manifest.js +16 -2
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +21 -8
- package/dist/diagnostics/diagnostic.js +48 -14
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.js +5 -5
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +12 -12
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +63 -7
- package/dist/frontend/lowering/lower-builtins.js +327 -35
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +4 -4
- package/dist/frontend/lowering/lower-calls.js +104 -70
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -0
- package/dist/frontend/lowering/lower-classes.js +82 -6
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +9 -5
- package/dist/frontend/lowering/lower-containers.js +88 -26
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.d.ts +70 -3
- package/dist/frontend/lowering/lower-emitter.js +438 -31
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +87 -12
- package/dist/frontend/lowering/lower-exprs.js +630 -125
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +5 -5
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +12 -3
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +8 -5
- package/dist/frontend/lowering/lower-modules.js +157 -63
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +24 -0
- package/dist/frontend/lowering/lower-namespaces.js +158 -16
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.js +11 -11
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +1 -1
- package/dist/frontend/lowering/lower-stmts.js +271 -36
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +171 -20
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +27 -10
- package/dist/frontend/lowering/lowerer.js +156 -54
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +44 -0
- package/dist/frontend/lowering/surfaces.js +236 -5
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm.d.ts +12 -0
- package/dist/frontend/npm.js +30 -0
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.d.ts +25 -0
- package/dist/frontend/program.js +145 -136
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/shared.d.ts +1 -1
- package/dist/frontend/shared.js +8 -8
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/types.d.ts +2 -2
- package/dist/frontend/types.js +15 -15
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +169 -5
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +174 -106
- package/dist/ir/nodes.js +69 -24
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +55 -36
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/fence-eval.js +12 -2
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.d.ts +119 -0
- package/dist/library/int-infer.js +1314 -0
- package/dist/library/int-infer.js.map +1 -0
- package/dist/library/int-infer.test.d.ts +1 -0
- package/dist/library/int-infer.test.js +279 -0
- package/dist/library/int-infer.test.js.map +1 -0
- package/dist/library/profile.d.ts +34 -5
- package/dist/library/profile.js +77 -13
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar-validate.js +1 -1
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.d.ts +24 -1
- package/dist/library/sidecar.js +163 -16
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|