@scriptc/compiler 0.0.3 → 0.0.5

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 (92) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +145 -9
  2. package/dist/backend/cc.d.ts +23 -0
  3. package/dist/backend/cc.js +89 -0
  4. package/dist/backend/cc.js.map +1 -1
  5. package/dist/backend/emission/emit-async.js +2 -2
  6. package/dist/backend/emission/emit-async.js.map +1 -1
  7. package/dist/backend/emission/emit-exprs.js +37 -2
  8. package/dist/backend/emission/emit-exprs.js.map +1 -1
  9. package/dist/backend/emission/emit-shapes.js +3 -2
  10. package/dist/backend/emission/emit-shapes.js.map +1 -1
  11. package/dist/backend/emission/emit-walkers.js +7 -8
  12. package/dist/backend/emission/emit-walkers.js.map +1 -1
  13. package/dist/backend/emission/emitter.d.ts +1 -0
  14. package/dist/backend/emission/emitter.js +123 -3
  15. package/dist/backend/emission/emitter.js.map +1 -1
  16. package/dist/backend/llvm/emitter.js +198 -15
  17. package/dist/backend/llvm/emitter.js.map +1 -1
  18. package/dist/backend/llvm/shapes.js +1 -0
  19. package/dist/backend/llvm/shapes.js.map +1 -1
  20. package/dist/diagnostics/diagnostic.d.ts +25 -0
  21. package/dist/diagnostics/diagnostic.js +88 -2
  22. package/dist/diagnostics/diagnostic.js.map +1 -1
  23. package/dist/frontend/lib-exports.d.ts +16 -0
  24. package/dist/frontend/lib-exports.js +35 -0
  25. package/dist/frontend/lib-exports.js.map +1 -0
  26. package/dist/frontend/lowering/lib-boundary.js +76 -6
  27. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  28. package/dist/frontend/lowering/lower-builtins.d.ts +37 -4
  29. package/dist/frontend/lowering/lower-builtins.js +240 -6
  30. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  31. package/dist/frontend/lowering/lower-calls.js +124 -34
  32. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  33. package/dist/frontend/lowering/lower-classes.d.ts +34 -2
  34. package/dist/frontend/lowering/lower-classes.js +257 -34
  35. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  36. package/dist/frontend/lowering/lower-containers.js +25 -3
  37. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  38. package/dist/frontend/lowering/lower-exprs.d.ts +10 -0
  39. package/dist/frontend/lowering/lower-exprs.js +411 -10
  40. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  41. package/dist/frontend/lowering/lower-inspect.d.ts +16 -1
  42. package/dist/frontend/lowering/lower-inspect.js +132 -5
  43. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  44. package/dist/frontend/lowering/lower-island.js +25 -4
  45. package/dist/frontend/lowering/lower-island.js.map +1 -1
  46. package/dist/frontend/lowering/lower-mixins.js +4 -2
  47. package/dist/frontend/lowering/lower-mixins.js.map +1 -1
  48. package/dist/frontend/lowering/lower-modules.d.ts +6 -5
  49. package/dist/frontend/lowering/lower-modules.js +56 -18
  50. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  51. package/dist/frontend/lowering/lower-stmts.js +163 -0
  52. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  53. package/dist/frontend/lowering/lowerer.d.ts +72 -2
  54. package/dist/frontend/lowering/lowerer.js +339 -19
  55. package/dist/frontend/lowering/lowerer.js.map +1 -1
  56. package/dist/frontend/lowering/surfaces.d.ts +9 -2
  57. package/dist/frontend/lowering/surfaces.js +44 -7
  58. package/dist/frontend/lowering/surfaces.js.map +1 -1
  59. package/dist/frontend/npm-static-rewrite.d.ts +3 -0
  60. package/dist/frontend/npm-static-rewrite.js +677 -0
  61. package/dist/frontend/npm-static-rewrite.js.map +1 -0
  62. package/dist/frontend/npm-static.d.ts +8 -1
  63. package/dist/frontend/npm-static.js +159 -18
  64. package/dist/frontend/npm-static.js.map +1 -1
  65. package/dist/frontend/npm.js +2 -2
  66. package/dist/frontend/npm.js.map +1 -1
  67. package/dist/frontend/program.d.ts +6 -0
  68. package/dist/frontend/program.js +110 -22
  69. package/dist/frontend/program.js.map +1 -1
  70. package/dist/frontend/resolve.js +120 -5
  71. package/dist/frontend/resolve.js.map +1 -1
  72. package/dist/frontend/shared.d.ts +8 -2
  73. package/dist/frontend/shared.js +22 -6
  74. package/dist/frontend/shared.js.map +1 -1
  75. package/dist/frontend/ts7/checker.js +1 -1
  76. package/dist/frontend/ts7/checker.js.map +1 -1
  77. package/dist/frontend/types.d.ts +25 -1
  78. package/dist/frontend/types.js +121 -7
  79. package/dist/frontend/types.js.map +1 -1
  80. package/dist/index.d.ts +22 -0
  81. package/dist/index.js +256 -17
  82. package/dist/index.js.map +1 -1
  83. package/dist/ir/nodes.d.ts +86 -1
  84. package/dist/ir/nodes.js +183 -0
  85. package/dist/ir/nodes.js.map +1 -1
  86. package/dist/ir/validate.js +89 -6
  87. package/dist/ir/validate.js.map +1 -1
  88. package/dist/library/profile.d.ts +52 -0
  89. package/dist/library/profile.js +232 -0
  90. package/dist/library/profile.js.map +1 -0
  91. package/package.json +2 -2
  92. package/surface-manifest.json +67 -2
