@scriptc/compiler 0.2.0 → 0.2.1
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 +25 -15
- package/dist/backend/cycle-analysis.js +1 -0
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/llvm/classes.js +21 -5
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +5 -0
- package/dist/backend/llvm/dyn.js +166 -99
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +2 -2
- package/dist/backend/llvm/emitter.js +79 -20
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +28 -17
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +1 -1
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +18 -9
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +11 -1
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +11 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +125 -17
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +23 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +45 -2
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/shapes.js +8 -2
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +3 -6
- package/dist/backend/native-toolchain.js +53 -23
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +72 -0
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-native.js +4 -1
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/backend/targets.js +10 -6
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +7 -0
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/compat/fetch-profile.js +34 -4
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -7
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
- package/dist/frontend/lowering/class-callbacks.js +26 -15
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-construction.d.ts +12 -0
- package/dist/frontend/lowering/class-construction.js +156 -0
- package/dist/frontend/lowering/class-construction.js.map +1 -0
- package/dist/frontend/lowering/class-descriptors.d.ts +1 -0
- package/dist/frontend/lowering/class-descriptors.js +46 -5
- package/dist/frontend/lowering/class-descriptors.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +11 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +403 -48
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +2 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
- package/dist/frontend/lowering/class-instance-constructor.js +4 -1
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
- package/dist/frontend/lowering/class-method-values.d.ts +4 -1
- package/dist/frontend/lowering/class-method-values.js +72 -7
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.d.ts +5 -2
- package/dist/frontend/lowering/class-prototypes.js +121 -13
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-runtime-statics.d.ts +8 -0
- package/dist/frontend/lowering/class-runtime-statics.js +66 -0
- package/dist/frontend/lowering/class-runtime-statics.js.map +1 -0
- package/dist/frontend/lowering/class-static-data.js +2 -2
- package/dist/frontend/lowering/class-static-data.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +49 -13
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +19 -47
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/error-methods.d.ts +4 -1
- package/dist/frontend/lowering/error-methods.js +100 -1
- package/dist/frontend/lowering/error-methods.js.map +1 -1
- package/dist/frontend/lowering/expressions/native-proxy.js +3 -1
- package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
- package/dist/frontend/lowering/expressions/nullish-assignment.js +19 -11
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +38 -7
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +3 -4
- package/dist/frontend/lowering/expressions/optional-chains.js +69 -17
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/function-context.d.ts +11 -3
- package/dist/frontend/lowering/function-context.js +11 -5
- package/dist/frontend/lowering/function-context.js.map +1 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +1 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js +39 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.d.ts +2 -0
- package/dist/frontend/lowering/lib-boundary.js +54 -1
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.d.ts +7 -1
- package/dist/frontend/lowering/lower-builtin-values.js +113 -3
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +6 -5
- package/dist/frontend/lowering/lower-builtins.js +156 -41
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +8 -5
- package/dist/frontend/lowering/lower-calls.js +448 -83
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +13 -13
- package/dist/frontend/lowering/lower-classes.js +650 -176
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +9 -0
- package/dist/frontend/lowering/lower-containers.js +135 -36
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.js +3 -2
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +2 -2
- package/dist/frontend/lowering/lower-exprs.js +531 -126
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +12 -1
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +2 -0
- package/dist/frontend/lowering/lower-island.js +32 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +220 -17
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-modules.test.js +39 -0
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +7 -3
- package/dist/frontend/lowering/lower-stmts.js +135 -26
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +1 -1
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +27 -1
- package/dist/frontend/lowering/lowerer.js +172 -50
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +149 -3
- package/dist/frontend/lowering/lowerer.test.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +2 -0
- package/dist/frontend/lowering/surfaces.js +25 -8
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.d.ts +2 -0
- package/dist/frontend/lowering/text-decoder-encoding.js +5 -0
- package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
- package/dist/frontend/lowering/unregistered-class-types.test.js +3 -3
- package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -1
- package/dist/frontend/npm-static-prune.js +116 -34
- package/dist/frontend/npm-static-prune.js.map +1 -1
- package/dist/frontend/npm-static-prune.test.js +8 -0
- package/dist/frontend/npm-static-prune.test.js.map +1 -1
- package/dist/frontend/program.d.ts +1 -1
- package/dist/frontend/program.js +53 -24
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +18 -0
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/ts7/ast.js +4 -3
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/semantic-json.js +5 -10
- package/dist/frontend/ts7/semantic-json.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +9 -1
- package/dist/frontend/type-mapper.js +90 -13
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +23 -0
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/frontend/union-discriminants.d.ts +1 -1
- package/dist/frontend/union-discriminants.js +2 -2
- package/dist/frontend/union-discriminants.js.map +1 -1
- package/dist/ir/analysis.js +2 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +52 -33
- package/dist/ir/ir.js +142 -42
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +35 -0
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/serialize.js +15 -3
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +5 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/validate.js +80 -11
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +61 -1
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/native/sanitizer.js +3 -1
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/native/toolchain.d.ts +1 -1
- package/dist/native/toolchain.js +26 -6
- package/dist/native/toolchain.js.map +1 -1
- package/dist/native/toolchain.test.js +53 -1
- package/dist/native/toolchain.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +56 -81
|
@@ -6,11 +6,11 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
6
6
|
* hierarchy registration. */
|
|
7
7
|
import * as ts from "../ts7/adapter.js";
|
|
8
8
|
import { BOOL, DATE_T, DYN, F64, bytesOf, JSVAL, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, UNDEFINED_T, VOID, arrayOf, isSupportedMapKey, isSupportedMapValue, isSupportedSetElem, isUnitType, typeEquals } from "../../ir/ir.js";
|
|
9
|
-
import { MAX_GENERIC_INSTANCES, appendImplicitUndefinedReturn, bodyReadsArguments, generatorMeta, genericCallInstance, implicitAnyParamSymbolsOf, implicitCallInstance, implicitMonoFile, omittedArgFor } from "./lower-calls.js";
|
|
9
|
+
import { MAX_GENERIC_INSTANCES, appendImplicitUndefinedReturn, bodyReadsArguments, generatorMeta, genericCallInstance, hasExplicitJsDocReturn, implicitAnyParamSymbolsOf, implicitCallInstance, implicitMonoFile, omittedArgFor } from "./lower-calls.js";
|
|
10
10
|
import { isGenericCallableMemberType, jsOpenObjectType, typeKey, withUnitArm } from "../type-mapper.js";
|
|
11
11
|
import { cjsClassExprWholeExportOf, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeTypesPath, locOf } from "../program.js";
|
|
12
|
-
import { PoisonError, dynFallbackType, dynUndefinedExpr, newFnCtx, own } from "./lowerer.js";
|
|
13
|
-
import { lowerArrayConstructor, lowerMapSeedNew, lowerSetSeedNew } from "./lower-containers.js";
|
|
12
|
+
import { PoisonError, dynFallbackType, dynUndefinedExpr, newFnCtx, nodeThrowExpr, own } from "./lowerer.js";
|
|
13
|
+
import { lowerArrayConstructor, lowerMapSeedNew, lowerObjectConstructor, lowerSetSeedNew } from "./lower-containers.js";
|
|
14
14
|
import { bufEncoding } from "./containers/bytes.js";
|
|
15
15
|
import { isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
16
16
|
import { lowerSearchParamsNew, lowerTextCodecNew } from "./lower-builtins.js";
|
|
@@ -26,10 +26,13 @@ import { mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
|
26
26
|
import { hasLexicalPrivateReference, hasStaticThis, rejectStaticThis } from "./static-this.js";
|
|
27
27
|
import { lowerUrlNew } from "./lower-url.js";
|
|
28
28
|
import { isNativeProxyInitializer, lowerNativeProxy } from "./expressions/native-proxy.js";
|
|
29
|
-
import { classPrototypeData } from "./class-prototypes.js";
|
|
30
29
|
import { classStaticDataFor } from "./class-static-data.js";
|
|
31
30
|
import { lowerObjectFactoryNew } from "./object-factory-new.js";
|
|
31
|
+
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
|
32
32
|
import { lowerInstanceConstructorNew } from "./class-instance-constructor.js";
|
|
33
|
+
import { classPrototypeData } from "./class-prototypes.js";
|
|
34
|
+
import { checkedClassConstruction } from "./class-construction.js";
|
|
35
|
+
import { initializeRuntimeStatics, hasRuntimeStatics } from "./class-runtime-statics.js";
|
|
33
36
|
export function returnsOnlyThis(member) {
|
|
34
37
|
const body = member.body;
|
|
35
38
|
const last = body?.statements.at(-1);
|
|
@@ -58,11 +61,20 @@ function functionLocalClass(decl) {
|
|
|
58
61
|
}
|
|
59
62
|
return false;
|
|
60
63
|
}
|
|
61
|
-
export function storedClassValueType(lowerer, expression) {
|
|
64
|
+
export function storedClassValueType(lowerer, expression, visiting = new Set()) {
|
|
65
|
+
if (visiting.has(expression))
|
|
66
|
+
return null;
|
|
67
|
+
visiting.add(expression);
|
|
68
|
+
if (ts.isParenthesizedExpression(expression))
|
|
69
|
+
return storedClassValueType(lowerer, expression.expression, visiting);
|
|
70
|
+
if (ts.isClassExpression(expression) && !functionLocalClass(expression) && !lowerer.collectingExprClasses.has(expression)) {
|
|
71
|
+
const info = lowerer.lowerClassExpressionInfo(expression);
|
|
72
|
+
return { kind: "classval", className: info.def.name };
|
|
73
|
+
}
|
|
62
74
|
if (ts.isPropertyAccessExpression(expression) && !expression.questionDotToken) {
|
|
63
75
|
const member = nsMemberIdentOf(lowerer, expression);
|
|
64
76
|
if (member)
|
|
65
|
-
return storedClassValueType(lowerer, member);
|
|
77
|
+
return storedClassValueType(lowerer, member, visiting);
|
|
66
78
|
}
|
|
67
79
|
if (expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
68
80
|
const stored = lowerer.resolveThis();
|
|
@@ -71,10 +83,149 @@ export function storedClassValueType(lowerer, expression) {
|
|
|
71
83
|
}
|
|
72
84
|
if (ts.isIdentifier(expression)) {
|
|
73
85
|
const stored = lowerer.peekLocal(expression) ?? lowerer.globalOf(expression);
|
|
74
|
-
if (stored?.type.kind === "classval")
|
|
86
|
+
if (stored?.type.kind === "classval" && lowerer.classes.has(stored.type.className))
|
|
75
87
|
return stored.type;
|
|
88
|
+
const symbol = lowerer.resolveValueSymbol(expression);
|
|
89
|
+
const declaration = symbol && lowerer.checker.declarationsOf(symbol).find((decl) => ts.isVariableDeclaration(decl) && decl.initializer && (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0);
|
|
90
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
91
|
+
const value = storedClassValueType(lowerer, declaration.initializer, visiting);
|
|
92
|
+
if (value?.kind === "classval")
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const type = lowerer.typeOf(expression);
|
|
97
|
+
const mapped = lowerer.mapTypeOf(type);
|
|
98
|
+
if (mapped?.kind === "classval" && !lowerer.classes.has(mapped.className)) {
|
|
99
|
+
for (const signature of lowerer.checker.getConstructSignatures(type)) {
|
|
100
|
+
const constructor = lowerer.checker.signatureDeclaration(signature);
|
|
101
|
+
const declaration = constructor && ts.isConstructorDeclaration(constructor) ? constructor.parent : undefined;
|
|
102
|
+
if (declaration && ts.isClassExpression(declaration) && !functionLocalClass(declaration) &&
|
|
103
|
+
!lowerer.collectingExprClasses.has(declaration)) {
|
|
104
|
+
const info = lowerer.lowerClassExpressionInfo(declaration);
|
|
105
|
+
return { kind: "classval", className: info.def.name };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (mapped?.kind === "classval" || !ts.isCallExpression(expression))
|
|
110
|
+
return mapped;
|
|
111
|
+
let callee = expression.expression;
|
|
112
|
+
let calls = 1;
|
|
113
|
+
const aliases = new Set();
|
|
114
|
+
for (;;) {
|
|
115
|
+
if (aliases.has(callee))
|
|
116
|
+
break;
|
|
117
|
+
aliases.add(callee);
|
|
118
|
+
if (ts.isParenthesizedExpression(callee)) {
|
|
119
|
+
callee = callee.expression;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (ts.isCallExpression(callee)) {
|
|
123
|
+
calls++;
|
|
124
|
+
callee = callee.expression;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
128
|
+
const member = nsMemberIdentOf(lowerer, callee);
|
|
129
|
+
if (member) {
|
|
130
|
+
callee = member;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (ts.isIdentifier(callee)) {
|
|
135
|
+
const symbol = lowerer.resolveValueSymbol(callee);
|
|
136
|
+
const declaration = symbol && lowerer.checker.declarationsOf(symbol).find((decl) => ts.isVariableDeclaration(decl) && decl.initializer && (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0);
|
|
137
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
138
|
+
callee = declaration.initializer;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
if ((ts.isArrowFunction(callee) || ts.isFunctionExpression(callee)) && isJsSourceFile(callee.getSourceFile()) &&
|
|
145
|
+
!callee.typeParameters?.length && !lowerer.preparingClassFactories.has(callee)) {
|
|
146
|
+
const signature = lowerer.checker.getSignatureFromDeclaration(callee);
|
|
147
|
+
let result = signature && lowerer.checker.getReturnTypeOfSignature(signature);
|
|
148
|
+
for (let i = 1; result && i < calls; i++) {
|
|
149
|
+
const signatures = lowerer.checker.getCallSignatures(result);
|
|
150
|
+
result = signatures.length === 1 ? lowerer.checker.getReturnTypeOfSignature(signatures[0]) : undefined;
|
|
151
|
+
}
|
|
152
|
+
if (result && lowerer.checker.getConstructSignatures(result).length > 0) {
|
|
153
|
+
const returned = (value) => {
|
|
154
|
+
let type = value.type;
|
|
155
|
+
for (let i = 0; i < calls && type.kind === "func"; i++)
|
|
156
|
+
type = type.ret;
|
|
157
|
+
return type.kind === "classval" ? type : null;
|
|
158
|
+
};
|
|
159
|
+
const home = callee.parent;
|
|
160
|
+
const generic = ts.isVariableDeclaration(home) && ts.isIdentifier(home.name) ? lowerer.genericFnOf(home.name) : null;
|
|
161
|
+
if (generic?.implicitParams && ts.isVariableDeclaration(home) && ts.isIdentifier(home.name)) {
|
|
162
|
+
const factory = lowerer.lowerGenericFnValue(home.name, generic);
|
|
163
|
+
const type = returned(factory);
|
|
164
|
+
if (type)
|
|
165
|
+
return type;
|
|
166
|
+
}
|
|
167
|
+
lowerer.preparingClassFactories.add(callee);
|
|
168
|
+
const collecting = lowerer.collecting, sink = lowerer.diagSink;
|
|
169
|
+
lowerer.collecting = false;
|
|
170
|
+
lowerer.diagSink = null;
|
|
171
|
+
try {
|
|
172
|
+
const factory = lowerer.preparedClassFactories.get(callee) ?? lowerer.lowerLambda(callee);
|
|
173
|
+
lowerer.preparedClassFactories.set(callee, factory);
|
|
174
|
+
const type = returned(factory);
|
|
175
|
+
if (type)
|
|
176
|
+
return type;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
lowerer.collecting = collecting;
|
|
180
|
+
lowerer.diagSink = sink;
|
|
181
|
+
lowerer.preparingClassFactories.delete(callee);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// A checked call can erase its returned constructor's ABI while the
|
|
186
|
+
// checker still identifies the class declaration. Collect that class in
|
|
187
|
+
// its factory's lexical scope before choosing the derived native layout.
|
|
188
|
+
// Cache the module-level closure so its captures and lifted bodies are
|
|
189
|
+
// exactly the ones used when the declaration executes.
|
|
190
|
+
for (const signature of lowerer.checker.getConstructSignatures(type)) {
|
|
191
|
+
const declaration = lowerer.checker.signatureDeclaration(signature);
|
|
192
|
+
const cls = declaration && ts.isConstructorDeclaration(declaration) ? declaration.parent : null;
|
|
193
|
+
if (!cls || (!ts.isClassExpression(cls) && !ts.isClassDeclaration(cls)))
|
|
194
|
+
continue;
|
|
195
|
+
let factory;
|
|
196
|
+
for (let parent = cls.parent; parent && !ts.isSourceFile(parent); parent = parent.parent) {
|
|
197
|
+
if (ts.isArrowFunction(parent) || ts.isFunctionExpression(parent))
|
|
198
|
+
factory = parent;
|
|
199
|
+
else if (ts.isFunctionLike(parent)) {
|
|
200
|
+
factory = undefined;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (!factory || !isJsSourceFile(factory.getSourceFile()) || factory.typeParameters?.length ||
|
|
205
|
+
!ts.isVariableDeclaration(factory.parent) || factory.parent.initializer !== factory ||
|
|
206
|
+
!ts.isVariableDeclarationList(factory.parent.parent) || !(factory.parent.parent.flags & ts.NodeFlags.Const) ||
|
|
207
|
+
!ts.isVariableStatement(factory.parent.parent.parent) || !ts.isSourceFile(factory.parent.parent.parent.parent) ||
|
|
208
|
+
lowerer.preparingClassFactories.has(factory))
|
|
209
|
+
continue;
|
|
210
|
+
if (!lowerer.preparedClassFactories.has(factory)) {
|
|
211
|
+
lowerer.preparingClassFactories.add(factory);
|
|
212
|
+
const collecting = lowerer.collecting, sink = lowerer.diagSink;
|
|
213
|
+
lowerer.collecting = false;
|
|
214
|
+
lowerer.diagSink = null;
|
|
215
|
+
try {
|
|
216
|
+
lowerer.preparedClassFactories.set(factory, lowerer.lowerLambda(factory));
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
lowerer.collecting = collecting;
|
|
220
|
+
lowerer.diagSink = sink;
|
|
221
|
+
lowerer.preparingClassFactories.delete(factory);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const resolved = lowerer.mapTypeOf(type);
|
|
225
|
+
if (resolved?.kind === "classval")
|
|
226
|
+
return resolved;
|
|
76
227
|
}
|
|
77
|
-
return
|
|
228
|
+
return mapped;
|
|
78
229
|
}
|
|
79
230
|
/** The KEY symbol behind a LATE-BOUND (`__@name@id`) property: resolved
|
|
80
231
|
* from the argument of the element-access assignment that declared it
|
|
@@ -186,6 +337,10 @@ export function registerBuiltinErrorClasses(lowerer) {
|
|
|
186
337
|
{ name: "%code", type: STRING },
|
|
187
338
|
{ name: "%cause", type: DYN },
|
|
188
339
|
{ name: "%causeEnumerable", type: BOOL },
|
|
340
|
+
{ name: "%messagePresent", type: BOOL },
|
|
341
|
+
{ name: "%messageEnumerable", type: BOOL },
|
|
342
|
+
{ name: "%namePresent", type: BOOL },
|
|
343
|
+
{ name: "%nameEnumerable", type: BOOL },
|
|
189
344
|
{ name: "%stackFrames", type: STRING },
|
|
190
345
|
{ name: "%stack", type: STRING },
|
|
191
346
|
],
|
|
@@ -340,6 +495,14 @@ function jsFieldInitializerType(lowerer, type, expression) {
|
|
|
340
495
|
let node = expression;
|
|
341
496
|
while (ts.isParenthesizedExpression(node))
|
|
342
497
|
node = node.expression;
|
|
498
|
+
if (ts.isFunctionExpression(node) || ts.isArrowFunction(node)) {
|
|
499
|
+
const ret = lowerer.declaredReturnType(node, node);
|
|
500
|
+
const arms = type.kind === "union" ? lowerer.unions.get(type.unionId)?.arms ?? [] : [type];
|
|
501
|
+
// Inferred arithmetic may return a checked Number or BigInt. Preserve
|
|
502
|
+
// the callable when its native return ABI differs from the checker slot.
|
|
503
|
+
if (arms.some((arm) => arm.kind === "func" && !typeEquals(arm.ret, ret)))
|
|
504
|
+
return DYN;
|
|
505
|
+
}
|
|
343
506
|
if (node.kind === ts.SyntaxKind.NullKeyword)
|
|
344
507
|
return withUnitArm(type, "nullT", lowerer.unions) ?? type;
|
|
345
508
|
const inferred = lowerer.typeOf(node);
|
|
@@ -803,12 +966,17 @@ mixin) {
|
|
|
803
966
|
// so the base's ClassInfo already exists here. An INSTANTIATION's
|
|
804
967
|
// base is its family (whose base is the declared one) — the heritage
|
|
805
968
|
// clause resolved when the family collected.
|
|
806
|
-
|
|
969
|
+
const computedBase = lowerer.computedClassBases.get(lowerer.classNamer(decl));
|
|
970
|
+
let base = inst ? inst.family : mixin ? mixin.base : computedBase?.classInfo ?? null;
|
|
807
971
|
let callableBase;
|
|
972
|
+
let factoryBaseExpression;
|
|
808
973
|
const adoptCallableBase = (expression) => {
|
|
809
|
-
if (
|
|
810
|
-
!ts.isClassDeclaration(decl) || familyMode || inst || mixin)
|
|
974
|
+
if (familyMode || inst || mixin)
|
|
811
975
|
return false;
|
|
976
|
+
if (lowerer.computedCallableBases.has(decl)) {
|
|
977
|
+
callableBase = { expression, constructorId: `%g.${className}.base`, prototypeId: `%g.${className}.basePrototype` };
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
812
980
|
const signatures = lowerer.checker.getCallSignatures(lowerer.typeOf(expression));
|
|
813
981
|
const source = signatures.length === 1 ? lowerer.checker.signatureDeclaration(signatures[0]) : undefined;
|
|
814
982
|
if (!source || !isJsSourceFile(source.getSourceFile()) ||
|
|
@@ -847,7 +1015,7 @@ mixin) {
|
|
|
847
1015
|
}
|
|
848
1016
|
}
|
|
849
1017
|
}
|
|
850
|
-
for (const clause of inst || mixin ? [] : (decl.heritageClauses ?? [])) {
|
|
1018
|
+
for (const clause of inst || mixin || computedBase ? [] : (decl.heritageClauses ?? [])) {
|
|
851
1019
|
// `implements` is pure type-world: tsc checked the conformance and
|
|
852
1020
|
// the clause erases — nothing about the runtime class changes.
|
|
853
1021
|
// (Assigning an instance INTO an interface-typed slot is a separate
|
|
@@ -884,8 +1052,6 @@ mixin) {
|
|
|
884
1052
|
// (the class statement evaluates at its init position; a base
|
|
885
1053
|
// block below it would still be uninitialized in Node).
|
|
886
1054
|
if (!t.expression.questionDotToken && nsMemberIdentOf(lowerer, t.expression)) {
|
|
887
|
-
if (t.typeArguments)
|
|
888
|
-
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
889
1055
|
if (memberSym)
|
|
890
1056
|
fenceEarlyNsMemberRef(lowerer, t.expression, memberSym);
|
|
891
1057
|
const stored = storedClassValueType(lowerer, t.expression);
|
|
@@ -896,6 +1062,8 @@ mixin) {
|
|
|
896
1062
|
continue;
|
|
897
1063
|
lowerer.unsupported("SC1090", t, `extending the namespace member '${t.expression.name.text}' (no class lowering)`);
|
|
898
1064
|
}
|
|
1065
|
+
if (t.typeArguments)
|
|
1066
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
899
1067
|
base = nsBase;
|
|
900
1068
|
continue;
|
|
901
1069
|
}
|
|
@@ -960,8 +1128,17 @@ mixin) {
|
|
|
960
1128
|
base = mixinBase;
|
|
961
1129
|
continue;
|
|
962
1130
|
}
|
|
1131
|
+
const stored = storedClassValueType(lowerer, t.expression);
|
|
1132
|
+
const factoryBase = stored?.kind === "classval" ? lowerer.classes.get(stored.className) : undefined;
|
|
1133
|
+
if (factoryBase) {
|
|
1134
|
+
base = factoryBase;
|
|
1135
|
+
factoryBaseExpression = t.expression;
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
963
1138
|
}
|
|
964
1139
|
if (!t || !ts.isIdentifier(t.expression)) {
|
|
1140
|
+
if (t && adoptCallableBase(t.expression))
|
|
1141
|
+
continue;
|
|
965
1142
|
lowerer.unsupported("SC1090", clause, "extending computed expressions");
|
|
966
1143
|
}
|
|
967
1144
|
const symbol = lowerer.resolveValueSymbol(t.expression);
|
|
@@ -1027,8 +1204,13 @@ mixin) {
|
|
|
1027
1204
|
lowerer.unsupported("SC1090", t, `extending classes not declared in the program ('${t.expression.text}')`);
|
|
1028
1205
|
}
|
|
1029
1206
|
}
|
|
1030
|
-
if (base?.localClass && !functionLocalClass(decl))
|
|
1031
|
-
|
|
1207
|
+
if (base?.localClass && !functionLocalClass(decl) && !computedBase && !factoryBaseExpression) {
|
|
1208
|
+
const heritage = decl.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)?.types[0];
|
|
1209
|
+
if (heritage)
|
|
1210
|
+
factoryBaseExpression = heritage.expression;
|
|
1211
|
+
else
|
|
1212
|
+
lowerer.unsupported("SC1090", decl, "extending a function-local class outside a function");
|
|
1213
|
+
}
|
|
1032
1214
|
if (lowerer.collectingExprClasses.has(decl)) {
|
|
1033
1215
|
for (let parent = base; parent; parent = parent.base) {
|
|
1034
1216
|
if (parent.builtinEmitter || parent.builtinStream !== undefined) {
|
|
@@ -1078,6 +1260,8 @@ mixin) {
|
|
|
1078
1260
|
const staticFields = [];
|
|
1079
1261
|
const staticMethods = new Map();
|
|
1080
1262
|
const staticBlocks = [];
|
|
1263
|
+
const runtimeStatics = [];
|
|
1264
|
+
const runtimePrototypeMembers = [];
|
|
1081
1265
|
// GENERIC methods (own type parameters), instance and static: only
|
|
1082
1266
|
// the SYNTAX is checked here — parameter/return types mention the
|
|
1083
1267
|
// type parameters and cannot map yet; bodies lower per call-site
|
|
@@ -1126,6 +1310,18 @@ mixin) {
|
|
|
1126
1310
|
});
|
|
1127
1311
|
};
|
|
1128
1312
|
for (const member of decl.members) {
|
|
1313
|
+
if (functionLocalClass(decl) && isJsSourceFile(decl.getSourceFile()) &&
|
|
1314
|
+
(ts.isClassStaticBlockDeclaration(member) || ts.getModifiers(member)?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))) {
|
|
1315
|
+
runtimeStatics.push(member);
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
if (functionLocalClass(decl) && isJsSourceFile(decl.getSourceFile()) &&
|
|
1319
|
+
(ts.isMethodDeclaration(member) || ts.isAccessor(member)) && member.body &&
|
|
1320
|
+
!ts.isPrivateIdentifier(member.name)) {
|
|
1321
|
+
runtimePrototypeMembers.push(member);
|
|
1322
|
+
if (ts.isComputedPropertyName(member.name) && classMemberNameOf(lowerer, member.name) === null)
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1129
1325
|
if (ts.isClassStaticBlockDeclaration(member)) {
|
|
1130
1326
|
// Statics live on the FAMILY (JS has one class, one static
|
|
1131
1327
|
// storage, however many instantiations exist) — instantiations
|
|
@@ -1175,7 +1371,8 @@ mixin) {
|
|
|
1175
1371
|
const type = lowerer.irTypeOf(member.name);
|
|
1176
1372
|
if (type.kind === "void")
|
|
1177
1373
|
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
1178
|
-
if (type.kind === "dyn" && !isJsSourceFile(member.getSourceFile())
|
|
1374
|
+
if (type.kind === "dyn" && !isJsSourceFile(member.getSourceFile()) &&
|
|
1375
|
+
(member.type !== undefined || (lowerer.typeOf(member.name).flags & ts.TypeFlags.Any) === 0)) {
|
|
1179
1376
|
lowerer.unsupported("SC1090", member.name, "'unknown'-typed static fields");
|
|
1180
1377
|
}
|
|
1181
1378
|
staticFields.push({
|
|
@@ -1237,7 +1434,14 @@ mixin) {
|
|
|
1237
1434
|
// (every named member kind stores a PropertyName there).
|
|
1238
1435
|
const memberName = member.name;
|
|
1239
1436
|
if (errorRooted && memberName && classMemberNameOf(lowerer, memberName) === "cause") {
|
|
1240
|
-
|
|
1437
|
+
if (ts.isPropertyDeclaration(member) && !member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
1438
|
+
if (!isJsSourceFile(member.getSourceFile()))
|
|
1439
|
+
lowerer.unsupported("SC1090", memberName, "redeclaring Error.cause (pass the cause in the Error constructor options)");
|
|
1440
|
+
fieldOrder.push({ name: "cause", type: DYN, initializer: member.initializer,
|
|
1441
|
+
...(member.initializer ? {} : { undefinedInitializer: locOf(member) }), redeclared: true, errorCause: true });
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
lowerer.unsupported("SC1090", memberName, "Error.cause accessors");
|
|
1241
1445
|
}
|
|
1242
1446
|
// #PRIVATE members compile: their names ('#m') are unspellable by
|
|
1243
1447
|
// any public identifier, so they ride the ordinary fields/methods
|
|
@@ -1351,33 +1555,38 @@ mixin) {
|
|
|
1351
1555
|
// get/set pair — declare the field and accessors explicitly.
|
|
1352
1556
|
lowerer.unsupported("SC1090", member, "auto-accessor fields ('accessor x')");
|
|
1353
1557
|
}
|
|
1354
|
-
if (ts.isComputedPropertyName(member.name)) {
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1558
|
+
if (ts.isComputedPropertyName(member.name) || ts.isStringLiteralLike(member.name) || ts.isNumericLiteral(member.name)) {
|
|
1559
|
+
const symbolKey = ts.isComputedPropertyName(member.name) ? classSymbolKeyOf(lowerer, member.name.expression) : null;
|
|
1560
|
+
const stringKey = ts.isComputedPropertyName(member.name) ? lowerer.foldedStringKeyOf(member.name.expression)
|
|
1561
|
+
: ts.isNumericLiteral(member.name) ? String(Number(member.name.text)) : member.name.text;
|
|
1562
|
+
const fieldName = symbolKey?.fieldName ?? stringKey;
|
|
1563
|
+
if (fieldName === null || (!member.initializer && !isJsSourceFile(member.getSourceFile()))) {
|
|
1564
|
+
lowerer.unsupported("SC1090", member, "computed class fields without an initializer and a stable string, number, or module-level literal Symbol()/Symbol.for() key");
|
|
1358
1565
|
}
|
|
1359
1566
|
const declared = lowerer.typeOf(member);
|
|
1360
1567
|
const inferred = member.type || !member.initializer ? declared : lowerer.checker.getBaseTypeOfLiteralType(lowerer.typeOf(member.initializer));
|
|
1361
1568
|
const mapped = member.initializer ? lowerer.mapTypeOf(inferred) ?? dynFallbackType(lowerer, member, inferred) : DYN;
|
|
1362
|
-
const type = mapped
|
|
1569
|
+
const type = mapped?.kind === "record" && isJsSourceFile(member.getSourceFile()) && !member.type
|
|
1570
|
+
? DYN : mapped ? jsOpenObjectType(member, mapped, lowerer.shapes, lowerer.unions) : null;
|
|
1363
1571
|
if (!type || type.kind === "void" || (type.kind === "dyn" && !isJsSourceFile(member.getSourceFile())))
|
|
1364
1572
|
lowerer.badType(member, inferred);
|
|
1365
1573
|
const undefinedInitializer = !member.initializer ? locOf(member) : undefined;
|
|
1366
|
-
const previous = fields.get(
|
|
1574
|
+
const previous = fields.get(fieldName);
|
|
1367
1575
|
if (previous) {
|
|
1368
|
-
if (!symbolFields.has(
|
|
1369
|
-
lowerer.unsupported("SC1090", member.name, `distinct keys sharing the printable name '${symbolFieldDisplayName(
|
|
1576
|
+
if (symbolKey && !symbolFields.has(symbolKey.identity)) {
|
|
1577
|
+
lowerer.unsupported("SC1090", member.name, `distinct keys sharing the printable name '${symbolFieldDisplayName(fieldName)}' in one class`);
|
|
1370
1578
|
}
|
|
1371
1579
|
if (!typeEquals(previous, type)) {
|
|
1372
|
-
lowerer.unsupported("SC1090", member.name, "redeclaring
|
|
1580
|
+
lowerer.unsupported("SC1090", member.name, "redeclaring computed fields at a different type");
|
|
1373
1581
|
}
|
|
1374
|
-
fieldOrder.push({ name:
|
|
1582
|
+
fieldOrder.push({ name: fieldName, type, initializer: member.initializer, ...(undefinedInitializer ? { undefinedInitializer } : {}), redeclared: true });
|
|
1375
1583
|
}
|
|
1376
1584
|
else {
|
|
1377
|
-
fields.set(
|
|
1378
|
-
fieldOrder.push({ name:
|
|
1585
|
+
fields.set(fieldName, type);
|
|
1586
|
+
fieldOrder.push({ name: fieldName, type, initializer: member.initializer, ...(undefinedInitializer ? { undefinedInitializer } : {}) });
|
|
1379
1587
|
}
|
|
1380
|
-
|
|
1588
|
+
if (symbolKey)
|
|
1589
|
+
symbolFields.set(symbolKey.identity, fieldName);
|
|
1381
1590
|
continue;
|
|
1382
1591
|
}
|
|
1383
1592
|
// #private fields ride the ordinary field machinery — the '#'
|
|
@@ -1426,6 +1635,12 @@ mixin) {
|
|
|
1426
1635
|
// as undefined on construction, verified), and reads/writes ride
|
|
1427
1636
|
// the ordinary undefined-armed union machinery.
|
|
1428
1637
|
let type = lowerer.irTypeOf(member.name);
|
|
1638
|
+
// Unannotated JavaScript callback slots retain the original
|
|
1639
|
+
// callable and its receiver instead of adapting its signature.
|
|
1640
|
+
if (isJsSourceFile(member.getSourceFile()) && !member.type &&
|
|
1641
|
+
!/@type\b/.test(member.getSourceFile().text.slice(member.pos, member.getStart())) &&
|
|
1642
|
+
(type.kind === "func" || type.kind === "record") && lowerer.dynConvertible(type))
|
|
1643
|
+
type = DYN;
|
|
1429
1644
|
// JS permits reads before a constructor's first assignment even
|
|
1430
1645
|
// when its inferred field type omits undefined.
|
|
1431
1646
|
if (isJsSourceFile(member.getSourceFile()) && !member.initializer && type.kind !== "dyn") {
|
|
@@ -1650,7 +1865,7 @@ mixin) {
|
|
|
1650
1865
|
// a generic-class instantiation's member.
|
|
1651
1866
|
if (implicitMonoFile(decl.getSourceFile()) && !functionLocalClass(decl) &&
|
|
1652
1867
|
ts.isIdentifier(member.name) &&
|
|
1653
|
-
!lowerer.virtualJsMethods.has(member) &&
|
|
1868
|
+
!lowerer.virtualJsMethods.has(member) && !lowerer.prototypeMethodAccesses.has(member.name.text) &&
|
|
1654
1869
|
inst === undefined && decl.typeParameters === undefined &&
|
|
1655
1870
|
!fields.has(member.name.text) &&
|
|
1656
1871
|
!lowerer.findMethodOn(base, member.name.text) &&
|
|
@@ -1669,6 +1884,22 @@ mixin) {
|
|
|
1669
1884
|
}
|
|
1670
1885
|
}
|
|
1671
1886
|
const { shapes, funcType: ft } = lowerer.lambdaSignature(member);
|
|
1887
|
+
// JS inference may retain only a symbol sentinel while a method
|
|
1888
|
+
// also returns values from an untyped mutable field or local.
|
|
1889
|
+
if (isJsSourceFile(member.getSourceFile()) && !member.type && !hasExplicitJsDocReturn(member) && ft.ret.kind === "symbol")
|
|
1890
|
+
ft.ret = DYN;
|
|
1891
|
+
if (isJsSourceFile(member.getSourceFile()) && lowerer.prototypeMethodAccesses.has(mName) &&
|
|
1892
|
+
!lowerer.findMethodOn(base, mName)?.declarer.def.runtime &&
|
|
1893
|
+
shapes.every((p) => p.mode === "required" || p.mode === "omittable")) {
|
|
1894
|
+
for (const shape of shapes) {
|
|
1895
|
+
shape.type = DYN;
|
|
1896
|
+
if (shape.bodyType)
|
|
1897
|
+
shape.bodyType = DYN;
|
|
1898
|
+
}
|
|
1899
|
+
ft.params = shapes.map((shape) => shape.type);
|
|
1900
|
+
if (ft.ret.kind !== "void")
|
|
1901
|
+
ft.ret = DYN;
|
|
1902
|
+
}
|
|
1672
1903
|
// JS subclasses can add parameters to a method. Reserve checked
|
|
1673
1904
|
// argument slots in the base before constructing its vtable; shorter
|
|
1674
1905
|
// bodies ignore them and omitted call arguments become undefined.
|
|
@@ -1704,6 +1935,12 @@ mixin) {
|
|
|
1704
1935
|
// `m(x: number | undefined)` interchange soundly in overrides.
|
|
1705
1936
|
const overridden = lowerer.findMethodOn(base, mName);
|
|
1706
1937
|
if (overridden && isJsSourceFile(member.getSourceFile())) {
|
|
1938
|
+
// An unannotated JS override can return a checked value into
|
|
1939
|
+
// the inherited native ABI, just as its untyped parameters do.
|
|
1940
|
+
// Body lowering inserts the conversion at each return.
|
|
1941
|
+
if (!member.type && ft.ret.kind === "dyn" && overridden.sig.ret.kind !== "dyn" &&
|
|
1942
|
+
!overridden.sig.async && !overridden.sig.gen)
|
|
1943
|
+
ft.ret = overridden.sig.ret;
|
|
1707
1944
|
// Fluent overrides return the same native receiver. Keep the
|
|
1708
1945
|
// inherited return ABI and upcast `this` when lowering returns.
|
|
1709
1946
|
if (overridden.sig.ret.kind === "object" && base &&
|
|
@@ -1748,7 +1985,18 @@ mixin) {
|
|
|
1748
1985
|
// the inherited call/dispatch ABI and refuse at method entry;
|
|
1749
1986
|
// lowering this body with the base signature would change JS.
|
|
1750
1987
|
const returnOnly = overridden.sig.params.length === shapes.length &&
|
|
1751
|
-
overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type))
|
|
1988
|
+
overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type)) &&
|
|
1989
|
+
!member.parameters.some((parameter, index) => {
|
|
1990
|
+
if (!parameter.initializer)
|
|
1991
|
+
return false;
|
|
1992
|
+
const baseMember = overridden.declarer.decl?.members.find((candidate) => ts.isMethodDeclaration(candidate) && classMemberNameOf(lowerer, candidate.name) === mName);
|
|
1993
|
+
const inherited = baseMember && ts.isMethodDeclaration(baseMember) ? baseMember.parameters[index]?.initializer : undefined;
|
|
1994
|
+
if (!inherited)
|
|
1995
|
+
return false;
|
|
1996
|
+
const ownType = lowerer.mapTypeOf(lowerer.typeOf(parameter.initializer));
|
|
1997
|
+
const baseType = lowerer.mapTypeOf(lowerer.typeOf(inherited));
|
|
1998
|
+
return ownType !== null && baseType !== null && !typeEquals(ownType, baseType);
|
|
1999
|
+
});
|
|
1752
2000
|
methodEntryFences.set(mName, unsupportedDiag("SC1090", locOf(member.name), returnOnly
|
|
1753
2001
|
? `overriding method '${mName}' with a different return type (the native return type must match the base declaration exactly)`
|
|
1754
2002
|
: `overriding method '${mName}' with a different signature (the native parameter and return types must match the base declaration exactly)`));
|
|
@@ -1803,9 +2051,19 @@ mixin) {
|
|
|
1803
2051
|
const isGet = ts.isGetAccessor(member);
|
|
1804
2052
|
// #private accessors collect as "get:#x"/"set:#x" — the same
|
|
1805
2053
|
// reserved spelling, one more unspellable segment.
|
|
1806
|
-
|
|
2054
|
+
const prop = classMemberNameOf(lowerer, member.name);
|
|
2055
|
+
if (prop === null) {
|
|
1807
2056
|
lowerer.unsupported("SC1090", member, "computed accessor names");
|
|
1808
2057
|
}
|
|
2058
|
+
if (ts.isComputedPropertyName(member.name)) {
|
|
2059
|
+
const key = classSymbolKeyOf(lowerer, member.name.expression);
|
|
2060
|
+
if (key) {
|
|
2061
|
+
if ([...symbolMethods, ...symbolFields].some(([identity, name]) => name === prop && identity !== key.identity)) {
|
|
2062
|
+
lowerer.unsupported("SC1090", member.name, `distinct symbol keys sharing the printable name '${symbolFieldDisplayName(prop)}' in one class`);
|
|
2063
|
+
}
|
|
2064
|
+
symbolMethods.set(key.identity, prop);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
1809
2067
|
// ABSTRACT accessors are the abstract-method story with property
|
|
1810
2068
|
// syntax: body-less by definition, they enter `methods` (marked
|
|
1811
2069
|
// abstract) as their "get:x"/"set:x" halves — slot declaration
|
|
@@ -1813,9 +2071,8 @@ mixin) {
|
|
|
1813
2071
|
const abstractAccessor = ts.getModifiers(member)?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword) === true;
|
|
1814
2072
|
if (!member.body && !abstractAccessor)
|
|
1815
2073
|
lowerer.unsupported("SC1090", member, "bodyless accessors");
|
|
1816
|
-
const prop = member.name.text;
|
|
1817
2074
|
const mName = `${isGet ? "get" : "set"}:${prop}`;
|
|
1818
|
-
if (fields.has(prop)) {
|
|
2075
|
+
if (fields.has(prop) && !((prop === "message" || prop === "name") && base && (base.def.name === "%Error" || lowerer.isSubclassOf(base.def.name, "%Error")))) {
|
|
1819
2076
|
// tsc rejects field/accessor mixing (TS2610/2611); defensive.
|
|
1820
2077
|
lowerer.unsupported("SC1090", member.name, "accessors sharing a name with a field");
|
|
1821
2078
|
}
|
|
@@ -2073,18 +2330,29 @@ mixin) {
|
|
|
2073
2330
|
if (lhs && ts.isElementAccessExpression(lhs) &&
|
|
2074
2331
|
lhs.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
2075
2332
|
const key = classSymbolKeyOf(lowerer, lhs.argumentExpression);
|
|
2076
|
-
|
|
2333
|
+
const stringKey = lowerer.foldedStringKeyOf(lhs.argumentExpression);
|
|
2334
|
+
const fieldName = key?.fieldName ?? stringKey;
|
|
2335
|
+
if (fieldName === null) {
|
|
2336
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(lhs.argumentExpression))?.kind === "symbol") {
|
|
2337
|
+
lowerer.unsupported("SC1090", lhs, "symbol-keyed class fields require a stable module-level key");
|
|
2338
|
+
}
|
|
2077
2339
|
continue;
|
|
2340
|
+
}
|
|
2341
|
+
if (!key && (fields.has(fieldName) || hasAccessor(fieldName)))
|
|
2342
|
+
continue;
|
|
2343
|
+
if (!key && (methods.has(fieldName) || lowerer.findMethodOn(base, fieldName))) {
|
|
2344
|
+
lowerer.unsupported("SC1090", lhs, "constructor-assigned fields shadowing methods");
|
|
2345
|
+
}
|
|
2078
2346
|
// A key already declared (own or inherited) makes later
|
|
2079
2347
|
// assignments writes, not declarations.
|
|
2080
|
-
if (symbolFields.has(key.identity))
|
|
2348
|
+
if (key && symbolFields.has(key.identity))
|
|
2081
2349
|
continue;
|
|
2082
|
-
if (fields.has(
|
|
2350
|
+
if (fields.has(fieldName)) {
|
|
2083
2351
|
// Two DISTINCT Symbol(...) consts with one description in
|
|
2084
2352
|
// one layout would need one printable name for two slots.
|
|
2085
|
-
lowerer.unsupported("SC1090", lhs, `distinct symbol keys sharing the printable name '${symbolFieldDisplayName(
|
|
2353
|
+
lowerer.unsupported("SC1090", lhs, `distinct symbol keys sharing the printable name '${symbolFieldDisplayName(fieldName)}' in one class`);
|
|
2086
2354
|
}
|
|
2087
|
-
const propSym = lateBoundByKey.get(key.sym);
|
|
2355
|
+
const propSym = key ? lateBoundByKey.get(key.sym) : instType ? lowerer.checker.getPropertyOfType(instType, fieldName) : undefined;
|
|
2088
2356
|
// tsgo does not synthesize the late-bound `__@name@id`
|
|
2089
2357
|
// property for a JS `this[k] = v` declaration (the finding-5
|
|
2090
2358
|
// family: no expando/late-bound synthesis in its stricter
|
|
@@ -2104,9 +2372,10 @@ mixin) {
|
|
|
2104
2372
|
type = jsOpenObjectType(lhs, type, lowerer.shapes, lowerer.unions);
|
|
2105
2373
|
if (!type || type.kind === "void")
|
|
2106
2374
|
lowerer.badType(lhs, t ?? lowerer.typeOf(lhs));
|
|
2107
|
-
fields.set(
|
|
2108
|
-
|
|
2109
|
-
|
|
2375
|
+
fields.set(fieldName, type);
|
|
2376
|
+
if (key)
|
|
2377
|
+
symbolFields.set(key.identity, fieldName);
|
|
2378
|
+
fieldOrder.push({ name: fieldName, type, initializer: undefined });
|
|
2110
2379
|
}
|
|
2111
2380
|
}
|
|
2112
2381
|
}
|
|
@@ -2197,7 +2466,13 @@ mixin) {
|
|
|
2197
2466
|
const ctorParams = ctor && !mixinForwarding
|
|
2198
2467
|
? lowerer.paramShapes(ctor.parameters)
|
|
2199
2468
|
: (base?.ctorParams ?? (callableBase ? [{ type: DYN, mode: "dynRest" }] : []));
|
|
2469
|
+
if (ctor && !mixinForwarding && isJsSourceFile(ctor.getSourceFile()) &&
|
|
2470
|
+
!ctorParams.some((shape) => shape.mode === "rest" || shape.mode === "dynRest" || shape.mode === "islandRest") && bodyReadsArguments(ctor)) {
|
|
2471
|
+
ctorParams.push({ type: DYN, mode: "arguments" });
|
|
2472
|
+
}
|
|
2200
2473
|
const info = {
|
|
2474
|
+
...(runtimeStatics.length ? { runtimeStatics } : {}),
|
|
2475
|
+
...(runtimePrototypeMembers.length ? { runtimePrototypeMembers } : {}),
|
|
2201
2476
|
def: {
|
|
2202
2477
|
name: className,
|
|
2203
2478
|
// The JS-observable .name (the class object's name string and
|
|
@@ -2237,6 +2512,7 @@ mixin) {
|
|
|
2237
2512
|
...(paramProps.length > 0 ? { paramProps } : {}),
|
|
2238
2513
|
base,
|
|
2239
2514
|
...(callableBase ? { callableBase } : {}),
|
|
2515
|
+
...(factoryBaseExpression ? { factoryBaseExpression } : {}),
|
|
2240
2516
|
subclasses: [],
|
|
2241
2517
|
throwingSetters,
|
|
2242
2518
|
staticFields,
|
|
@@ -2297,7 +2573,15 @@ mixin) {
|
|
|
2297
2573
|
if (base)
|
|
2298
2574
|
base.subclasses.push(info);
|
|
2299
2575
|
lowerer.classes.set(className, info);
|
|
2300
|
-
if (
|
|
2576
|
+
if (computedBase && !functionLocalClass(decl)) {
|
|
2577
|
+
info.def.baseValueGlobal = `%g.${className}.computedBase`;
|
|
2578
|
+
lowerer.globalsList.push({ id: info.def.baseValueGlobal, name: `${className} base`, type: computedBase.value.type, mutable: false });
|
|
2579
|
+
}
|
|
2580
|
+
if (factoryBaseExpression && !functionLocalClass(decl)) {
|
|
2581
|
+
info.def.baseValueGlobal = `%g.${className}.computedBase`;
|
|
2582
|
+
lowerer.globalsList.push({ id: info.def.baseValueGlobal, name: `${className} base`, type: { kind: "classval", className: base.def.name }, mutable: false });
|
|
2583
|
+
}
|
|
2584
|
+
if (callableBase && !functionLocalClass(decl)) {
|
|
2301
2585
|
lowerer.globalsList.push({ id: callableBase.constructorId, name: `${className} base`, type: DYN, mutable: false }, { id: callableBase.prototypeId, name: `${className} base prototype`, type: DYN, mutable: false });
|
|
2302
2586
|
}
|
|
2303
2587
|
// A NAMED class binds its name (declarations in their scope, class
|
|
@@ -2487,10 +2771,28 @@ function withInstanceBindings(lowerer, info, fn) {
|
|
|
2487
2771
|
* field, like fieldInitStmts; a block lowers as the block statement it
|
|
2488
2772
|
* is, so its statements poison individually inside lowerStmts. */
|
|
2489
2773
|
export function lowerStaticFieldInits(lowerer, info) {
|
|
2774
|
+
if (info.localClass)
|
|
2775
|
+
return [];
|
|
2490
2776
|
// Mixin instantiations lower their initializers under the
|
|
2491
2777
|
// instantiation's bindings/context (a no-op for everything else —
|
|
2492
2778
|
// generic FAMILIES own their statics and carry no genericInstance).
|
|
2493
|
-
|
|
2779
|
+
const diagsBefore = lowerer.diags.length;
|
|
2780
|
+
try {
|
|
2781
|
+
return withInstanceBindings(lowerer, info, () => lowerStaticFieldInitsInner(lowerer, info));
|
|
2782
|
+
}
|
|
2783
|
+
catch (error) {
|
|
2784
|
+
// Heritage and decorators run before the per-field recovery below.
|
|
2785
|
+
// Keep their refusal attached to the class declaration instead of
|
|
2786
|
+
// letting a poisoned initializer escape the module lowering pass.
|
|
2787
|
+
if (!(error instanceof PoisonError))
|
|
2788
|
+
throw error;
|
|
2789
|
+
if (info.decl && isJsSourceFile(info.decl.getSourceFile()) && lowerer.diagSink === null) {
|
|
2790
|
+
const fence = lowerer.deferToRuntimeFence(diagsBefore, info.decl, { kind: "statement" });
|
|
2791
|
+
if (fence)
|
|
2792
|
+
return [fence];
|
|
2793
|
+
}
|
|
2794
|
+
return [];
|
|
2795
|
+
}
|
|
2494
2796
|
}
|
|
2495
2797
|
function lowerStaticFieldInitsInner(lowerer, info) {
|
|
2496
2798
|
// Decoration first: TC39 evaluates decorator expressions, creates the
|
|
@@ -2498,14 +2800,38 @@ function lowerStaticFieldInitsInner(lowerer, info) {
|
|
|
2498
2800
|
// initializers and static blocks (verified against Node — the
|
|
2499
2801
|
// decorated result is what `this`/the class name mean inside them).
|
|
2500
2802
|
const out = [...lowerClassDecoration(lowerer, info)];
|
|
2803
|
+
const computedBase = info.decl ? lowerer.computedClassBases.get(lowerer.classNamer(info.decl)) : undefined;
|
|
2804
|
+
if (info.factoryBaseExpression && info.def.baseValueGlobal) {
|
|
2805
|
+
const loc = locOf(info.factoryBaseExpression);
|
|
2806
|
+
const type = { kind: "classval", className: info.base.def.name };
|
|
2807
|
+
out.push({ kind: "assign", localId: info.def.baseValueGlobal, value: lowerer.lowerExprExpecting(info.factoryBaseExpression, type), loc });
|
|
2808
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
2809
|
+
lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN),
|
|
2810
|
+
lowerer.coerceToExpected({ kind: "varRef", localId: info.def.baseValueGlobal, type, loc }, DYN),
|
|
2811
|
+
], type: DYN, loc }, loc });
|
|
2812
|
+
}
|
|
2813
|
+
if (computedBase && info.def.baseValueGlobal) {
|
|
2814
|
+
const loc = computedBase.value.loc;
|
|
2815
|
+
out.push({ kind: "assign", localId: info.def.baseValueGlobal, value: computedBase.value, loc });
|
|
2816
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
2817
|
+
lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN),
|
|
2818
|
+
lowerer.coerceToExpected({ kind: "varRef", localId: info.def.baseValueGlobal, type: computedBase.value.type, loc }, DYN),
|
|
2819
|
+
], type: DYN, loc }, loc });
|
|
2820
|
+
}
|
|
2501
2821
|
if (info.callableBase) {
|
|
2502
2822
|
const { expression, constructorId, prototypeId } = info.callableBase;
|
|
2503
2823
|
const loc = locOf(expression);
|
|
2504
|
-
out.push({ kind: "assign", localId: constructorId, value: lowerer.
|
|
2824
|
+
out.push({ kind: "assign", localId: constructorId, value: info.decl && lowerer.computedCallableBases.has(info.decl)
|
|
2825
|
+
? lowerer.coerceToExpected(lowerer.computedCallableBases.get(info.decl), DYN)
|
|
2826
|
+
: lowerer.lowerExprExpecting(expression, DYN), loc }, { kind: "assign", localId: prototypeId, value: { kind: "libCall", fn: "dyn.classBasePrototype",
|
|
2505
2827
|
args: [{ kind: "varRef", localId: constructorId, type: DYN, loc }], type: DYN, loc }, loc });
|
|
2506
2828
|
const prototype = classPrototypeData(lowerer, info, loc);
|
|
2507
2829
|
if (prototype)
|
|
2508
2830
|
out.push({ kind: "exprStmt", expr: prototype, loc });
|
|
2831
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
2832
|
+
lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN),
|
|
2833
|
+
{ kind: "varRef", localId: constructorId, type: DYN, loc },
|
|
2834
|
+
], type: DYN, loc }, loc });
|
|
2509
2835
|
}
|
|
2510
2836
|
const items = [
|
|
2511
2837
|
...info.staticFields.map((f) => ({ pos: f.initializer.getStart(), kind: "field", f })),
|
|
@@ -2523,12 +2849,23 @@ function lowerStaticFieldInitsInner(lowerer, info) {
|
|
|
2523
2849
|
lowerer.bumpFileStat(locOf(f.initializer).file, "total");
|
|
2524
2850
|
const diagsBefore = lowerer.diags.length;
|
|
2525
2851
|
try {
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2852
|
+
const previousThis = lowerer.ctx.thisLocal;
|
|
2853
|
+
const receiver = f.initializer.kind === ts.SyntaxKind.ThisKeyword || hasStaticThis(f.initializer)
|
|
2854
|
+
? lowerer.declareHiddenLocal("%staticThis", { kind: "classval", className: info.def.name }) : null;
|
|
2855
|
+
if (receiver) {
|
|
2856
|
+
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
2857
|
+
out.push({ kind: "varDecl", localId: receiver.id, init: { kind: "classRef", className: info.def.name,
|
|
2858
|
+
type: receiver.type, loc: locOf(f.initializer) }, loc: locOf(f.initializer) });
|
|
2859
|
+
}
|
|
2860
|
+
let value;
|
|
2861
|
+
try {
|
|
2862
|
+
if (receiver)
|
|
2863
|
+
lowerer.ctx.thisLocal = receiver;
|
|
2864
|
+
value = lowerer.lowerExprExpecting(f.initializer, f.type);
|
|
2865
|
+
}
|
|
2866
|
+
finally {
|
|
2867
|
+
lowerer.ctx.thisLocal = previousThis;
|
|
2868
|
+
}
|
|
2532
2869
|
out.push({ kind: "assign", localId: f.globalId, value, loc: locOf(f.initializer) });
|
|
2533
2870
|
}
|
|
2534
2871
|
catch (e) {
|
|
@@ -2815,6 +3152,11 @@ function staticMethodValue(lowerer, declarer, name, sig, blame, loc) {
|
|
|
2815
3152
|
export function classValueRef(lowerer, info, blame) {
|
|
2816
3153
|
const display = info.def.name.replace(/^%|^%m\d+\./, "");
|
|
2817
3154
|
fenceDecorationThrows(lowerer, info, blame);
|
|
3155
|
+
if (info.localClass && ts.isIdentifier(blame)) {
|
|
3156
|
+
const stored = lowerer.resolveLocal(blame);
|
|
3157
|
+
if (stored)
|
|
3158
|
+
return lowerer.coerceToExpected({ kind: "varRef", localId: stored.id, type: stored.type, loc: locOf(blame) }, { kind: "classval", className: info.def.name });
|
|
3159
|
+
}
|
|
2818
3160
|
if (info.localClass && !ts.isClassExpression(blame) && !ts.isClassDeclaration(blame)) {
|
|
2819
3161
|
lowerer.unsupported("SC1090", blame, "named self references inside function-local classes");
|
|
2820
3162
|
}
|
|
@@ -2879,6 +3221,8 @@ export function staticFieldWriteTarget(lowerer, access) {
|
|
|
2879
3221
|
* fall through to the ordinary chain so the static fence or the
|
|
2880
3222
|
* generic member rejection names the site. */
|
|
2881
3223
|
export function lowerStaticFieldRead(lowerer, expr) {
|
|
3224
|
+
if (expr.name.text === "prototype")
|
|
3225
|
+
return null;
|
|
2882
3226
|
if (expr.questionDotToken)
|
|
2883
3227
|
return null;
|
|
2884
3228
|
if (!ts.isIdentifier(expr.expression) && expr.expression.kind !== ts.SyntaxKind.ThisKeyword)
|
|
@@ -2903,6 +3247,9 @@ export function lowerStaticFieldRead(lowerer, expr) {
|
|
|
2903
3247
|
return null;
|
|
2904
3248
|
const loc = locOf(expr);
|
|
2905
3249
|
const found = findStaticOn(lowerer, info, expr.name.text);
|
|
3250
|
+
if (hasRuntimeStatics(info) && !found?.field)
|
|
3251
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(classValueRef(lowerer, info, expr.expression), DYN),
|
|
3252
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
2906
3253
|
// A #private static resolves only through the DECLARING class's own
|
|
2907
3254
|
// name: in JS the brand lives on that one constructor object, so
|
|
2908
3255
|
// `D.#s` (a subclass receiver) throws Node's TypeError instead of
|
|
@@ -2930,6 +3277,9 @@ export function lowerStaticFieldRead(lowerer, expr) {
|
|
|
2930
3277
|
if (expr.name.text === "name" && info.def.jsName !== undefined) {
|
|
2931
3278
|
return { kind: "strLit", value: info.def.jsName, type: STRING, loc };
|
|
2932
3279
|
}
|
|
3280
|
+
if (info.callableBase)
|
|
3281
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(classValueRef(lowerer, info, expr.expression), DYN),
|
|
3282
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
2933
3283
|
const data = classStaticDataFor(lowerer, info, expr.name.text, loc);
|
|
2934
3284
|
if (data)
|
|
2935
3285
|
return { kind: "dynKeyGet", value: data, key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
@@ -2987,13 +3337,29 @@ export function lowerClassExpressionInfo(lowerer, expr) {
|
|
|
2987
3337
|
lowerer.unsupported("SC1090", expr, "function-local classes with private brands");
|
|
2988
3338
|
}
|
|
2989
3339
|
if (local && (expr.typeParameters?.length ||
|
|
2990
|
-
decoratorNodesOf(expr).length || expr.members.some((member) =>
|
|
2991
|
-
|
|
3340
|
+
decoratorNodesOf(expr).length || expr.members.some((member) => decoratorNodesOf(member).length ||
|
|
3341
|
+
(!isJsSourceFile(expr.getSourceFile()) && (ts.isClassStaticBlockDeclaration(member) ||
|
|
3342
|
+
ts.getModifiers(member)?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))) ||
|
|
2992
3343
|
(ts.isMethodDeclaration(member) && member.typeParameters?.length)))) {
|
|
2993
3344
|
lowerer.unsupported("SC1090", expr, "function-local classes with statics, decorators, or generic members");
|
|
2994
3345
|
}
|
|
2995
3346
|
lowerer.collectingExprClasses.add(expr);
|
|
2996
3347
|
try {
|
|
3348
|
+
if (local && lowerer.fnStack.length > 0 && !lowerer.computedClassBases.has(lowerer.classNamer(expr))) {
|
|
3349
|
+
const heritage = expr.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)?.types[0]?.expression;
|
|
3350
|
+
if (heritage && (ts.isCallExpression(heritage) ||
|
|
3351
|
+
ts.isIdentifier(heritage) && (lowerer.peekLocal(heritage) ?? lowerer.globalOf(heritage)) != null)) {
|
|
3352
|
+
let value = lowerer.lowerExpr(heritage);
|
|
3353
|
+
const known = value.type.kind === "dyn" ? storedClassValueType(lowerer, heritage) : null;
|
|
3354
|
+
if (known?.kind === "classval")
|
|
3355
|
+
value = lowerer.coerceToExpected(value, known);
|
|
3356
|
+
const base = value.type.kind === "classval" ? lowerer.classes.get(value.type.className) : undefined;
|
|
3357
|
+
if (base)
|
|
3358
|
+
lowerer.computedClassBases.set(lowerer.classNamer(expr), { value, classInfo: base });
|
|
3359
|
+
else if (value.type.kind === "dyn" || value.type.kind === "func")
|
|
3360
|
+
lowerer.computedCallableBases.set(expr, value);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
2997
3363
|
lowerer.collectClassShapeInner(expr, namedEvaluationName(expr));
|
|
2998
3364
|
}
|
|
2999
3365
|
finally {
|
|
@@ -3022,7 +3388,7 @@ export function lowerClassExpressionInfo(lowerer, expr) {
|
|
|
3022
3388
|
// drains it), which is JS's order for whole-initializer positions.
|
|
3023
3389
|
// Anything subtler (multi-declarator statements, arguments evaluated
|
|
3024
3390
|
// after other side effects) is a named fence, never a reordering.
|
|
3025
|
-
if (info.staticFields.length > 0 || (info.staticBlocks?.length ?? 0) > 0) {
|
|
3391
|
+
if (!local && (info.callableBase || info.def.baseValueGlobal || info.staticFields.length > 0 || (info.staticBlocks?.length ?? 0) > 0)) {
|
|
3026
3392
|
let holder = expr.parent;
|
|
3027
3393
|
while (ts.isParenthesizedExpression(holder) || ts.isClassExpression(holder) ||
|
|
3028
3394
|
ts.isHeritageClause(holder) || ts.isExpressionWithTypeArguments(holder)) {
|
|
@@ -3042,7 +3408,6 @@ export function lowerClassExpressionInfo(lowerer, expr) {
|
|
|
3042
3408
|
if (!wholeInit) {
|
|
3043
3409
|
lowerer.unsupported("SC1090", expr, "class expressions with static initializers or static blocks outside a whole-initializer position (their declaration-time code must run exactly where the expression evaluates — bind the class in its own top-level `const C = class …` statement)");
|
|
3044
3410
|
}
|
|
3045
|
-
lowerer.pendingClassExprInits.push(...lowerer.lowerStaticFieldInits(info));
|
|
3046
3411
|
}
|
|
3047
3412
|
return info;
|
|
3048
3413
|
}
|
|
@@ -3068,6 +3433,11 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3068
3433
|
}
|
|
3069
3434
|
}
|
|
3070
3435
|
const info = lowerClassExpressionInfo(lowerer, expr);
|
|
3436
|
+
if (!info.localClass && !info.expressionInitializationEmitted &&
|
|
3437
|
+
(info.callableBase || info.def.baseValueGlobal || info.staticFields.length > 0 || (info.staticBlocks?.length ?? 0) > 0)) {
|
|
3438
|
+
info.expressionInitializationEmitted = true;
|
|
3439
|
+
lowerer.pendingClassExprInits.push(...lowerer.lowerStaticFieldInits(info));
|
|
3440
|
+
}
|
|
3071
3441
|
if (info.localClass && !info.localClass.ready) {
|
|
3072
3442
|
const context = newFnCtx(true, null, null, VOID);
|
|
3073
3443
|
info.localClass.context = context;
|
|
@@ -3083,6 +3453,7 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3083
3453
|
info.def.localCaptures.push({ localId: "%base", name: "base", type: { kind: "classval", className: info.base.def.name } });
|
|
3084
3454
|
}
|
|
3085
3455
|
info.localClass.ready = true;
|
|
3456
|
+
lowerer.onExprClassCollected?.(info);
|
|
3086
3457
|
}
|
|
3087
3458
|
finally {
|
|
3088
3459
|
lowerer.fnStack.pop();
|
|
@@ -3090,9 +3461,27 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3090
3461
|
}
|
|
3091
3462
|
}
|
|
3092
3463
|
const value = classValueRef(lowerer, info, expr);
|
|
3464
|
+
if (info.localClass && info.callableBase && value.kind === "classRef") {
|
|
3465
|
+
const loc = locOf(expr);
|
|
3466
|
+
const base = lowerer.lowerExprExpecting(info.callableBase.expression, DYN);
|
|
3467
|
+
const baseLocal = lowerer.declareHiddenLocal("%classBase", DYN);
|
|
3468
|
+
const clsLocal = lowerer.declareHiddenLocal("%classValue", value.type);
|
|
3469
|
+
const baseRef = { kind: "varRef", localId: baseLocal.id, type: DYN, loc };
|
|
3470
|
+
const clsRef = { kind: "varRef", localId: clsLocal.id, type: value.type, loc };
|
|
3471
|
+
const boxed = { kind: "dynFrom", value: clsRef, type: DYN, loc };
|
|
3472
|
+
info.localPrototypeData = true;
|
|
3473
|
+
return initializeRuntimeStatics(lowerer, info, { kind: "seqExpr", stmts: [
|
|
3474
|
+
{ kind: "varDecl", localId: baseLocal.id, init: base, loc },
|
|
3475
|
+
{ kind: "varDecl", localId: clsLocal.id, init: { ...value, captures: info.localClass.captureSources }, loc },
|
|
3476
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [boxed, baseRef], type: DYN, loc }, loc },
|
|
3477
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classPrototype", args: [boxed,
|
|
3478
|
+
{ kind: "libCall", fn: "dyn.classBasePrototype", args: [baseRef], type: DYN, loc },
|
|
3479
|
+
], type: DYN, loc }, loc },
|
|
3480
|
+
], result: clsRef, type: value.type, loc });
|
|
3481
|
+
}
|
|
3093
3482
|
if (info.localClass?.baseExpression && info.base && value.kind === "classRef") {
|
|
3094
3483
|
const loc = locOf(expr);
|
|
3095
|
-
const base = lowerer.
|
|
3484
|
+
const base = lowerer.lowerExprExpecting(info.localClass.baseExpression, { kind: "classval", className: info.base.def.name });
|
|
3096
3485
|
const baseLocal = lowerer.declareHiddenLocal("%classBase", base.type);
|
|
3097
3486
|
baseLocal.boxed = true;
|
|
3098
3487
|
const clsLocal = lowerer.declareHiddenLocal("%classValue", value.type);
|
|
@@ -3100,17 +3489,20 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3100
3489
|
const clsRef = { kind: "varRef", localId: clsLocal.id, type: value.type, loc };
|
|
3101
3490
|
const basePrototype = classPrototypeData(lowerer, info.base, loc, baseRef);
|
|
3102
3491
|
info.localPrototypeData = true;
|
|
3103
|
-
return { kind: "seqExpr", stmts: [
|
|
3492
|
+
return initializeRuntimeStatics(lowerer, info, { kind: "seqExpr", stmts: [
|
|
3104
3493
|
{ kind: "varDecl", localId: baseLocal.id, init: base, loc },
|
|
3105
3494
|
{ kind: "varDecl", localId: clsLocal.id, init: { ...value, captures: [...info.localClass.captureSources, baseLocal.id] }, loc },
|
|
3495
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
3496
|
+
{ kind: "dynFrom", value: clsRef, type: DYN, loc }, { kind: "dynFrom", value: baseRef, type: DYN, loc },
|
|
3497
|
+
], type: DYN, loc }, loc },
|
|
3106
3498
|
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classPrototype", args: [
|
|
3107
3499
|
{ kind: "dynFrom", value: clsRef, type: DYN, loc }, basePrototype,
|
|
3108
3500
|
], type: DYN, loc }, loc },
|
|
3109
|
-
], result: clsRef, type: value.type, loc };
|
|
3501
|
+
], result: clsRef, type: value.type, loc });
|
|
3110
3502
|
}
|
|
3111
|
-
return info.localClass && value.kind === "classRef"
|
|
3503
|
+
return initializeRuntimeStatics(lowerer, info, info.localClass && value.kind === "classRef"
|
|
3112
3504
|
? { ...value, captures: info.localClass.captureSources }
|
|
3113
|
-
: value;
|
|
3505
|
+
: value);
|
|
3114
3506
|
}
|
|
3115
3507
|
/** The EXACT class a receiver expression is statically known to BE (not
|
|
3116
3508
|
* merely be typed by): the class name itself, or a `const` binding
|
|
@@ -3274,7 +3666,12 @@ export function lowerStaticMethodCall(lowerer, call, access) {
|
|
|
3274
3666
|
// export assignment pins it) — the direct-name rules apply.
|
|
3275
3667
|
if (!ts.isIdentifier(access.expression)) {
|
|
3276
3668
|
if (!isModuleExportsAccess(access.expression) || !isCjsJsFile(access.getSourceFile(), lowerer.program)) {
|
|
3277
|
-
|
|
3669
|
+
let receiverType = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
3670
|
+
if (receiverType?.kind !== "classval" && isJsSourceFile(call.getSourceFile()) && ts.isCallExpression(access.expression)) {
|
|
3671
|
+
const receiver = tryLowerExpression(lowerer, access.expression);
|
|
3672
|
+
if (receiver?.type.kind === "classval")
|
|
3673
|
+
receiverType = receiver.type;
|
|
3674
|
+
}
|
|
3278
3675
|
if (receiverType?.kind !== "classval")
|
|
3279
3676
|
return null;
|
|
3280
3677
|
const info = lowerer.classes.get(receiverType.className);
|
|
@@ -3331,6 +3728,10 @@ function classValueIsExactlyOwn(lowerer, recv, info) {
|
|
|
3331
3728
|
}
|
|
3332
3729
|
function staticCallOn(lowerer, call, access, info, throughValue) {
|
|
3333
3730
|
const loc = locOf(call);
|
|
3731
|
+
if (hasRuntimeStatics(info) && !findStaticOn(lowerer, info, access.name.text)?.field || info.callableBase && !findStaticOn(lowerer, info, access.name.text) && !findGenericStaticOn(lowerer, info, access.name.text))
|
|
3732
|
+
return { kind: "dynInvoke", recv: lowerer.lowerExprExpecting(access.expression, DYN),
|
|
3733
|
+
method: access.name.text, calleeName: access.name.text,
|
|
3734
|
+
args: call.arguments.map((argument) => lowerer.lowerExprExpecting(argument, DYN)), type: DYN, loc };
|
|
3334
3735
|
// #private statics: through-a-VALUE receiver the call devirtualizes
|
|
3335
3736
|
// exactly when the value can only BE the declaring class
|
|
3336
3737
|
// (classValueIsExactlyOwn — a slot that could hold a descendant at
|
|
@@ -3382,7 +3783,7 @@ function staticCallOn(lowerer, call, access, info, throughValue) {
|
|
|
3382
3783
|
}
|
|
3383
3784
|
args.push(absent);
|
|
3384
3785
|
}
|
|
3385
|
-
return { kind: "callValue", callee, args, type: found.field.type.ret, loc };
|
|
3786
|
+
return { kind: "callValue", callee, receiver: lowerer.lowerExprExpecting(access.expression, DYN), args, type: found.field.type.ret, loc };
|
|
3386
3787
|
}
|
|
3387
3788
|
if (found.method.member.body && hasStaticThis(found.method.member)) {
|
|
3388
3789
|
const generic = found.method.receiverInfo ??= {
|
|
@@ -3435,6 +3836,10 @@ export function lowerClassValueProperty(lowerer, expr) {
|
|
|
3435
3836
|
return null;
|
|
3436
3837
|
const loc = locOf(expr);
|
|
3437
3838
|
const member = expr.name.text;
|
|
3839
|
+
const info = lowerer.classes.get(recvT.className);
|
|
3840
|
+
if (info && (hasRuntimeStatics(info) && !findStaticOn(lowerer, info, member)?.field || info.callableBase && !findStaticOn(lowerer, info, member)))
|
|
3841
|
+
return { kind: "dynKeyGet", value: lowerer.lowerExprExpecting(expr.expression, DYN),
|
|
3842
|
+
key: { kind: "strLit", value: member, type: STRING, loc }, type: DYN, loc };
|
|
3438
3843
|
// `X.name` reads the RUNTIME class object's stored name — the one
|
|
3439
3844
|
// genuinely dynamic member. It CONSUMES the receiver (no evaluation
|
|
3440
3845
|
// is discarded), so any receiver expression is fine here.
|
|
@@ -3444,6 +3849,8 @@ export function lowerClassValueProperty(lowerer, expr) {
|
|
|
3444
3849
|
return null;
|
|
3445
3850
|
return { kind: "libCall", fn: "class.name", args: [recv], type: STRING, loc };
|
|
3446
3851
|
}
|
|
3852
|
+
if (!info)
|
|
3853
|
+
return null;
|
|
3447
3854
|
if (!ts.isIdentifier(expr.expression) &&
|
|
3448
3855
|
expr.expression.kind !== ts.SyntaxKind.ThisKeyword &&
|
|
3449
3856
|
// `module.exports.label` in a class-replaced CJS module: the
|
|
@@ -3457,9 +3864,6 @@ export function lowerClassValueProperty(lowerer, expr) {
|
|
|
3457
3864
|
}
|
|
3458
3865
|
// The direct class-name spelling resolved in lowerStaticFieldRead;
|
|
3459
3866
|
// reaching here means the receiver is a classval-typed BINDING.
|
|
3460
|
-
const info = lowerer.classes.get(recvT.className);
|
|
3461
|
-
if (!info)
|
|
3462
|
-
return null;
|
|
3463
3867
|
const found = findStaticOn(lowerer, info, member);
|
|
3464
3868
|
if (!found) {
|
|
3465
3869
|
lowerer.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)`);
|
|
@@ -3559,8 +3963,26 @@ export function genericOverrideBelow(lowerer, info, name) {
|
|
|
3559
3963
|
* collected subclass graph cannot answer this question yet. The checker
|
|
3560
3964
|
* hierarchy identifies the original declarations through aliases as well. */
|
|
3561
3965
|
export function collectVirtualJsMethods(lowerer, files) {
|
|
3562
|
-
|
|
3563
|
-
|
|
3966
|
+
// Record named prototype accesses before collecting method signatures.
|
|
3967
|
+
// Those slots need a callable ABI even when JavaScript would otherwise
|
|
3968
|
+
// specialize each call independently.
|
|
3969
|
+
for (const file of files)
|
|
3970
|
+
ts.walkPreorder(file, (node) => {
|
|
3971
|
+
if (!ts.isPropertyAccessExpression(node) && !ts.isElementAccessExpression(node))
|
|
3972
|
+
return;
|
|
3973
|
+
const name = ts.isPropertyAccessExpression(node) ? node.name.text
|
|
3974
|
+
: ts.isStringLiteralLike(node.argumentExpression) ? node.argumentExpression.text : null;
|
|
3975
|
+
if (name === null)
|
|
3976
|
+
return;
|
|
3977
|
+
const prototype = ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "prototype";
|
|
3978
|
+
const assigned = ts.isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
|
|
3979
|
+
const symbol = !assigned ? undefined : ts.isPropertyAccessExpression(node)
|
|
3980
|
+
? lowerer.checker.getSymbolAtLocation(node.name)
|
|
3981
|
+
: lowerer.checker.getPropertyOfType(lowerer.typeOf(node.expression), name);
|
|
3982
|
+
if (prototype || symbol && lowerer.checker.declarationsOf(symbol).some(ts.isMethodDeclaration)) {
|
|
3983
|
+
lowerer.prototypeMethodAccesses.set(name, node);
|
|
3984
|
+
}
|
|
3985
|
+
});
|
|
3564
3986
|
const visit = (node) => {
|
|
3565
3987
|
if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
|
|
3566
3988
|
const members = node.members.filter((member) => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
|
|
@@ -3786,12 +4208,22 @@ export function lowerClassMembers(lowerer, info) {
|
|
|
3786
4208
|
// signature-blocked function (lowerStaticMethod's rule) instead of
|
|
3787
4209
|
// crashing the whole lowering.
|
|
3788
4210
|
if (!info.generic && (always || lowerer.wantBody(`%${className}.constructor`))) {
|
|
4211
|
+
const diagsBefore = lowerer.diags.length;
|
|
3789
4212
|
try {
|
|
3790
4213
|
out.push(lowerer.lowerClassCtor(info));
|
|
3791
4214
|
}
|
|
3792
4215
|
catch (e) {
|
|
3793
4216
|
if (!(e instanceof PoisonError))
|
|
3794
4217
|
throw e;
|
|
4218
|
+
if (info.decl && isJsSourceFile(info.decl.getSourceFile())) {
|
|
4219
|
+
const types = [{ kind: "object", className }, ...info.ctorParams.map((param) => param.type)];
|
|
4220
|
+
const fallback = lowerer.deferToRuntimeFence(diagsBefore, info.ctor ?? info.decl, {
|
|
4221
|
+
kind: "function", name: `%${className}.constructor`, returnType: VOID,
|
|
4222
|
+
params: types.map((type, index) => ({ localId: `p.${index}`, name: index === 0 ? "this" : `p${index}`, type })),
|
|
4223
|
+
});
|
|
4224
|
+
if (fallback)
|
|
4225
|
+
out.push(fallback);
|
|
4226
|
+
}
|
|
3795
4227
|
}
|
|
3796
4228
|
}
|
|
3797
4229
|
for (const { mName, member } of lowerer.classMethodMembers(info)) {
|
|
@@ -3877,6 +4309,11 @@ function lowerClassCtorInner(lowerer, info) {
|
|
|
3877
4309
|
// defaults on entry, ahead of everything the body does.
|
|
3878
4310
|
const declared = lowerer.declareParams(info.ctor.parameters, info.ctorParams);
|
|
3879
4311
|
params.push(...declared.params);
|
|
4312
|
+
if (info.ctorParams.at(-1)?.mode === "arguments") {
|
|
4313
|
+
const local = lowerer.declareHiddenLocal("%arguments", DYN);
|
|
4314
|
+
params.push({ localId: local.id, name: local.name, type: DYN });
|
|
4315
|
+
lowerer.ctx.argumentsLocal = local;
|
|
4316
|
+
}
|
|
3880
4317
|
body.push(...declared.prologue);
|
|
3881
4318
|
if (!ctorBase && !info.callableBase) {
|
|
3882
4319
|
// Node's base-class order: field initializers run at the start
|
|
@@ -4174,7 +4611,7 @@ export function lowerClassMethodMember(lowerer, info, fnLike) {
|
|
|
4174
4611
|
function lowerClassMethodMemberInner(lowerer, info, fnLike) {
|
|
4175
4612
|
const className = info.def.name;
|
|
4176
4613
|
const thisType = { kind: "object", className };
|
|
4177
|
-
const memberName =
|
|
4614
|
+
const memberName = classMemberNameOf(lowerer, fnLike.name);
|
|
4178
4615
|
if (memberName === null)
|
|
4179
4616
|
return null;
|
|
4180
4617
|
const mName = ts.isMethodDeclaration(fnLike)
|
|
@@ -4383,6 +4820,13 @@ export function fieldInitStmts(lowerer, info, thisLocal) {
|
|
|
4383
4820
|
const out = [];
|
|
4384
4821
|
const thisType = { kind: "object", className: info.def.name };
|
|
4385
4822
|
for (const f of info.fieldOrder) {
|
|
4823
|
+
if (f.errorCause) {
|
|
4824
|
+
const loc = f.initializer ? locOf(f.initializer) : f.undefinedInitializer;
|
|
4825
|
+
const value = f.initializer ? lowerer.lowerExprExpecting(f.initializer, DYN) : dynUndefinedExpr(loc);
|
|
4826
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "error.defineCause",
|
|
4827
|
+
args: [{ kind: "varRef", localId: thisLocal.id, type: thisType, loc }, value], type: VOID, loc }, loc });
|
|
4828
|
+
continue;
|
|
4829
|
+
}
|
|
4386
4830
|
if (f.undefinedInitializer) {
|
|
4387
4831
|
const loc = f.undefinedInitializer;
|
|
4388
4832
|
out.push({
|
|
@@ -4455,100 +4899,93 @@ function paramPropInitStmts(lowerer, info, thisLocal) {
|
|
|
4455
4899
|
}
|
|
4456
4900
|
return out;
|
|
4457
4901
|
}
|
|
4458
|
-
/**
|
|
4459
|
-
*
|
|
4460
|
-
*
|
|
4461
|
-
|
|
4462
|
-
* guarantees a super call exists and runs before any this-use; the
|
|
4463
|
-
* supported form is a top-level expression statement — anywhere else
|
|
4464
|
-
* (conditionals, expression positions) is rejected, not misordered. */
|
|
4465
|
-
export function lowerDerivedCtorBody(lowerer, info, thisLocal,
|
|
4466
|
-
/** Mixin forwarding-constructor mode: `super(...args)` forwards these
|
|
4467
|
-
* pre-declared synthetic params directly (the spread never lowers —
|
|
4468
|
-
* the base's ABI is this constructor's ABI). */
|
|
4469
|
-
forward) {
|
|
4470
|
-
const out = [];
|
|
4902
|
+
/** Super statements initialize the base and then the derived fields on
|
|
4903
|
+
* whichever control-flow path executes. Repeated calls are explicitly
|
|
4904
|
+
* refused until native layouts can allocate a second receiver. */
|
|
4905
|
+
export function lowerDerivedCtorBody(lowerer, info, thisLocal, forward) {
|
|
4471
4906
|
let superSeen = false;
|
|
4472
|
-
const
|
|
4473
|
-
|
|
4474
|
-
lowerer.
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4907
|
+
const initialized = lowerer.declareHiddenLocal("%superInitialized", BOOL);
|
|
4908
|
+
initialized.mutable = true;
|
|
4909
|
+
const context = lowerer.ctx;
|
|
4910
|
+
const previous = context.superCall;
|
|
4911
|
+
const previousInitialized = context.superInitialized;
|
|
4912
|
+
context.superInitialized = initialized;
|
|
4913
|
+
context.superCall = (superCall) => {
|
|
4914
|
+
if (!ts.isExpressionStatement(superCall.parent))
|
|
4915
|
+
lowerer.unsupported("SC1090", superCall, "super() in an expression position");
|
|
4916
|
+
superSeen = true;
|
|
4917
|
+
const loc = locOf(superCall);
|
|
4918
|
+
const statements = [{ kind: "if", cond: { kind: "varRef", localId: initialized.id, type: BOOL, loc }, then: [{ kind: "exprStmt", expr: nodeThrowExpr(0, "SC2020", "repeated super() calls require a fresh receiver and are not supported yet", DYN, loc), loc }], else_: null, loc }];
|
|
4919
|
+
if (info.callableBase) {
|
|
4920
|
+
if (superCall.arguments.some(ts.isSpreadElement))
|
|
4921
|
+
lowerer.unsupported("SC1090", superCall, "spreading explicit super arguments into an ordinary constructor");
|
|
4922
|
+
const pack = { kind: "dynArrLit", elems: superCall.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc };
|
|
4923
|
+
statements.push(lowerer.superCallStmt(info, thisLocal, [pack], loc));
|
|
4924
|
+
}
|
|
4925
|
+
else {
|
|
4926
|
+
const base = superBaseOf(info);
|
|
4927
|
+
if (base.builtinEmitter && superCall.arguments.length > 0)
|
|
4928
|
+
lowerer.unsupported("SC1090", superCall, "EventEmitter constructor options ('captureRejections')");
|
|
4929
|
+
if (base.builtinStream) {
|
|
4930
|
+
statements.push(...lowerStreamSuperCall(lowerer, info, base, superCall.arguments, thisLocal, loc, superCall));
|
|
4491
4931
|
}
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
const loc = locOf(stmt);
|
|
4500
|
-
const pack = { kind: "dynArrLit", elems: superCall.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc };
|
|
4501
|
-
out.push(lowerer.superCallStmt(info, thisLocal, [pack], loc));
|
|
4502
|
-
out.push(...lowerer.fieldInitStmts(info, thisLocal));
|
|
4503
|
-
out.push(...paramPropInitStmts(lowerer, info, thisLocal));
|
|
4504
|
-
continue;
|
|
4505
|
-
}
|
|
4506
|
-
const base = superBaseOf(info);
|
|
4507
|
-
if (base.builtinEmitter && superCall.arguments.length > 0) {
|
|
4508
|
-
// @types/node admits super({ captureRejections }) — no lowering.
|
|
4509
|
-
lowerer.unsupported("SC1090", superCall, "EventEmitter constructor options ('captureRejections')");
|
|
4510
|
-
}
|
|
4511
|
-
if (base.builtinStream) {
|
|
4512
|
-
// super(options?) into a runtime stream base: the stream spoke
|
|
4513
|
-
// parses the options and binds overridden underscore methods.
|
|
4514
|
-
out.push(...lowerStreamSuperCall(lowerer, info, base, superCall.arguments, thisLocal, locOf(stmt), stmt));
|
|
4515
|
-
out.push(...lowerer.fieldInitStmts(info, thisLocal));
|
|
4516
|
-
out.push(...paramPropInitStmts(lowerer, info, thisLocal));
|
|
4517
|
-
continue;
|
|
4932
|
+
else {
|
|
4933
|
+
const surplusSpread = forward === undefined && !base.builtinError && !base.builtinEmitter &&
|
|
4934
|
+
base.ctorParams.length === 0 && superCall.arguments.some(ts.isSpreadElement);
|
|
4935
|
+
if (surplusSpread) {
|
|
4936
|
+
const evaluated = lowerer.completeArgs(superCall.arguments, [{ mode: "dynRest", type: DYN }], loc, superCall);
|
|
4937
|
+
for (const value of evaluated)
|
|
4938
|
+
statements.push({ kind: "exprStmt", expr: value, loc });
|
|
4518
4939
|
}
|
|
4519
|
-
const
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
throw e;
|
|
4535
|
-
if (!lowerer.suppressStats) {
|
|
4536
|
-
lowerer.stats.statementsFailed++;
|
|
4537
|
-
lowerer.bumpFileStat(locOf(stmt).file, "failed");
|
|
4940
|
+
const surplus = forward === undefined && !base.builtinError && !base.builtinEmitter &&
|
|
4941
|
+
!base.ctorParams.some((param) => param.mode === "rest" || param.mode === "dynRest" || param.mode === "islandRest") &&
|
|
4942
|
+
!superCall.arguments.some(ts.isSpreadElement) && superCall.arguments.length > base.ctorParams.length;
|
|
4943
|
+
let args = surplusSpread ? [] : forward !== undefined ? forward : base.builtinError
|
|
4944
|
+
? lowerer.errorConstructorArgs(superCall.arguments, loc, superCall) : base.builtinEmitter ? []
|
|
4945
|
+
: lowerer.completeArgs(surplus ? superCall.arguments.slice(0, base.ctorParams.length) : superCall.arguments, base.ctorParams, loc, superCall);
|
|
4946
|
+
if (surplus) {
|
|
4947
|
+
args = args.map((arg) => {
|
|
4948
|
+
const local = lowerer.declareHiddenLocal("%superArgument", arg.type);
|
|
4949
|
+
statements.push({ kind: "varDecl", localId: local.id, init: arg, loc });
|
|
4950
|
+
return { kind: "varRef", localId: local.id, type: arg.type, loc };
|
|
4951
|
+
});
|
|
4952
|
+
for (const argument of superCall.arguments.slice(base.ctorParams.length)) {
|
|
4953
|
+
statements.push({ kind: "exprStmt", expr: lowerer.lowerExpr(argument), loc: locOf(argument) });
|
|
4954
|
+
}
|
|
4538
4955
|
}
|
|
4956
|
+
statements.push(lowerer.superCallStmt(info, thisLocal, args, loc));
|
|
4539
4957
|
}
|
|
4540
4958
|
}
|
|
4959
|
+
statements.push({ kind: "assign", localId: initialized.id, value: { kind: "boolLit", value: true, type: BOOL, loc }, loc });
|
|
4960
|
+
delete context.superInitialized;
|
|
4961
|
+
try {
|
|
4962
|
+
statements.push(...lowerer.fieldInitStmts(info, thisLocal), ...paramPropInitStmts(lowerer, info, thisLocal));
|
|
4963
|
+
}
|
|
4964
|
+
finally {
|
|
4965
|
+
context.superInitialized = initialized;
|
|
4966
|
+
}
|
|
4967
|
+
const result = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
|
|
4968
|
+
return { kind: "seqExpr", stmts: statements.map((statement) => statement.kind === "runtimeFence"
|
|
4969
|
+
? { kind: "exprStmt", expr: nodeThrowExpr(0, statement.code, statement.message, DYN, statement.loc), loc: statement.loc }
|
|
4970
|
+
: statement), result, type: result.type, loc };
|
|
4971
|
+
};
|
|
4972
|
+
try {
|
|
4973
|
+
const result = lowerer.lowerStmts(info.ctor.body.statements);
|
|
4974
|
+
result.push({ kind: "if", cond: { kind: "unary", op: "!", operand: { kind: "varRef", localId: initialized.id, type: BOOL, loc: locOf(info.ctor) }, type: BOOL, loc: locOf(info.ctor) }, then: [{ kind: "exprStmt", expr: nodeThrowExpr(5, "", "Must call super constructor in derived class before accessing 'this' or returning from derived constructor", DYN, locOf(info.ctor)), loc: locOf(info.ctor) }], else_: null, loc: locOf(info.ctor) });
|
|
4975
|
+
if (!superSeen)
|
|
4976
|
+
lowerer.pushDiag(unsupportedDiag("SC1090", locOf(info.ctor), "derived constructors without a lowered super() call"));
|
|
4977
|
+
return [{ kind: "varDecl", localId: initialized.id, init: { kind: "boolLit", value: false, type: BOOL, loc: locOf(info.ctor) }, loc: locOf(info.ctor) }, ...result];
|
|
4541
4978
|
}
|
|
4542
4979
|
finally {
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4980
|
+
if (previous)
|
|
4981
|
+
context.superCall = previous;
|
|
4982
|
+
else
|
|
4983
|
+
delete context.superCall;
|
|
4984
|
+
if (previousInitialized)
|
|
4985
|
+
context.superInitialized = previousInitialized;
|
|
4986
|
+
else
|
|
4987
|
+
delete context.superInitialized;
|
|
4550
4988
|
}
|
|
4551
|
-
return out;
|
|
4552
4989
|
}
|
|
4553
4990
|
/** `super(args)` → direct call of the base constructor with the SAME
|
|
4554
4991
|
* `this` (upcast; retained by the varRef read — the callee owns and
|
|
@@ -4564,7 +5001,10 @@ export function superCallStmt(lowerer, info, thisLocal, args, loc) {
|
|
|
4564
5001
|
if (info.callableBase)
|
|
4565
5002
|
return {
|
|
4566
5003
|
kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classSuper",
|
|
4567
|
-
args: [{ kind: "
|
|
5004
|
+
args: [info.localClass ? { kind: "libCall", fn: "dyn.getPrototype", args: [
|
|
5005
|
+
{ kind: "dynFrom", value: { kind: "fieldGet", obj: thisRef, className: info.def.name,
|
|
5006
|
+
field: `%classEnvironment:${info.def.name}`, type: { kind: "classval", className: info.def.name }, loc }, type: DYN, loc },
|
|
5007
|
+
], type: DYN, loc } : { kind: "varRef", localId: info.callableBase.constructorId, type: DYN, loc },
|
|
4568
5008
|
lowerer.coerceToExpected(thisRef, DYN), args[0]], type: VOID, loc }, loc,
|
|
4569
5009
|
};
|
|
4570
5010
|
if (base.builtinError) {
|
|
@@ -4682,6 +5122,16 @@ export function lowerSuperMethodCall(lowerer, call, access) {
|
|
|
4682
5122
|
lowerer.unsupported("SC1080", access);
|
|
4683
5123
|
const loc = locOf(call);
|
|
4684
5124
|
const thisRef = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
|
|
5125
|
+
if (lowerer.prototypeMethodAccesses.has(access.name.text)) {
|
|
5126
|
+
const prototype = classPrototypeData(lowerer, cls.base, loc);
|
|
5127
|
+
if (prototype) {
|
|
5128
|
+
const callee = { kind: "dynKeyGet", value: prototype,
|
|
5129
|
+
key: { kind: "strLit", value: access.name.text, type: STRING, loc }, type: DYN, loc };
|
|
5130
|
+
const value = { kind: "dynCall", callee, receiver: lowerer.coerceToExpected(thisRef, DYN),
|
|
5131
|
+
calleeName: access.getText(), args: call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc };
|
|
5132
|
+
return found.sig.ret.kind === "void" ? value : lowerer.coerceToExpected(value, found.sig.ret);
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
4685
5135
|
if (found.declarer.builtinError) {
|
|
4686
5136
|
return { kind: "libCall", fn: "error.toString", args: [lowerer.upcastTo(thisRef, "%Error")], type: STRING, loc };
|
|
4687
5137
|
}
|
|
@@ -4865,6 +5315,11 @@ function lowerProgramClassNew(lowerer, expr, declaredInfo, loc) {
|
|
|
4865
5315
|
const args = lowerer.inheritsBuiltinErrorCtor(info)
|
|
4866
5316
|
? lowerer.errorConstructorArgs(expr.arguments ?? [], loc, expr)
|
|
4867
5317
|
: lowerer.completeArgs(expr.arguments ?? [], info.ctorParams, loc, expr);
|
|
5318
|
+
for (let current = info; current; current = current.base) {
|
|
5319
|
+
if (current.def.baseValueGlobal)
|
|
5320
|
+
return { kind: "newValue", callee: classValueRef(lowerer, info, expr),
|
|
5321
|
+
args, type: { kind: "object", className: info.def.name }, loc };
|
|
5322
|
+
}
|
|
4868
5323
|
return {
|
|
4869
5324
|
kind: "new",
|
|
4870
5325
|
className: info.def.name,
|
|
@@ -4892,6 +5347,16 @@ export function lowerNew(lowerer, expr) {
|
|
|
4892
5347
|
const objectFactory = lowerObjectFactoryNew(lowerer, expr);
|
|
4893
5348
|
if (objectFactory)
|
|
4894
5349
|
return objectFactory;
|
|
5350
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
5351
|
+
const symbol = lowerer.resolveValueSymbol(expr.expression);
|
|
5352
|
+
const declaration = symbol && lowerer.checker.declarationsOf(symbol).find((node) => ts.isFunctionDeclaration(node) && node.body !== undefined);
|
|
5353
|
+
if (declaration && ts.isFunctionDeclaration(declaration) && declaration.body) {
|
|
5354
|
+
const last = declaration.body.statements.at(-1);
|
|
5355
|
+
if (last && ts.isReturnStatement(last) && last.expression && ts.isObjectLiteralExpression(last.expression)) {
|
|
5356
|
+
lowerer.unsupported("SC1090", expr, "object-returning constructors that observe this or return mixed primitive/object values");
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
4895
5360
|
let selected = expr.expression;
|
|
4896
5361
|
while (ts.isParenthesizedExpression(selected))
|
|
4897
5362
|
selected = selected.expression;
|
|
@@ -5004,9 +5469,11 @@ export function lowerNew(lowerer, expr) {
|
|
|
5004
5469
|
if (info?.classDecorators?.valueGlobalId !== undefined) {
|
|
5005
5470
|
lowerer.unsupported("SC1090", expr, "constructing a decorated class through a qualified name (a replacing decorator rebinds the class name — construct through the bare name)");
|
|
5006
5471
|
}
|
|
5007
|
-
if (info)
|
|
5472
|
+
if (info && !info.localClass)
|
|
5008
5473
|
return lowerProgramClassNew(lowerer, expr, info, loc);
|
|
5009
|
-
|
|
5474
|
+
// A namespace export can hold a class returned by a factory.
|
|
5475
|
+
// Let the class-value path below preserve that constructor's
|
|
5476
|
+
// lexical environment instead of requiring a class declaration.
|
|
5010
5477
|
}
|
|
5011
5478
|
// `new B.C()` where B is an AMBIENT namespace (fundule merges
|
|
5012
5479
|
// included): Node evaluates the callee first and throws
|
|
@@ -5092,11 +5559,17 @@ export function lowerNew(lowerer, expr) {
|
|
|
5092
5559
|
if (agent)
|
|
5093
5560
|
return agent;
|
|
5094
5561
|
}
|
|
5095
|
-
if (ts.isIdentifier(expr.expression))
|
|
5562
|
+
if (ts.isIdentifier(expr.expression) || ts.isPropertyAccessExpression(expr.expression) && ts.isIdentifier(expr.expression.name) &&
|
|
5563
|
+
lowerer.isStdlibGlobal(expr.expression.expression, "globalThis")) {
|
|
5096
5564
|
// `import C = N.C; new C()` — the alias's own source-order guards
|
|
5097
5565
|
// (a no-op for every non-import= binding).
|
|
5098
|
-
|
|
5099
|
-
|
|
5566
|
+
if (ts.isIdentifier(expr.expression))
|
|
5567
|
+
fenceEarlyAliasUse(lowerer, expr.expression, expr);
|
|
5568
|
+
const identifier = ts.isIdentifier(expr.expression) ? expr.expression : expr.expression.name;
|
|
5569
|
+
const symbol = ts.isIdentifier(identifier) ? lowerer.resolveValueSymbol(identifier) : null;
|
|
5570
|
+
if (ts.isIdentifier(identifier) && lowerer.isStdlibGlobal(identifier, "Array")) {
|
|
5571
|
+
return lowerArrayConstructor(lowerer, expr, expr.arguments ?? []);
|
|
5572
|
+
}
|
|
5100
5573
|
if (isNativeProxyInitializer(lowerer, expr))
|
|
5101
5574
|
return lowerNativeProxy(lowerer, expr);
|
|
5102
5575
|
// `new Error(msg?)` and its standard subclasses: the
|
|
@@ -5307,20 +5780,10 @@ export function lowerNew(lowerer, expr) {
|
|
|
5307
5780
|
// types get their half named specifically instead of the component
|
|
5308
5781
|
// fence (SC2009, which names Map slots at value positions elsewhere).
|
|
5309
5782
|
// Both Array() and new Array() share the elements/count lowering.
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
// story with no lowering — so it keeps the constructor fence.
|
|
5315
|
-
if (symbol?.name === "Object" &&
|
|
5316
|
-
lowerer.isStdlibSymbol(symbol) &&
|
|
5317
|
-
(expr.arguments ?? []).length === 0) {
|
|
5318
|
-
return {
|
|
5319
|
-
kind: "recordLit",
|
|
5320
|
-
fields: [],
|
|
5321
|
-
type: { kind: "record", shapeId: lowerer.shapes.intern([]) },
|
|
5322
|
-
loc,
|
|
5323
|
-
};
|
|
5783
|
+
if (symbol?.name === "Object" && lowerer.isStdlibSymbol(symbol)) {
|
|
5784
|
+
const object = lowerObjectConstructor(lowerer, expr.arguments ?? [], loc);
|
|
5785
|
+
if (object)
|
|
5786
|
+
return object;
|
|
5324
5787
|
}
|
|
5325
5788
|
if (symbol?.name === "Array" && lowerer.isStdlibSymbol(symbol)) {
|
|
5326
5789
|
return lowerArrayConstructor(lowerer, expr, expr.arguments ?? []);
|
|
@@ -5440,6 +5903,10 @@ export function lowerNew(lowerer, expr) {
|
|
|
5440
5903
|
if (seeded)
|
|
5441
5904
|
return seeded;
|
|
5442
5905
|
}
|
|
5906
|
+
if (!mapped && isJsSourceFile(expr.getSourceFile()) && (expr.arguments?.length ?? 0) <= 1 && !expr.arguments?.some(ts.isSpreadElement)) {
|
|
5907
|
+
return { kind: "libCall", fn: "dyn.nativeSetNew", args: [expr.arguments?.[0]
|
|
5908
|
+
? lowerer.lowerExprExpecting(expr.arguments[0], DYN) : dynUndefinedExpr(loc)], type: DYN, loc };
|
|
5909
|
+
}
|
|
5443
5910
|
// JavaScript's identity-Set idiom: `new Set([setTimeout, atob,
|
|
5444
5911
|
// ...])` — the element TYPE (a union of stdlib signatures) has no
|
|
5445
5912
|
// mapping, but the element VALUES all lower to identity tokens
|
|
@@ -5583,7 +6050,7 @@ export function lowerNew(lowerer, expr) {
|
|
|
5583
6050
|
Proxy: "native Proxy construction requires checked-native plain targets and handlers",
|
|
5584
6051
|
Function: "runtime code generation cannot be compiled ahead of time (the eval stance) — write the function",
|
|
5585
6052
|
};
|
|
5586
|
-
lowerer.noLowering(`new ${expr.expression.text}`, expr, ctorHints[expr.expression.text], symbol);
|
|
6053
|
+
lowerer.noLowering(`new ${expr.expression.text}`, expr, ctorHints[ts.isIdentifier(expr.expression) ? expr.expression.text : expr.expression.name.text], symbol);
|
|
5587
6054
|
}
|
|
5588
6055
|
}
|
|
5589
6056
|
// `new crypto.X509Certificate(data)` — the Dirent-style data record:
|
|
@@ -5696,6 +6163,8 @@ export function lowerNew(lowerer, expr) {
|
|
|
5696
6163
|
lowerer.unsupported("SC1090", expr, "constructing through a class value of an uninstantiated generic type (annotate the slot with a concrete instantiation — e.g. 'new (v: number) => Box<number>')");
|
|
5697
6164
|
}
|
|
5698
6165
|
const callee = lowerer.lowerExpr(expr.expression);
|
|
6166
|
+
if (callee.type.kind === "dyn")
|
|
6167
|
+
return checkedClassConstruction(lowerer, callee, (expr.arguments ?? []).map((arg) => lowerer.lowerExprExpecting(arg, DYN)), loc);
|
|
5699
6168
|
if (callee.type.kind !== "classval")
|
|
5700
6169
|
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
5701
6170
|
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
@@ -5745,6 +6214,11 @@ export function lowerNew(lowerer, expr) {
|
|
|
5745
6214
|
}
|
|
5746
6215
|
}
|
|
5747
6216
|
}
|
|
6217
|
+
// Checked construction supports emitted classes and builtin typed arrays.
|
|
6218
|
+
const callee = tryLowerExpression(lowerer, expr.expression);
|
|
6219
|
+
if (callee && (callee.type.kind === "dyn" || callee.type.kind === "func" && lowerer.dynConvertible(callee.type)) && !(expr.arguments ?? []).some(ts.isSpreadElement)) {
|
|
6220
|
+
return checkedClassConstruction(lowerer, lowerer.coerceToExpected(callee, DYN), (expr.arguments ?? []).map((arg) => lowerer.lowerExprExpecting(arg, DYN)), loc);
|
|
6221
|
+
}
|
|
5748
6222
|
lowerer.unsupported("SC1090", expr, "constructing values other than classes declared in the program");
|
|
5749
6223
|
}
|
|
5750
6224
|
/** A getter/setter invocation over an accessor target's receiver — the
|