@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.
- package/ambient/scriptc-node-fallback.d.ts +145 -9
- package/dist/backend/cc.d.ts +23 -0
- package/dist/backend/cc.js +89 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.js +2 -2
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +37 -2
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-shapes.js +3 -2
- package/dist/backend/emission/emit-shapes.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +7 -8
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +1 -0
- package/dist/backend/emission/emitter.js +123 -3
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +198 -15
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +1 -0
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +25 -0
- package/dist/diagnostics/diagnostic.js +88 -2
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lib-exports.d.ts +16 -0
- package/dist/frontend/lib-exports.js +35 -0
- package/dist/frontend/lib-exports.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +76 -6
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +37 -4
- package/dist/frontend/lowering/lower-builtins.js +240 -6
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +124 -34
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +34 -2
- package/dist/frontend/lowering/lower-classes.js +257 -34
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +25 -3
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +10 -0
- package/dist/frontend/lowering/lower-exprs.js +411 -10
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +16 -1
- package/dist/frontend/lowering/lower-inspect.js +132 -5
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +25 -4
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.js +4 -2
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +6 -5
- package/dist/frontend/lowering/lower-modules.js +56 -18
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +163 -0
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +72 -2
- package/dist/frontend/lowering/lowerer.js +339 -19
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +9 -2
- package/dist/frontend/lowering/surfaces.js +44 -7
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.d.ts +3 -0
- package/dist/frontend/npm-static-rewrite.js +677 -0
- package/dist/frontend/npm-static-rewrite.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +8 -1
- package/dist/frontend/npm-static.js +159 -18
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.js +2 -2
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.d.ts +6 -0
- package/dist/frontend/program.js +110 -22
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.js +120 -5
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/shared.d.ts +8 -2
- package/dist/frontend/shared.js +22 -6
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/ts7/checker.js +1 -1
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/types.d.ts +25 -1
- package/dist/frontend/types.js +121 -7
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.js +256 -17
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +86 -1
- package/dist/ir/nodes.js +183 -0
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +89 -6
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/profile.d.ts +52 -0
- package/dist/library/profile.js +232 -0
- package/dist/library/profile.js.map +1 -0
- package/package.json +2 -2
- 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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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:
|
|
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`)
|
|
942
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1158
|
-
// gen-spawn wrappers has no story yet); module-level
|
|
1159
|
-
// and object-literal *methods compile
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1411
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|