@@ -14,17 +14,30 @@ export interface ClassInfo {
14
14
  fieldOrder: {
15
15
  name: string;
16
16
  type: IrType;
17
- initializer: ts.Expression | undefined;
17
+ initializer: ts.Expression | undefined; /** Redeclared INHERITED field: the initializer assigns the base slot at this position; no new slot (def.fields excludes it). */
18
+ redeclared?: true;
18
19
  }[];
19
20
  /** OWN declared methods only — inherited lookups walk the base chain
20
21
  * (findMethodOn). An `abstract` entry is a signature with no body (and
21
22
  * no module function): it declares the vtable slot; concrete subclasses
22
- * fill it (tsc guarantees every instantiable class implements). */
23
+ * fill it (tsc guarantees every instantiable class implements).
24
+ * #PRIVATE members key by their spelled name ('#m', "get:#x") — no
25
+ * public identifier can collide, subclass redeclarations of an
26
+ * inherited private name are fenced at collection, and tsc confines
27
+ * every access site to the declaring class's body, so the base-chain
28
+ * walk IS lexical resolution and privates never join vtables (JS's
29
+ * no-dynamic-dispatch semantics by construction). A `gen` entry is a
30
+ * #private GENERATOR method: the body is a generator IrFunction and
31
+ * calls enter through its gen-spawn wrapper. */
23
32
  methods: Map<string, {
24
33
  params: ParamShape[];
25
34
  ret: IrType;
26
35
  abstract?: true;
27
36
  async?: true;
37
+ gen?: {
38
+ yieldT: IrType;
39
+ nextT: IrType;
40
+ };
28
41
  }>;
29
42
  /** OWN GENERIC instance methods (own type parameters — `m<T>(x: T)`),
30
43
  * monomorphized per call site like top-level generic functions: instance
@@ -39,6 +52,16 @@ export interface ClassInfo {
39
52
  * generic twin of staticMethods (same this/super fence, same
40
53
  * through-a-VALUE shadowing rules via staticShadowBelow). */
41
54
  genericStatics?: Map<string, GenericFnInfo>;
55
+ /** DEFERRED-INIT fields (inherited included, like `fields`): a
56
+ * `stream!: T` definite-assignment assertion (or an SPI-off
57
+ * initializer-less field) whose first assignment happens past the
58
+ * constructor's top level. The SLOT is the undefined-armed union —
59
+ * allocation writes the interned undefined, exactly Node's
60
+ * pre-assignment read — writes wrap into the arm, and every READ is a
61
+ * CHECKED extraction back to the declared type: a genuinely
62
+ * unassigned read throws the catchable TypeError instead of yielding
63
+ * an undefined the declared type cannot hold (SEMANTICS.md). */
64
+ deferredInitFields?: Set<string>;
42
65
  /** null for the builtin error classes (runtime-provided; no source).
43
66
  * Class EXPRESSIONS carry their ts.ClassExpression here — members,
44
67
  * accessors, and locs read identically off either form. */
@@ -509,6 +532,10 @@ export declare function findMethodOn(L: Lowerer, info: ClassInfo | null, name: s
509
532
  ret: IrType;
510
533
  abstract?: true;
511
534
  async?: true;
535
+ gen?: {
536
+ yieldT: IrType;
537
+ nextT: IrType;
538
+ };
512
539
  };
513
540
  } | null;
514
541
  /** True when `sub` is a STRICT descendant of `sup` in the class graph. */
@@ -725,3 +752,8 @@ export declare function lowerNew(L: Lowerer, expr: ts.NewExpression): IrExpr;
725
752
  * this HALF of the accessor (get and set devirtualize independently),
726
753
  * a direct call of the nearest declaration otherwise. */
727
754
  export declare function accessorCall(L: Lowerer, className: string, member: string, obj: IrExpr, extraArgs: IrExpr[], ret: IrType, loc: SrcLoc): IrExpr;
755
+ /** The generic function-like INITIALIZER behind a class FIELD —
756
+ * `time = async <T>(...) => {...}` or `= function g<T>(...) {...}`
757
+ * (parens stripped): bindingGenericFnNodeOf's shape rule, member form.
758
+ * Null when the field isn't that shape. */
759
+ export declare function genericFieldFnNodeOf(member: ts.PropertyDeclaration): ts.FunctionExpression | ts.ArrowFunction | null;
@@ -6,7 +6,7 @@
6
6
  import * as ts from "../ts7/adapter.js";
7
7
  import { BOOL, DYN, F64, JSVAL, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, UNDEFINED_T, URL_T, VOID, arrayOf, isSupportedMapKey, isUnitType, typeEquals } from "../../ir/nodes.js";
8
8
  import { MAX_GENERIC_INSTANCES, genericCallInstance, implicitAnyParamSymbolsOf, implicitCallInstance, implicitMonoFile, omittedArgFor } from "./lower-calls.js";
9
- import { typeKey } from "../types.js";
9
+ import { isGenericCallableMemberType, typeKey } from "../types.js";
10
10
  import { cjsClassExprWholeExportOf, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeTypesPath, locOf } from "../program.js";
11
11
  import { PoisonError, dynFallbackType, dynUndefinedExpr, newFnCtx, own } from "./lowerer.js";
12
12
  import { bufEncoding, lowerMapSeedArrayNew } from "./lower-containers.js";
@@ -833,29 +833,40 @@ mixin) {
833
833
  // `member.cls` backlink fills after the ClassInfo assembles below.
834
834
  const genericMethods = new Map();
835
835
  const genericStatics = new Map();
836
+ // Accepts generic METHODS and instance FIELDS initialized with a
837
+ // generic arrow/function expression (`time = async <T>(...) => {...}`
838
+ // — the field form of a generic method: no closure slot can hold a
839
+ // generic function, so the member collects aside like a method and
840
+ // calls dispatch statically per instantiation; the arrow's lexical
841
+ // `this` IS the instance, exactly the method's param 0). ASYNC
842
+ // members collect too: a generic async instance is an async
843
+ // IrFunction like any other (lowerGenericInstance), calls enter
844
+ // through the instance's own spawn wrapper, and no vtable slot is
845
+ // ever involved — generic members always dispatch statically.
836
846
  const collectGenericMember = (member, isStatic) => {
837
- if (!ts.isIdentifier(member.name)) {
847
+ const fnNode = ts.isPropertyDeclaration(member)
848
+ ? genericFieldFnNodeOf(member)
849
+ : member;
850
+ if (!ts.isIdentifier(member.name) && !ts.isPrivateIdentifier(member.name)) {
838
851
  L.unsupported("SC1090", member, "computed generic method names");
839
852
  }
840
- if (member.asteriskToken)
853
+ if (fnNode.asteriskToken)
841
854
  L.unsupported("SC1071", member);
842
- if (member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
843
- L.unsupported("SC1090", member, "async methods");
844
- }
845
855
  const mName = member.name.text;
846
856
  const typeParams = [];
847
- for (const tp of member.typeParameters) {
857
+ for (const tp of fnNode.typeParameters) {
848
858
  const sym = L.checker.getSymbolAtLocation(tp.name);
849
859
  if (!sym)
850
860
  L.unsupported("SC1090", member, "this method form");
851
861
  typeParams.push(sym);
852
862
  }
853
- for (const param of member.parameters) {
854
- if (!ts.isIdentifier(param.name))
863
+ for (const param of fnNode.parameters) {
864
+ if (!ts.isIdentifier(param.name) && !ts.isObjectBindingPattern(param.name) && !ts.isArrayBindingPattern(param.name)) {
855
865
  L.unsupported("SC1031", param);
866
+ }
856
867
  }
857
868
  (isStatic ? genericStatics : genericMethods).set(mName, {
858
- decl: member,
869
+ decl: fnNode,
859
870
  baseName: mName,
860
871
  qualifiedName: `%${className}.${isStatic ? "static:" : ""}${mName}`,
861
872
  typeParams,
@@ -908,8 +919,17 @@ mixin) {
908
919
  // exists, so the members that would need one — accessors, and
909
920
  // initializer-less fields (undefined until someone assigns
910
921
  // them) — keep the fence, each named at its use site.
922
+ // #PRIVATE statics ride along under their spelled names
923
+ // ('#count' → the module global %g.s.C.#count, '#make' → the
924
+ // module function %C.static:#make): tsc confines every access
925
+ // to the declaring class's body, and the resolution guard in
926
+ // findStaticOn's callers keeps a SUBCLASS-named receiver
927
+ // (`D.#s` — Node's brand TypeError) from resolving up the
928
+ // chain. Class-VALUE receivers fence for privates (a classval
929
+ // slot can hold a descendant at runtime, and only the declaring
930
+ // class object carries the brand in JS).
911
931
  if (ts.isPropertyDeclaration(member) &&
912
- ts.isIdentifier(member.name) &&
932
+ (ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) &&
913
933
  member.initializer &&
914
934
  member.postfixToken?.kind !== ts.SyntaxKind.QuestionToken) {
915
935
  const type = L.irTypeOf(member.name);
@@ -930,7 +950,7 @@ mixin) {
930
950
  // function `%C.static:m` is an async IrFunction (fiber spawn
931
951
  // wrapper), no vtable in sight — statics never dispatch.
932
952
  if (ts.isMethodDeclaration(member) &&
933
- ts.isIdentifier(member.name) &&
953
+ (ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) &&
934
954
  member.body &&
935
955
  member.typeParameters === undefined &&
936
956
  member.asteriskToken === undefined) {
@@ -938,13 +958,14 @@ mixin) {
938
958
  staticMethods.set(member.name.text, { params: shapes, ret: ft.ret, member });
939
959
  }
940
960
  // GENERIC static methods monomorphize like top-level generic
941
- // functions (`%C.static:m%n`); async ones keep the async-method
942
- // use-site fence below.
961
+ // functions (`%C.static:m%n`), async ones included — a generic
962
+ // async instance is an async module function entered through its
963
+ // own spawn wrapper (the async-static precedent above, per
964
+ // instantiation).
943
965
  if (ts.isMethodDeclaration(member) &&
944
- ts.isIdentifier(member.name) &&
966
+ (ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) &&
945
967
  member.body &&
946
- member.typeParameters !== undefined &&
947
- !member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
968
+ member.typeParameters !== undefined) {
948
969
  collectGenericMember(member, true);
949
970
  }
950
971
  // Statics that don't qualify for the module-global/module-
@@ -963,8 +984,28 @@ mixin) {
963
984
  // 7's ClassElement base carries no `name`; read it structurally
964
985
  // (every named member kind stores a PropertyName there).
965
986
  const memberName = member.name;
987
+ // #PRIVATE members compile: their names ('#m') are unspellable by
988
+ // any public identifier, so they ride the ordinary fields/methods
989
+ // maps collision-free — with the base-chain walks doubling as
990
+ // LEXICAL resolution because a subclass re-declaring an inherited
991
+ // private NAME is fenced here (JS would give the two classes
992
+ // DISTINCT private slots under one spelling; one name, one slot is
993
+ // the static story — rename one). tsc guarantees every access site
994
+ // sits inside the declaring class's body, and privates never
995
+ // enter vtables (no redeclaration below ⇒ overrideBelow is false
996
+ // ⇒ every call devirtualizes), which is exactly JS's semantics:
997
+ // lexically bound, no dynamic dispatch, a subclass cannot
998
+ // override.
966
999
  if (memberName && ts.isPrivateIdentifier(memberName)) {
967
- L.unsupported("SC1090", member, "#private class members");
1000
+ const pname = memberName.text;
1001
+ if (base !== null &&
1002
+ (base.fields.has(pname) ||
1003
+ L.findMethodOn(base, pname) !== null ||
1004
+ L.findMethodOn(base, `get:${pname}`) !== null ||
1005
+ L.findMethodOn(base, `set:${pname}`) !== null ||
1006
+ findGenericMethodOn(L, base, pname) !== null)) {
1007
+ L.unsupported("SC1090", memberName, `redeclaring the private name '${pname}' of a base class (JS gives each class its own distinct '${pname}' slot; these layouts have one slot per name — rename one)`);
1008
+ }
968
1009
  }
969
1010
  // The EventEmitter API surface is runtime-provided: a subclass
970
1011
  // member with one of its names would shadow behavior the runtime
@@ -1029,8 +1070,45 @@ mixin) {
1029
1070
  // get/set pair — declare the field and accessors explicitly.
1030
1071
  L.unsupported("SC1090", member, "auto-accessor fields ('accessor x')");
1031
1072
  }
1032
- if (!ts.isIdentifier(member.name))
1073
+ // #private fields ride the ordinary field machinery — the '#'
1074
+ // name is unspellable publicly, so the slot never collides, and
1075
+ // enumeration surfaces (inspect) exclude it like Node.
1076
+ if (!ts.isIdentifier(member.name) && !ts.isPrivateIdentifier(member.name)) {
1033
1077
  L.unsupported("SC1090", member, "computed field names");
1078
+ }
1079
+ // A field initialized with a GENERIC arrow/function expression
1080
+ // (`time = async <T>(label, fn) => {...}` — the Output.time
1081
+ // idiom): a generic MEMBER, not a field. No closure slot can
1082
+ // hold a generic function (the record-shape exclusion rule), so
1083
+ // the member collects like a generic method — no field slot,
1084
+ // static per-instantiation dispatch, `this` as param 0 (the
1085
+ // arrow's lexical `this` IS the instance). Reads of the field as
1086
+ // a VALUE and writes to it fence at their sites — there is no
1087
+ // slot — which enforces never-reassigned by construction.
1088
+ // Guarded on the member TYPE still carrying its type parameters:
1089
+ // an annotation that pins a concrete signature makes an ordinary
1090
+ // closure field, which the normal path below owns.
1091
+ if (genericFieldFnNodeOf(member) !== null &&
1092
+ isGenericCallableMemberType(L.typeOf(member.name), L.checker)) {
1093
+ if (fields.has(member.name.text)) {
1094
+ L.unsupported("SC1090", member.name, "redeclaring inherited fields");
1095
+ }
1096
+ if (L.findMethodOn(base, member.name.text)) {
1097
+ L.unsupported("SC1090", member.name, "fields shadowing inherited methods");
1098
+ }
1099
+ if (findGenericMethodOn(L, base, member.name.text)) {
1100
+ L.unsupported("SC1090", member.name, `redeclaring the inherited generic member '${member.name.text}' (generic members dispatch statically, so the base's call sites could never reach this redeclaration)`);
1101
+ }
1102
+ collectGenericMember(member, false);
1103
+ continue;
1104
+ }
1105
+ // Non-generic fields shadowing an inherited GENERIC member split
1106
+ // the two dispatch worlds (static per-instantiation calls would
1107
+ // never see the field's value) — the method-vs-generic mixing
1108
+ // fence, field form.
1109
+ if (findGenericMethodOn(L, base, member.name.text)) {
1110
+ L.unsupported("SC1090", member.name, `fields shadowing the inherited generic member '${member.name.text}' (generic members dispatch statically and would never reach this field's value)`);
1111
+ }
1034
1112
  // OPTIONAL fields (`a?: string`) are the record-field precedent
1035
1113
  // applied to class shapes: the checker already types the slot
1036
1114
  // `string | undefined`, the allocation writes the interned
@@ -1046,7 +1124,26 @@ mixin) {
1046
1124
  L.unsupported("SC1090", member.name, "'unknown'-typed class fields");
1047
1125
  }
1048
1126
  if (fields.has(member.name.text)) {
1049
- L.unsupported("SC1090", member.name, "redeclaring inherited fields");
1127
+ // REDECLARING an inherited field: Node [[Define]]s the OWN
1128
+ // property again when THIS class's field initializers run
1129
+ // (after super()), so the base slot simply takes the new
1130
+ // value at that position — a slot-type-exact redeclare WITH
1131
+ // an initializer lowers as an assignment into the inherited
1132
+ // slot, no new slot, no layout change (the `class
1133
+ // ConfigError extends Error { name = "ConfigError" }`; the
1134
+ // builtin Error prefix included — reads, toString, and throw
1135
+ // reports all answer the overwritten name like Node). A BARE
1136
+ // redeclare writes undefined in Node (`class B extends A
1137
+ // { x; }` reads undefined!) and a type-changing redeclare has
1138
+ // no single slot type — both keep the fence.
1139
+ const baseType = fields.get(member.name.text);
1140
+ if (member.initializer && typeEquals(type, baseType)) {
1141
+ fieldOrder.push({ name: member.name.text, type, initializer: member.initializer, redeclared: true });
1142
+ continue;
1143
+ }
1144
+ L.unsupported("SC1090", member.name, member.initializer
1145
+ ? "redeclaring inherited fields at a different type"
1146
+ : "redeclaring inherited fields without an initializer (Node resets the field to undefined)");
1050
1147
  }
1051
1148
  if (L.findMethodOn(base, member.name.text)) {
1052
1149
  L.unsupported("SC1090", member.name, "fields shadowing inherited methods");
@@ -1154,11 +1251,22 @@ mixin) {
1154
1251
  const mName = classMemberNameOf(L, member.name);
1155
1252
  if (mName === null)
1156
1253
  L.unsupported("SC1090", member, "computed method names");
1157
- // Generator METHODS stay fenced (virtualCall dispatch over
1158
- // gen-spawn wrappers has no story yet); module-level function*
1159
- // and object-literal *methods compile.
1160
- if (member.asteriskToken !== undefined) {
1161
- L.unsupported("SC1071", member, "generator methods (declare a module-level function* and call it from the method)");
1254
+ // PUBLIC generator METHODS stay fenced (virtualCall dispatch
1255
+ // over gen-spawn wrappers has no story yet); module-level
1256
+ // function* and object-literal *methods compile — and #PRIVATE
1257
+ // generator methods (`*#walk()`) compile below: privates never
1258
+ // enter vtables (a subclass redeclaration is fenced, so
1259
+ // overrideBelow can never flip), every call is a direct call the
1260
+ // emitter routes through the gen-spawn wrapper with `this` as
1261
+ // param 0 — the async-method precedent, generator form.
1262
+ if (member.asteriskToken !== undefined && !ts.isPrivateIdentifier(member.name)) {
1263
+ L.unsupported("SC1071", member, "generator methods (a #private generator method compiles — privates never dispatch dynamically; or declare a module-level function* and call it from the method)");
1264
+ }
1265
+ // An async #private generator (`async *#m()`) is still an async
1266
+ // generator — the blanket SC1071 fence.
1267
+ if (member.asteriskToken !== undefined &&
1268
+ member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
1269
+ L.unsupported("SC1071", member, "async generators (async function*)");
1162
1270
  }
1163
1271
  // An ABSTRACT method is a signature with no body — type-world,
1164
1272
  // except that it declares the vtable slot: calls through
@@ -1302,7 +1410,18 @@ mixin) {
1302
1410
  if (overridden && (asyncMember || overridden.sig.async === true)) {
1303
1411
  L.unsupported("SC1090", member.name, `overriding ${overridden.sig.async === true ? "the async method" : "a method with an async method"} '${mName}' (async methods dispatch statically — the vtable slot machinery has no fiber-spawn story)`);
1304
1412
  }
1305
- methods.set(mName, asyncMember ? { params: shapes, ret: ft.ret, async: true } : { params: shapes, ret: ft.ret });
1413
+ // A #private GENERATOR method carries its channels on the sig:
1414
+ // the body lowers as a generator IrFunction (`this` as param 0),
1415
+ // and every call — direct by construction — enters through the
1416
+ // emitted gen-spawn wrapper, answering the suspended generator.
1417
+ if (member.asteriskToken !== undefined) {
1418
+ if (ft.ret.kind !== "generator")
1419
+ L.badType(member.name, L.typeOf(member.name));
1420
+ methods.set(mName, { params: shapes, ret: ft.ret, gen: { yieldT: ft.ret.yieldT, nextT: ft.ret.nextT } });
1421
+ }
1422
+ else {
1423
+ methods.set(mName, asyncMember ? { params: shapes, ret: ft.ret, async: true } : { params: shapes, ret: ft.ret });
1424
+ }
1306
1425
  // Overrides keep the inherited ABI exactly, so only non-override
1307
1426
  // methods may still refine once the ctor scan runs.
1308
1427
  if (ft.ret.kind === "dyn" && !overridden) {
@@ -1317,7 +1436,9 @@ mixin) {
1317
1436
  // whole-program devirtualization, vtable slots, may-throw) then
1318
1437
  // applies verbatim, with the get and set halves independent.
1319
1438
  const isGet = ts.isGetAccessor(member);
1320
- if (!ts.isIdentifier(member.name)) {
1439
+ // #private accessors collect as "get:#x"/"set:#x" — the same
1440
+ // reserved spelling, one more unspellable segment.
1441
+ if (!ts.isIdentifier(member.name) && !ts.isPrivateIdentifier(member.name)) {
1321
1442
  L.unsupported("SC1090", member, "computed accessor names");
1322
1443
  }
1323
1444
  // ABSTRACT accessors are the abstract-method story with property
@@ -1395,6 +1516,7 @@ mixin) {
1395
1516
  // (conditional branches, assignment in a method, no constructor at
1396
1517
  // all) leaves a window where Node reads undefined and these layouts
1397
1518
  // would read zeroed memory, so it fences instead.
1519
+ const deferredInitFields = new Set(base?.deferredInitFields ?? []);
1398
1520
  if (unguardedFields.length > 0) {
1399
1521
  const topAssigned = new Set();
1400
1522
  for (const stmt of ctor?.body?.statements ?? []) {
@@ -1407,8 +1529,32 @@ mixin) {
1407
1529
  }
1408
1530
  }
1409
1531
  for (const f of unguardedFields) {
1410
- if (!topAssigned.has(f.name))
1411
- L.unsupported("SC1090", f.node, f.why);
1532
+ if (topAssigned.has(f.name))
1533
+ continue;
1534
+ // DEFERRED INITIALIZATION (the Output.initialize idiom —
1535
+ // `stream!: T` assigned inside a method the constructor calls):
1536
+ // the slot becomes the undefined-armed union — allocation writes
1537
+ // the interned undefined (Node's pre-assignment value), writes
1538
+ // wrap, and reads CHECKED-extract the declared type, trapping a
1539
+ // genuinely-unassigned read with the catchable TypeError.
1540
+ // Only single-arm declared types take the deferral (the checked
1541
+ // extraction targets one arm); union-typed `!` fields keep the
1542
+ // fence.
1543
+ const declared = fields.get(f.name);
1544
+ const armable = declared !== undefined && !isUnitType(declared) &&
1545
+ declared.kind !== "union" && declared.kind !== "jsval" && declared.kind !== "dyn" &&
1546
+ declared.kind !== "map" && declared.kind !== "generator" && declared.kind !== "void" &&
1547
+ declared.kind !== "caught";
1548
+ const armed = armable ? L.withUndefinedArm(declared) : null;
1549
+ if (armed !== null && armed.kind === "union") {
1550
+ fields.set(f.name, armed);
1551
+ const fo = fieldOrder.find((x) => x.name === f.name);
1552
+ if (fo)
1553
+ fo.type = armed;
1554
+ deferredInitFields.add(f.name);
1555
+ continue;
1556
+ }
1557
+ L.unsupported("SC1090", f.node, f.why);
1412
1558
  }
1413
1559
  }
1414
1560
  // Partial overrides of an inherited accessor pair. JS gives the
@@ -1672,9 +1818,11 @@ mixin) {
1672
1818
  ...(base ? { base: base.def.name } : {}),
1673
1819
  // Layout order: the base chain's fields as an IDENTICAL prefix,
1674
1820
  // then this class's own — what makes an upcast a reinterpret.
1821
+ // Redeclared INHERITED fields contribute no slot (their
1822
+ // initializers assign the prefix slot).
1675
1823
  fields: [
1676
1824
  ...(base?.def.fields ?? []),
1677
- ...fieldOrder.map((f) => ({ name: f.name, type: f.type })),
1825
+ ...fieldOrder.filter((f) => f.redeclared !== true).map((f) => ({ name: f.name, type: f.type })),
1678
1826
  ],
1679
1827
  ...(methods.size > 0 ? { methods: [...methods.keys()] } : {}),
1680
1828
  ...(abstractClass ? { abstract: true } : {}),
@@ -1700,6 +1848,7 @@ mixin) {
1700
1848
  ...(staticBlocks.length > 0 ? { staticBlocks } : {}),
1701
1849
  ...(symbolFields.size > 0 ? { symbolFields } : {}),
1702
1850
  ...(classDecoratorNodes.length > 0 ? { classDecorators: { nodes: classDecoratorNodes } } : {}),
1851
+ ...(deferredInitFields.size > 0 ? { deferredInitFields } : {}),
1703
1852
  };
1704
1853
  // GENERIC members get their declaring-class backlink now that the
1705
1854
  // info exists (instance lowering reads it for `this` typing and the
@@ -2324,6 +2473,13 @@ export function lowerStaticFieldRead(L, expr) {
2324
2473
  return null;
2325
2474
  const loc = locOf(expr);
2326
2475
  const found = findStaticOn(L, info, expr.name.text);
2476
+ // A #private static resolves only through the DECLARING class's own
2477
+ // name: in JS the brand lives on that one constructor object, so
2478
+ // `D.#s` (a subclass receiver) throws Node's TypeError instead of
2479
+ // reaching up the chain — fenced rather than silently resolved.
2480
+ if (found && expr.name.text.startsWith("#") && found.declarer !== info) {
2481
+ L.unsupported("SC1090", expr, `reading the private static '${expr.name.text}' through the subclass '${info.def.name.replace(/^%|^%m\d+\./, "")}' (JS brands the declaring class object alone — Node throws a TypeError here; spell the declaring class's name)`);
2482
+ }
2327
2483
  if (found?.field !== undefined) {
2328
2484
  return L.maybeNarrow({ kind: "varRef", localId: found.field.globalId, type: found.field.type, loc }, expr);
2329
2485
  }
@@ -2648,7 +2804,17 @@ export function lowerStaticMethodCall(L, call, access) {
2648
2804
  }
2649
2805
  function staticCallOn(L, call, access, info, throughValue) {
2650
2806
  const loc = locOf(call);
2807
+ // #private statics: through-a-VALUE receivers fence (a classval slot
2808
+ // can hold a descendant at runtime and only the declaring class
2809
+ // object carries the brand in JS), and the direct spelling resolves
2810
+ // only on the declaring class itself — `D.#s` is Node's TypeError.
2811
+ if (access.name.text.startsWith("#") && throughValue) {
2812
+ L.unsupported("SC1090", call, `calling the private static '${access.name.text}' through a class value (JS brands the declaring class object alone — call it through the class's own name)`);
2813
+ }
2651
2814
  const found = findStaticOn(L, info, access.name.text);
2815
+ if (found && access.name.text.startsWith("#") && found.declarer !== info) {
2816
+ L.unsupported("SC1090", call, `calling the private static '${access.name.text}' through the subclass '${info.def.name.replace(/^%|^%m\d+\./, "")}' (JS brands the declaring class object alone — Node throws a TypeError here; spell the declaring class's name)`);
2817
+ }
2652
2818
  if (!found) {
2653
2819
  // GENERIC static methods: monomorphized like top-level generic
2654
2820
  // functions, called directly as `%C.static:m%n` — with the same
@@ -2731,6 +2897,14 @@ export function lowerClassValueProperty(L, expr) {
2731
2897
  if (!found) {
2732
2898
  L.unsupported("SC1090", expr, `the static member '${member}' of class '${info.def.name.replace(/^%|^%m\d+\./, "")}' (static accessors and initializer-less static fields have no lowering, and Function members like .call/.bind/.prototype have no value form)`);
2733
2899
  }
2900
+ // #private statics never read through class VALUES: the slot can hold
2901
+ // a descendant at runtime and JS brands the declaring class object
2902
+ // alone (Node's TypeError on any other receiver). The direct
2903
+ // class-name spelling resolved in lowerStaticFieldRead, so a private
2904
+ // reaching here is a classval-typed binding.
2905
+ if (member.startsWith("#")) {
2906
+ L.unsupported("SC1090", expr, `reading the private static '${member}' through a class value (JS brands the declaring class object alone — spell the declaring class's name)`);
2907
+ }
2734
2908
  if (staticShadowBelow(L, info, member)) {
2735
2909
  L.unsupported("SC1090", expr, `reading the static member '${member}' through a class value (a subclass of '${info.def.name.replace(/^%|^%m\d+\./, "")}' redeclares it, so the runtime class decides which declaration answers)`);
2736
2910
  }
@@ -3091,6 +3265,10 @@ function lowerClassCtorInner(L, info) {
3091
3265
  export function classMemberNameOf(L, name) {
3092
3266
  if (ts.isIdentifier(name))
3093
3267
  return name.text;
3268
+ // #private methods key by their spelled name ('#m') — '#' is
3269
+ // unspellable in public identifiers, the accessor-colon precedent.
3270
+ if (ts.isPrivateIdentifier(name))
3271
+ return name.text;
3094
3272
  if (!ts.isComputedPropertyName(name))
3095
3273
  return null;
3096
3274
  let e = name.expression;
@@ -3305,7 +3483,7 @@ export function lowerClassMethodMember(L, info, fnLike) {
3305
3483
  function lowerClassMethodMemberInner(L, info, fnLike) {
3306
3484
  const className = info.def.name;
3307
3485
  const thisType = { kind: "object", className };
3308
- const memberName = ts.isMethodDeclaration(fnLike) ? classMemberNameOf(L, fnLike.name) : ts.isIdentifier(fnLike.name) ? fnLike.name.text : null;
3486
+ const memberName = ts.isMethodDeclaration(fnLike) ? classMemberNameOf(L, fnLike.name) : ts.isIdentifier(fnLike.name) || ts.isPrivateIdentifier(fnLike.name) ? fnLike.name.text : null;
3309
3487
  if (memberName === null)
3310
3488
  return null;
3311
3489
  const mName = ts.isMethodDeclaration(fnLike)
@@ -3323,9 +3501,21 @@ function lowerClassMethodMemberInner(L, info, fnLike) {
3323
3501
  // spawn's argument pack). Dispatch is static by construction — the
3324
3502
  // override fence at collection keeps async methods out of vtables.
3325
3503
  const isAsync = sig.async === true && sig.ret.kind === "promise";
3326
- const bodyReturn = isAsync && sig.ret.kind === "promise" ? sig.ret.inner : sig.ret;
3504
+ // #PRIVATE GENERATOR methods: the module function is a generator
3505
+ // IrFunction — the body returns the TReturn channel, yields ride
3506
+ // ctx.generator, and every call (direct by construction — privates
3507
+ // never virtualize) enters through the emitted gen-spawn wrapper with
3508
+ // `this` in the argument pack, answering the suspended generator.
3509
+ const genCh = sig.gen !== undefined && sig.ret.kind === "generator" ? sig.gen : null;
3510
+ const bodyReturn = isAsync && sig.ret.kind === "promise"
3511
+ ? sig.ret.inner
3512
+ : genCh !== null
3513
+ ? L.genBodyReturnType(sig.ret)
3514
+ : sig.ret;
3327
3515
  const fnCtx = newFnCtx(false, null, null, bodyReturn);
3328
3516
  fnCtx.isAsync = isAsync;
3517
+ if (genCh !== null)
3518
+ fnCtx.generator = genCh;
3329
3519
  L.fnStack.push(fnCtx);
3330
3520
  try {
3331
3521
  const thisLocal = L.declareThis(thisType);
@@ -3345,6 +3535,8 @@ function lowerClassMethodMemberInner(L, info, fnLike) {
3345
3535
  };
3346
3536
  if (isAsync)
3347
3537
  fn.async = true;
3538
+ if (genCh !== null)
3539
+ fn.generator = genCh;
3348
3540
  return fn;
3349
3541
  }
3350
3542
  finally {
@@ -4001,7 +4193,7 @@ export function lowerNew(L, expr) {
4001
4193
  (isModuleExportsAccess(expr.expression.expression) ||
4002
4194
  (ts.isIdentifier(expr.expression.expression) &&
4003
4195
  expr.expression.expression.text === "exports" &&
4004
- !L.resolveLocal(expr.expression.expression) &&
4196
+ !L.peekLocal(expr.expression.expression) &&
4005
4197
  !L.globalOf(expr.expression.expression)))) ||
4006
4198
  L.cjsLocalModuleBindingOf(expr.expression.expression))) {
4007
4199
  // Candidate symbols for the export global: the member symbol as
@@ -4369,7 +4561,22 @@ export function lowerNew(L, expr) {
4369
4561
  if (!ts.isSpreadElement(argNode)) {
4370
4562
  const argIr = L.mapTypeOf(L.typeOf(argNode));
4371
4563
  if (argIr?.kind === "array" && typeEquals(argIr.elem, mapped.elem)) {
4372
- return { kind: "setNew", seed: L.lowerExpr(argNode), type: mapped, loc };
4564
+ let seed = L.lowerExpr(argNode);
4565
+ // A T[]-DECLARED seed whose value is an island handle (a
4566
+ // package's exported array — the binding never held a
4567
+ // static array): the VALIDATED exit copies the engine
4568
+ // array out (strict elements, the catchable TypeError on a
4569
+ // lying handle), and the bulk add proceeds on the copy —
4570
+ // construction reads the seed once, so the aliasing
4571
+ // divergence has nothing to observe.
4572
+ if (seed.type.kind === "jsval" && L.boundaryExitSafe(arrayOf(mapped.elem))) {
4573
+ seed = { kind: "jsExit", value: seed, type: arrayOf(mapped.elem), loc: seed.loc };
4574
+ }
4575
+ if (typeEquals(seed.type, arrayOf(mapped.elem))) {
4576
+ return { kind: "setNew", seed, type: mapped, loc };
4577
+ }
4578
+ // Any other lowered kind falls through to the named fence
4579
+ // below — never a mistyped seed into the validator.
4373
4580
  }
4374
4581
  }
4375
4582
  }
@@ -4698,4 +4905,20 @@ export function accessorCall(L, className, member, obj, extraArgs, ret, loc) {
4698
4905
  loc,
4699
4906
  };
4700
4907
  }
4908
+ /** The generic function-like INITIALIZER behind a class FIELD —
4909
+ * `time = async <T>(...) => {...}` or `= function g<T>(...) {...}`
4910
+ * (parens stripped): bindingGenericFnNodeOf's shape rule, member form.
4911
+ * Null when the field isn't that shape. */
4912
+ export function genericFieldFnNodeOf(member) {
4913
+ if (member.initializer === undefined)
4914
+ return null;
4915
+ let init = member.initializer;
4916
+ while (ts.isParenthesizedExpression(init))
4917
+ init = init.expression;
4918
+ if ((ts.isArrowFunction(init) || ts.isFunctionExpression(init)) &&
4919
+ init.typeParameters !== undefined && init.body !== undefined) {
4920
+ return init;
4921
+ }
4922
+ return null;
4923
+ }
4701
4924
  //# sourceMappingURL=lower-classes.js.map