@scriptc/compiler 0.2.0 → 0.2.2
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 +84 -19
- 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 +8 -0
- package/dist/backend/llvm/dyn.js +247 -108
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +6 -3
- package/dist/backend/llvm/emitter.js +99 -25
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +9 -1
- package/dist/backend/llvm/emitter.test.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-containers.js +5 -0
- package/dist/backend/llvm/expr-containers.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 +47 -13
- 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 +187 -18
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +2 -2
- package/dist/backend/llvm/expr-stream-callbacks.js +7 -57
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +41 -3
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +12 -9
- package/dist/backend/llvm/lib-filesystem.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 +80 -3
- 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/may-throw.js +4 -0
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +1 -1
- package/dist/backend/native-codegen.js +4 -3
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +11 -0
- package/dist/backend/native-codegen.test.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/target-diagnostics.d.ts +5 -0
- package/dist/backend/target-diagnostics.js +72 -1
- package/dist/backend/target-diagnostics.js.map +1 -1
- package/dist/backend/target-diagnostics.test.d.ts +1 -0
- package/dist/backend/target-diagnostics.test.js +68 -0
- package/dist/backend/target-diagnostics.test.js.map +1 -0
- 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/coverage/surface-manifest.js +5 -3
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -7
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +7 -3
- package/dist/executable/executable-cache.js +47 -11
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +109 -2
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.js +2 -1
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/builtin-modules.d.ts +1 -1
- package/dist/frontend/builtin-modules.js +1 -1
- package/dist/frontend/builtin-modules.js.map +1 -1
- package/dist/frontend/function-completion.d.ts +6 -0
- package/dist/frontend/function-completion.js +50 -0
- package/dist/frontend/function-completion.js.map +1 -0
- package/dist/frontend/lowering/abstract-equality.js +5 -1
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/checked-iterable-spread.d.ts +4 -0
- package/dist/frontend/lowering/checked-iterable-spread.js +30 -0
- package/dist/frontend/lowering/checked-iterable-spread.js.map +1 -0
- package/dist/frontend/lowering/checked-promise-all.d.ts +5 -0
- package/dist/frontend/lowering/checked-promise-all.js +31 -0
- package/dist/frontend/lowering/checked-promise-all.js.map +1 -0
- package/dist/frontend/lowering/class-callbacks.d.ts +4 -4
- package/dist/frontend/lowering/class-callbacks.js +29 -17
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-construction.d.ts +14 -0
- package/dist/frontend/lowering/class-construction.js +185 -0
- package/dist/frontend/lowering/class-construction.js.map +1 -0
- package/dist/frontend/lowering/class-descriptors.d.ts +2 -1
- package/dist/frontend/lowering/class-descriptors.js +58 -10
- package/dist/frontend/lowering/class-descriptors.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +36 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +965 -91
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +66 -3
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
- package/dist/frontend/lowering/class-instance-constructor.d.ts +1 -1
- package/dist/frontend/lowering/class-instance-constructor.js +15 -42
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
- package/dist/frontend/lowering/class-method-values.d.ts +20 -1
- package/dist/frontend/lowering/class-method-values.js +120 -24
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.d.ts +10 -3
- package/dist/frontend/lowering/class-prototypes.js +188 -21
- 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 +84 -0
- package/dist/frontend/lowering/class-runtime-statics.js.map +1 -0
- package/dist/frontend/lowering/class-static-data.d.ts +1 -0
- package/dist/frontend/lowering/class-static-data.js +6 -4
- package/dist/frontend/lowering/class-static-data.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +66 -17
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +24 -47
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/deferred-module-initializers.d.ts +13 -0
- package/dist/frontend/lowering/deferred-module-initializers.js +410 -0
- package/dist/frontend/lowering/deferred-module-initializers.js.map +1 -0
- package/dist/frontend/lowering/deferred-module-initializers.test.d.ts +1 -0
- package/dist/frontend/lowering/deferred-module-initializers.test.js +92 -0
- package/dist/frontend/lowering/deferred-module-initializers.test.js.map +1 -0
- 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 +68 -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 +72 -20
- 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 +10 -14
- package/dist/frontend/lowering/lower-builtins.js +443 -88
- 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 +717 -142
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +18 -13
- package/dist/frontend/lowering/lower-classes.js +1076 -188
- 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 +172 -51
- 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 +12 -8
- package/dist/frontend/lowering/lower-exprs.js +802 -232
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +17 -2
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +15 -1
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +2 -0
- package/dist/frontend/lowering/lower-island.js +36 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-island.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-island.test.js +51 -0
- package/dist/frontend/lowering/lower-island.test.js.map +1 -0
- package/dist/frontend/lowering/lower-modules.d.ts +1 -3
- package/dist/frontend/lowering/lower-modules.js +380 -73
- 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 +107 -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 +221 -47
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-stmts.test.js +77 -0
- package/dist/frontend/lowering/lower-stmts.test.js.map +1 -0
- package/dist/frontend/lowering/lower-stream.js +3 -1
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-url.d.ts +2 -1
- package/dist/frontend/lowering/lower-url.js +32 -9
- package/dist/frontend/lowering/lower-url.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +53 -10
- package/dist/frontend/lowering/lowerer.js +390 -77
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +187 -3
- package/dist/frontend/lowering/lowerer.test.js.map +1 -1
- package/dist/frontend/lowering/module-namespace-elements.d.ts +7 -0
- package/dist/frontend/lowering/module-namespace-elements.js +54 -0
- package/dist/frontend/lowering/module-namespace-elements.js.map +1 -0
- package/dist/frontend/lowering/object-factory-new.d.ts +4 -3
- package/dist/frontend/lowering/object-factory-new.js +27 -14
- package/dist/frontend/lowering/object-factory-new.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +3 -0
- package/dist/frontend/lowering/surfaces.js +48 -19
- 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 +8 -0
- package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.test.js +2 -1
- package/dist/frontend/lowering/text-decoder-encoding.test.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 +57 -26
- 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 +169 -23
- 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 +24 -9
- package/dist/frontend/union-discriminants.js.map +1 -1
- package/dist/index.js +16 -10
- package/dist/index.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 +74 -41
- package/dist/ir/ir.js +167 -43
- 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 +35 -3
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +26 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/traverse.d.ts +0 -3
- package/dist/ir/traverse.js +33 -20
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/validate.js +136 -16
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +77 -1
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js +1 -1
- package/dist/library/compiler-self-identity.test.js +2 -2
- package/dist/library/compiler-self-identity.test.js.map +1 -1
- package/dist/library/int-infer.d.ts +2 -2
- package/dist/library/int-infer.js +10 -7
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +44 -0
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/prepare.js +2 -1
- package/dist/library/prepare.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 +170 -93
|
@@ -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,14 @@ 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, checkedClassConstructionPacked } from "./class-construction.js";
|
|
35
|
+
import { initializeRuntimeStatics, hasRuntimeStatics } from "./class-runtime-statics.js";
|
|
36
|
+
import { reflectedClassStaticMethodValue } from "./class-method-values.js";
|
|
33
37
|
export function returnsOnlyThis(member) {
|
|
34
38
|
const body = member.body;
|
|
35
39
|
const last = body?.statements.at(-1);
|
|
@@ -58,11 +62,20 @@ function functionLocalClass(decl) {
|
|
|
58
62
|
}
|
|
59
63
|
return false;
|
|
60
64
|
}
|
|
61
|
-
export function storedClassValueType(lowerer, expression) {
|
|
65
|
+
export function storedClassValueType(lowerer, expression, visiting = new Set()) {
|
|
66
|
+
if (visiting.has(expression))
|
|
67
|
+
return null;
|
|
68
|
+
visiting.add(expression);
|
|
69
|
+
if (ts.isParenthesizedExpression(expression))
|
|
70
|
+
return storedClassValueType(lowerer, expression.expression, visiting);
|
|
71
|
+
if (ts.isClassExpression(expression) && !functionLocalClass(expression) && !lowerer.collectingExprClasses.has(expression)) {
|
|
72
|
+
const info = lowerer.lowerClassExpressionInfo(expression);
|
|
73
|
+
return { kind: "classval", className: info.def.name };
|
|
74
|
+
}
|
|
62
75
|
if (ts.isPropertyAccessExpression(expression) && !expression.questionDotToken) {
|
|
63
76
|
const member = nsMemberIdentOf(lowerer, expression);
|
|
64
77
|
if (member)
|
|
65
|
-
return storedClassValueType(lowerer, member);
|
|
78
|
+
return storedClassValueType(lowerer, member, visiting);
|
|
66
79
|
}
|
|
67
80
|
if (expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
68
81
|
const stored = lowerer.resolveThis();
|
|
@@ -71,10 +84,149 @@ export function storedClassValueType(lowerer, expression) {
|
|
|
71
84
|
}
|
|
72
85
|
if (ts.isIdentifier(expression)) {
|
|
73
86
|
const stored = lowerer.peekLocal(expression) ?? lowerer.globalOf(expression);
|
|
74
|
-
if (stored?.type.kind === "classval")
|
|
87
|
+
if (stored?.type.kind === "classval" && lowerer.classes.has(stored.type.className))
|
|
75
88
|
return stored.type;
|
|
89
|
+
const symbol = lowerer.resolveValueSymbol(expression);
|
|
90
|
+
const declaration = symbol && lowerer.checker.declarationsOf(symbol).find((decl) => ts.isVariableDeclaration(decl) && decl.initializer && (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0);
|
|
91
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
92
|
+
const value = storedClassValueType(lowerer, declaration.initializer, visiting);
|
|
93
|
+
if (value?.kind === "classval")
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const type = lowerer.typeOf(expression);
|
|
98
|
+
const mapped = lowerer.mapTypeOf(type);
|
|
99
|
+
if (mapped?.kind === "classval" && !lowerer.classes.has(mapped.className)) {
|
|
100
|
+
for (const signature of lowerer.checker.getConstructSignatures(type)) {
|
|
101
|
+
const constructor = lowerer.checker.signatureDeclaration(signature);
|
|
102
|
+
const declaration = constructor && ts.isConstructorDeclaration(constructor) ? constructor.parent : undefined;
|
|
103
|
+
if (declaration && ts.isClassExpression(declaration) && !functionLocalClass(declaration) &&
|
|
104
|
+
!lowerer.collectingExprClasses.has(declaration)) {
|
|
105
|
+
const info = lowerer.lowerClassExpressionInfo(declaration);
|
|
106
|
+
return { kind: "classval", className: info.def.name };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (mapped?.kind === "classval" || !ts.isCallExpression(expression))
|
|
111
|
+
return mapped;
|
|
112
|
+
let callee = expression.expression;
|
|
113
|
+
let calls = 1;
|
|
114
|
+
const aliases = new Set();
|
|
115
|
+
for (;;) {
|
|
116
|
+
if (aliases.has(callee))
|
|
117
|
+
break;
|
|
118
|
+
aliases.add(callee);
|
|
119
|
+
if (ts.isParenthesizedExpression(callee)) {
|
|
120
|
+
callee = callee.expression;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (ts.isCallExpression(callee)) {
|
|
124
|
+
calls++;
|
|
125
|
+
callee = callee.expression;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
129
|
+
const member = nsMemberIdentOf(lowerer, callee);
|
|
130
|
+
if (member) {
|
|
131
|
+
callee = member;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (ts.isIdentifier(callee)) {
|
|
136
|
+
const symbol = lowerer.resolveValueSymbol(callee);
|
|
137
|
+
const declaration = symbol && lowerer.checker.declarationsOf(symbol).find((decl) => ts.isVariableDeclaration(decl) && decl.initializer && (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0);
|
|
138
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
139
|
+
callee = declaration.initializer;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
if ((ts.isArrowFunction(callee) || ts.isFunctionExpression(callee)) && isJsSourceFile(callee.getSourceFile()) &&
|
|
146
|
+
!callee.typeParameters?.length && !lowerer.preparingClassFactories.has(callee)) {
|
|
147
|
+
const signature = lowerer.checker.getSignatureFromDeclaration(callee);
|
|
148
|
+
let result = signature && lowerer.checker.getReturnTypeOfSignature(signature);
|
|
149
|
+
for (let i = 1; result && i < calls; i++) {
|
|
150
|
+
const signatures = lowerer.checker.getCallSignatures(result);
|
|
151
|
+
result = signatures.length === 1 ? lowerer.checker.getReturnTypeOfSignature(signatures[0]) : undefined;
|
|
152
|
+
}
|
|
153
|
+
if (result && lowerer.checker.getConstructSignatures(result).length > 0) {
|
|
154
|
+
const returned = (value) => {
|
|
155
|
+
let type = value.type;
|
|
156
|
+
for (let i = 0; i < calls && type.kind === "func"; i++)
|
|
157
|
+
type = type.ret;
|
|
158
|
+
return type.kind === "classval" ? type : null;
|
|
159
|
+
};
|
|
160
|
+
const home = callee.parent;
|
|
161
|
+
const generic = ts.isVariableDeclaration(home) && ts.isIdentifier(home.name) ? lowerer.genericFnOf(home.name) : null;
|
|
162
|
+
if (generic?.implicitParams && ts.isVariableDeclaration(home) && ts.isIdentifier(home.name)) {
|
|
163
|
+
const factory = lowerer.lowerGenericFnValue(home.name, generic);
|
|
164
|
+
const type = returned(factory);
|
|
165
|
+
if (type)
|
|
166
|
+
return type;
|
|
167
|
+
}
|
|
168
|
+
lowerer.preparingClassFactories.add(callee);
|
|
169
|
+
const collecting = lowerer.collecting, sink = lowerer.diagSink;
|
|
170
|
+
lowerer.collecting = false;
|
|
171
|
+
lowerer.diagSink = null;
|
|
172
|
+
try {
|
|
173
|
+
const factory = lowerer.preparedClassFactories.get(callee) ?? lowerer.lowerLambda(callee);
|
|
174
|
+
lowerer.preparedClassFactories.set(callee, factory);
|
|
175
|
+
const type = returned(factory);
|
|
176
|
+
if (type)
|
|
177
|
+
return type;
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
lowerer.collecting = collecting;
|
|
181
|
+
lowerer.diagSink = sink;
|
|
182
|
+
lowerer.preparingClassFactories.delete(callee);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
76
185
|
}
|
|
77
|
-
|
|
186
|
+
// A checked call can erase its returned constructor's ABI while the
|
|
187
|
+
// checker still identifies the class declaration. Collect that class in
|
|
188
|
+
// its factory's lexical scope before choosing the derived native layout.
|
|
189
|
+
// Cache the module-level closure so its captures and lifted bodies are
|
|
190
|
+
// exactly the ones used when the declaration executes.
|
|
191
|
+
for (const signature of lowerer.checker.getConstructSignatures(type)) {
|
|
192
|
+
const declaration = lowerer.checker.signatureDeclaration(signature);
|
|
193
|
+
const cls = declaration && ts.isConstructorDeclaration(declaration) ? declaration.parent : null;
|
|
194
|
+
if (!cls || (!ts.isClassExpression(cls) && !ts.isClassDeclaration(cls)))
|
|
195
|
+
continue;
|
|
196
|
+
let factory;
|
|
197
|
+
for (let parent = cls.parent; parent && !ts.isSourceFile(parent); parent = parent.parent) {
|
|
198
|
+
if (ts.isArrowFunction(parent) || ts.isFunctionExpression(parent))
|
|
199
|
+
factory = parent;
|
|
200
|
+
else if (ts.isFunctionLike(parent)) {
|
|
201
|
+
factory = undefined;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (!factory || !isJsSourceFile(factory.getSourceFile()) || factory.typeParameters?.length ||
|
|
206
|
+
!ts.isVariableDeclaration(factory.parent) || factory.parent.initializer !== factory ||
|
|
207
|
+
!ts.isVariableDeclarationList(factory.parent.parent) || !(factory.parent.parent.flags & ts.NodeFlags.Const) ||
|
|
208
|
+
!ts.isVariableStatement(factory.parent.parent.parent) || !ts.isSourceFile(factory.parent.parent.parent.parent) ||
|
|
209
|
+
lowerer.preparingClassFactories.has(factory))
|
|
210
|
+
continue;
|
|
211
|
+
if (!lowerer.preparedClassFactories.has(factory)) {
|
|
212
|
+
lowerer.preparingClassFactories.add(factory);
|
|
213
|
+
const collecting = lowerer.collecting, sink = lowerer.diagSink;
|
|
214
|
+
lowerer.collecting = false;
|
|
215
|
+
lowerer.diagSink = null;
|
|
216
|
+
try {
|
|
217
|
+
lowerer.preparedClassFactories.set(factory, lowerer.lowerLambda(factory));
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
lowerer.collecting = collecting;
|
|
221
|
+
lowerer.diagSink = sink;
|
|
222
|
+
lowerer.preparingClassFactories.delete(factory);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const resolved = lowerer.mapTypeOf(type);
|
|
226
|
+
if (resolved?.kind === "classval")
|
|
227
|
+
return resolved;
|
|
228
|
+
}
|
|
229
|
+
return mapped;
|
|
78
230
|
}
|
|
79
231
|
/** The KEY symbol behind a LATE-BOUND (`__@name@id`) property: resolved
|
|
80
232
|
* from the argument of the element-access assignment that declared it
|
|
@@ -186,6 +338,10 @@ export function registerBuiltinErrorClasses(lowerer) {
|
|
|
186
338
|
{ name: "%code", type: STRING },
|
|
187
339
|
{ name: "%cause", type: DYN },
|
|
188
340
|
{ name: "%causeEnumerable", type: BOOL },
|
|
341
|
+
{ name: "%messagePresent", type: BOOL },
|
|
342
|
+
{ name: "%messageEnumerable", type: BOOL },
|
|
343
|
+
{ name: "%namePresent", type: BOOL },
|
|
344
|
+
{ name: "%nameEnumerable", type: BOOL },
|
|
189
345
|
{ name: "%stackFrames", type: STRING },
|
|
190
346
|
{ name: "%stack", type: STRING },
|
|
191
347
|
],
|
|
@@ -337,12 +493,102 @@ export function builtinStreamInfoOf(lowerer, symbol) {
|
|
|
337
493
|
/** A dictionary lookup may be absent even when its checker type is a
|
|
338
494
|
* required reference. Keep literal nullish fallbacks in inferred JS storage. */
|
|
339
495
|
function jsFieldInitializerType(lowerer, type, expression) {
|
|
496
|
+
if (ts.isIdentifier(expression)) {
|
|
497
|
+
const symbol = lowerer.checker.getSymbolAtLocation(expression);
|
|
498
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && (ts.isConstructorDeclaration(decl.parent) ||
|
|
499
|
+
(lowerer.typeOf(expression).flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)))
|
|
500
|
+
return DYN;
|
|
501
|
+
}
|
|
502
|
+
if (type.kind === "array" || type.kind === "record")
|
|
503
|
+
return DYN;
|
|
504
|
+
// JavaScript documentation cannot guarantee that a call or an indexed
|
|
505
|
+
// read returns a present number. Numeric fields receiving those values
|
|
506
|
+
// must retain undefined, so later arithmetic performs JS coercion (NaN).
|
|
507
|
+
if (type.kind === "f64" && isJsSourceFile(expression.getSourceFile())) {
|
|
508
|
+
const assignment = expression.parent;
|
|
509
|
+
const left = ts.isBinaryExpression(assignment) ? assignment.left : null;
|
|
510
|
+
const field = left && ts.isPropertyAccessExpression(left) && left.expression.kind === ts.SyntaxKind.ThisKeyword ? left.name.text : null;
|
|
511
|
+
let owner = expression.parent;
|
|
512
|
+
while (owner && !ts.isClassDeclaration(owner) && !ts.isClassExpression(owner))
|
|
513
|
+
owner = owner.parent;
|
|
514
|
+
if (field && owner) {
|
|
515
|
+
let acceptsUnchecked = false;
|
|
516
|
+
const scan = (node) => {
|
|
517
|
+
if (acceptsUnchecked || node !== owner && (ts.isClassDeclaration(node) || ts.isClassExpression(node)))
|
|
518
|
+
return;
|
|
519
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
520
|
+
ts.isPropertyAccessExpression(node.left) && node.left.expression.kind === ts.SyntaxKind.ThisKeyword && node.left.name.text === field) {
|
|
521
|
+
let rhs = node.right;
|
|
522
|
+
while (ts.isParenthesizedExpression(rhs))
|
|
523
|
+
rhs = rhs.expression;
|
|
524
|
+
if (ts.isCallExpression(rhs) || ts.isElementAccessExpression(rhs))
|
|
525
|
+
acceptsUnchecked = true;
|
|
526
|
+
}
|
|
527
|
+
node.forEachChild(scan);
|
|
528
|
+
};
|
|
529
|
+
scan(owner);
|
|
530
|
+
if (acceptsUnchecked)
|
|
531
|
+
return DYN;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
340
534
|
let node = expression;
|
|
341
535
|
while (ts.isParenthesizedExpression(node))
|
|
342
536
|
node = node.expression;
|
|
537
|
+
if (ts.isCallExpression(node) && type.kind === "bytes") {
|
|
538
|
+
let prototypeArgument = false;
|
|
539
|
+
for (const argument of node.arguments)
|
|
540
|
+
ts.walkPreorder(argument, (part) => {
|
|
541
|
+
if (!ts.isPropertyAccessExpression(part) || part.expression.kind !== ts.SyntaxKind.ThisKeyword)
|
|
542
|
+
return;
|
|
543
|
+
const symbol = lowerer.checker.getSymbolAtLocation(part.name);
|
|
544
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
|
|
545
|
+
const member = ts.isBinaryExpression(decl) ? decl.left : decl;
|
|
546
|
+
return ts.isPropertyAccessExpression(member) && ts.isPropertyAccessExpression(member.expression) &&
|
|
547
|
+
member.expression.name.text === "prototype";
|
|
548
|
+
}))
|
|
549
|
+
prototypeArgument = true;
|
|
550
|
+
// JS checkers can synthesize a property declaration for prototype
|
|
551
|
+
// assignments. Keep the source ownership proof when that happens.
|
|
552
|
+
let owner = node.parent;
|
|
553
|
+
while (owner && !(ts.isClassDeclaration(owner) || ts.isClassExpression(owner)))
|
|
554
|
+
owner = owner.parent;
|
|
555
|
+
if (owner && (ts.isClassDeclaration(owner) || ts.isClassExpression(owner)) && owner.name) {
|
|
556
|
+
const className = owner.name.text;
|
|
557
|
+
for (const statement of owner.getSourceFile().statements) {
|
|
558
|
+
if (!ts.isExpressionStatement(statement) || !ts.isBinaryExpression(statement.expression))
|
|
559
|
+
continue;
|
|
560
|
+
const left = statement.expression.left;
|
|
561
|
+
if (statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && ts.isPropertyAccessExpression(left) &&
|
|
562
|
+
left.name.text === part.name.text && ts.isPropertyAccessExpression(left.expression) && left.expression.name.text === "prototype" &&
|
|
563
|
+
ts.isIdentifier(left.expression.expression) && left.expression.expression.text === className)
|
|
564
|
+
prototypeArgument = true;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
// A prototype-selected conversion can return different container kinds
|
|
569
|
+
// in subclasses even when JSDoc describes the base class's default.
|
|
570
|
+
if (prototypeArgument)
|
|
571
|
+
return DYN;
|
|
572
|
+
}
|
|
573
|
+
if (ts.isFunctionExpression(node) || ts.isArrowFunction(node)) {
|
|
574
|
+
const ret = lowerer.declaredReturnType(node, node);
|
|
575
|
+
const arms = type.kind === "union" ? lowerer.unions.get(type.unionId)?.arms ?? [] : [type];
|
|
576
|
+
// Inferred arithmetic may return a checked Number or BigInt. Preserve
|
|
577
|
+
// the callable when its native return ABI differs from the checker slot.
|
|
578
|
+
if (arms.some((arm) => arm.kind === "func" && !typeEquals(arm.ret, ret)))
|
|
579
|
+
return DYN;
|
|
580
|
+
}
|
|
343
581
|
if (node.kind === ts.SyntaxKind.NullKeyword)
|
|
344
582
|
return withUnitArm(type, "nullT", lowerer.unions) ?? type;
|
|
345
583
|
const inferred = lowerer.typeOf(node);
|
|
584
|
+
const scalar = (inferred.flags & ts.TypeFlags.BooleanLike) !== 0 ? "bool"
|
|
585
|
+
: (inferred.flags & ts.TypeFlags.NumberLike) !== 0 ? "f64"
|
|
586
|
+
: (inferred.flags & ts.TypeFlags.StringLike) !== 0 ? "string"
|
|
587
|
+
: (inferred.flags & ts.TypeFlags.BigIntLike) !== 0 ? "bigint"
|
|
588
|
+
: (inferred.flags & ts.TypeFlags.ESSymbolLike) !== 0 ? "symbol" : null;
|
|
589
|
+
if (scalar && type.kind !== "dyn" && type.kind !== scalar &&
|
|
590
|
+
!(type.kind === "union" && lowerer.unions.get(type.unionId)?.arms.some((arm) => arm.kind === scalar)))
|
|
591
|
+
return DYN;
|
|
346
592
|
for (const arm of inferred.isUnionType() ? ts.constituentTypes(inferred) : [inferred]) {
|
|
347
593
|
if ((arm.flags & ts.TypeFlags.Undefined) !== 0)
|
|
348
594
|
type = lowerer.withUndefinedArmOf(type) ?? type;
|
|
@@ -362,6 +608,56 @@ function jsFieldInitializerType(lowerer, type, expression) {
|
|
|
362
608
|
}
|
|
363
609
|
return type;
|
|
364
610
|
}
|
|
611
|
+
function inheritedJsFieldType(lowerer, decl, name, type) {
|
|
612
|
+
for (const expression of lowerer.inheritedJsFieldWrites.get(decl)?.get(name) ?? []) {
|
|
613
|
+
// Keep one shared layout, including sibling subclasses. A descendant
|
|
614
|
+
// initializer that exceeds the base slot needs checked JS storage.
|
|
615
|
+
if (!typeEquals(type, jsFieldInitializerType(lowerer, type, expression)))
|
|
616
|
+
return DYN;
|
|
617
|
+
}
|
|
618
|
+
return type;
|
|
619
|
+
}
|
|
620
|
+
function returnsWidenedJsField(lowerer, decl, member, fields, ret) {
|
|
621
|
+
if (!isJsSourceFile(member.getSourceFile()) || member.type || member.asteriskToken || hasExplicitJsDocReturn(member))
|
|
622
|
+
return false;
|
|
623
|
+
const widened = (expression) => {
|
|
624
|
+
if (ts.isParenthesizedExpression(expression))
|
|
625
|
+
return widened(expression.expression);
|
|
626
|
+
if (ts.isConditionalExpression(expression))
|
|
627
|
+
return widened(expression.whenTrue) || widened(expression.whenFalse);
|
|
628
|
+
if (ts.isCallExpression(expression)) {
|
|
629
|
+
const callee = expression.expression;
|
|
630
|
+
const symbol = ts.isPropertyAccessExpression(callee) ? lowerer.checker.getSymbolAtLocation(callee.name)
|
|
631
|
+
: ts.isElementAccessExpression(callee) ? lowerer.checker.getPropertyOfType(lowerer.typeOf(callee.expression), lowerer.foldedStringKeyOf(callee.argumentExpression) ?? "")
|
|
632
|
+
: undefined;
|
|
633
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((declaration) => ts.isMethodDeclaration(declaration) && lowerer.checkedVirtualJsMethods.has(declaration)))
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
const name = ts.isPropertyAccessExpression(expression) ? expression.name.text
|
|
637
|
+
: ts.isElementAccessExpression(expression) ? lowerer.foldedStringKeyOf(expression.argumentExpression) : null;
|
|
638
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
639
|
+
const symbol = lowerer.checker.getSymbolAtLocation(expression.name);
|
|
640
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((declaration) => (ts.isGetAccessorDeclaration(declaration) || ts.isSetAccessorDeclaration(declaration)) && lowerer.checkedVirtualJsAccessors.has(declaration)))
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
if (name !== null && (ts.isPropertyAccessExpression(expression) || ts.isElementAccessExpression(expression)) &&
|
|
644
|
+
expression.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
645
|
+
return fields.get(name)?.kind === "dyn" || inheritedJsFieldType(lowerer, decl, name, ret).kind === "dyn";
|
|
646
|
+
}
|
|
647
|
+
return false;
|
|
648
|
+
};
|
|
649
|
+
let result = false;
|
|
650
|
+
const scan = (node) => {
|
|
651
|
+
if (result || ts.isFunctionLike(node))
|
|
652
|
+
return;
|
|
653
|
+
if (ts.isReturnStatement(node) && node.expression && widened(node.expression))
|
|
654
|
+
result = true;
|
|
655
|
+
node.forEachChild(scan);
|
|
656
|
+
};
|
|
657
|
+
if (member.body)
|
|
658
|
+
scan(member.body);
|
|
659
|
+
return result;
|
|
660
|
+
}
|
|
365
661
|
/** The undefined-armed union of a JS class property's inferred type — the
|
|
366
662
|
* honest slot for a field first assigned outside the constructor's top
|
|
367
663
|
* level (undefined until the write runs, Node-exact). Null when the
|
|
@@ -803,12 +1099,17 @@ mixin) {
|
|
|
803
1099
|
// so the base's ClassInfo already exists here. An INSTANTIATION's
|
|
804
1100
|
// base is its family (whose base is the declared one) — the heritage
|
|
805
1101
|
// clause resolved when the family collected.
|
|
806
|
-
|
|
1102
|
+
const computedBase = lowerer.computedClassBases.get(lowerer.classNamer(decl));
|
|
1103
|
+
let base = inst ? inst.family : mixin ? mixin.base : computedBase?.classInfo ?? null;
|
|
807
1104
|
let callableBase;
|
|
1105
|
+
let factoryBaseExpression;
|
|
808
1106
|
const adoptCallableBase = (expression) => {
|
|
809
|
-
if (
|
|
810
|
-
!ts.isClassDeclaration(decl) || familyMode || inst || mixin)
|
|
1107
|
+
if (familyMode || inst || mixin)
|
|
811
1108
|
return false;
|
|
1109
|
+
if (lowerer.computedCallableBases.has(decl)) {
|
|
1110
|
+
callableBase = { expression, constructorId: `%g.${className}.base`, prototypeId: `%g.${className}.basePrototype` };
|
|
1111
|
+
return true;
|
|
1112
|
+
}
|
|
812
1113
|
const signatures = lowerer.checker.getCallSignatures(lowerer.typeOf(expression));
|
|
813
1114
|
const source = signatures.length === 1 ? lowerer.checker.signatureDeclaration(signatures[0]) : undefined;
|
|
814
1115
|
if (!source || !isJsSourceFile(source.getSourceFile()) ||
|
|
@@ -847,7 +1148,7 @@ mixin) {
|
|
|
847
1148
|
}
|
|
848
1149
|
}
|
|
849
1150
|
}
|
|
850
|
-
for (const clause of inst || mixin ? [] : (decl.heritageClauses ?? [])) {
|
|
1151
|
+
for (const clause of inst || mixin || computedBase ? [] : (decl.heritageClauses ?? [])) {
|
|
851
1152
|
// `implements` is pure type-world: tsc checked the conformance and
|
|
852
1153
|
// the clause erases — nothing about the runtime class changes.
|
|
853
1154
|
// (Assigning an instance INTO an interface-typed slot is a separate
|
|
@@ -884,8 +1185,6 @@ mixin) {
|
|
|
884
1185
|
// (the class statement evaluates at its init position; a base
|
|
885
1186
|
// block below it would still be uninitialized in Node).
|
|
886
1187
|
if (!t.expression.questionDotToken && nsMemberIdentOf(lowerer, t.expression)) {
|
|
887
|
-
if (t.typeArguments)
|
|
888
|
-
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
889
1188
|
if (memberSym)
|
|
890
1189
|
fenceEarlyNsMemberRef(lowerer, t.expression, memberSym);
|
|
891
1190
|
const stored = storedClassValueType(lowerer, t.expression);
|
|
@@ -896,6 +1195,8 @@ mixin) {
|
|
|
896
1195
|
continue;
|
|
897
1196
|
lowerer.unsupported("SC1090", t, `extending the namespace member '${t.expression.name.text}' (no class lowering)`);
|
|
898
1197
|
}
|
|
1198
|
+
if (t.typeArguments)
|
|
1199
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
899
1200
|
base = nsBase;
|
|
900
1201
|
continue;
|
|
901
1202
|
}
|
|
@@ -960,8 +1261,17 @@ mixin) {
|
|
|
960
1261
|
base = mixinBase;
|
|
961
1262
|
continue;
|
|
962
1263
|
}
|
|
1264
|
+
const stored = storedClassValueType(lowerer, t.expression);
|
|
1265
|
+
const factoryBase = stored?.kind === "classval" ? lowerer.classes.get(stored.className) : undefined;
|
|
1266
|
+
if (factoryBase) {
|
|
1267
|
+
base = factoryBase;
|
|
1268
|
+
factoryBaseExpression = t.expression;
|
|
1269
|
+
continue;
|
|
1270
|
+
}
|
|
963
1271
|
}
|
|
964
1272
|
if (!t || !ts.isIdentifier(t.expression)) {
|
|
1273
|
+
if (t && adoptCallableBase(t.expression))
|
|
1274
|
+
continue;
|
|
965
1275
|
lowerer.unsupported("SC1090", clause, "extending computed expressions");
|
|
966
1276
|
}
|
|
967
1277
|
const symbol = lowerer.resolveValueSymbol(t.expression);
|
|
@@ -1027,8 +1337,13 @@ mixin) {
|
|
|
1027
1337
|
lowerer.unsupported("SC1090", t, `extending classes not declared in the program ('${t.expression.text}')`);
|
|
1028
1338
|
}
|
|
1029
1339
|
}
|
|
1030
|
-
if (base?.localClass && !functionLocalClass(decl))
|
|
1031
|
-
|
|
1340
|
+
if (base?.localClass && !functionLocalClass(decl) && !computedBase && !factoryBaseExpression) {
|
|
1341
|
+
const heritage = decl.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)?.types[0];
|
|
1342
|
+
if (heritage)
|
|
1343
|
+
factoryBaseExpression = heritage.expression;
|
|
1344
|
+
else
|
|
1345
|
+
lowerer.unsupported("SC1090", decl, "extending a function-local class outside a function");
|
|
1346
|
+
}
|
|
1032
1347
|
if (lowerer.collectingExprClasses.has(decl)) {
|
|
1033
1348
|
for (let parent = base; parent; parent = parent.base) {
|
|
1034
1349
|
if (parent.builtinEmitter || parent.builtinStream !== undefined) {
|
|
@@ -1078,6 +1393,14 @@ mixin) {
|
|
|
1078
1393
|
const staticFields = [];
|
|
1079
1394
|
const staticMethods = new Map();
|
|
1080
1395
|
const staticBlocks = [];
|
|
1396
|
+
const runtimeStatics = [];
|
|
1397
|
+
const runtimePrototypeMembers = [];
|
|
1398
|
+
// Public JS accessors need live constructor descriptors, including
|
|
1399
|
+
// receiver-sensitive getters and setters inherited by subclasses.
|
|
1400
|
+
const checkedStatics = isJsSourceFile(decl.getSourceFile()) && (base && hasRuntimeStatics(base) || decl.members.some((member) => ts.isAccessor(member) && !ts.isPrivateIdentifier(member.name) &&
|
|
1401
|
+
member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))) &&
|
|
1402
|
+
!decl.members.some((member) => member.name && ts.isPrivateIdentifier(member.name) &&
|
|
1403
|
+
ts.getModifiers(member)?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword));
|
|
1081
1404
|
// GENERIC methods (own type parameters), instance and static: only
|
|
1082
1405
|
// the SYNTAX is checked here — parameter/return types mention the
|
|
1083
1406
|
// type parameters and cannot map yet; bodies lower per call-site
|
|
@@ -1126,6 +1449,18 @@ mixin) {
|
|
|
1126
1449
|
});
|
|
1127
1450
|
};
|
|
1128
1451
|
for (const member of decl.members) {
|
|
1452
|
+
if ((functionLocalClass(decl) || checkedStatics) && isJsSourceFile(decl.getSourceFile()) &&
|
|
1453
|
+
(ts.isClassStaticBlockDeclaration(member) || ts.getModifiers(member)?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))) {
|
|
1454
|
+
runtimeStatics.push(member);
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
if (functionLocalClass(decl) && isJsSourceFile(decl.getSourceFile()) &&
|
|
1458
|
+
(ts.isMethodDeclaration(member) || ts.isAccessor(member)) && member.body &&
|
|
1459
|
+
!ts.isPrivateIdentifier(member.name)) {
|
|
1460
|
+
runtimePrototypeMembers.push(member);
|
|
1461
|
+
if (ts.isComputedPropertyName(member.name) && classMemberNameOf(lowerer, member.name) === null)
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1129
1464
|
if (ts.isClassStaticBlockDeclaration(member)) {
|
|
1130
1465
|
// Statics live on the FAMILY (JS has one class, one static
|
|
1131
1466
|
// storage, however many instantiations exist) — instantiations
|
|
@@ -1172,10 +1507,15 @@ mixin) {
|
|
|
1172
1507
|
(ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) &&
|
|
1173
1508
|
member.initializer &&
|
|
1174
1509
|
member.postfixToken?.kind !== ts.SyntaxKind.QuestionToken) {
|
|
1510
|
+
const initializerSymbol = ts.isIdentifier(member.initializer) ? lowerer.resolveValueSymbol(member.initializer) : null;
|
|
1511
|
+
// Enum namespace aliases are erased; member reads fold through the checker.
|
|
1512
|
+
if (initializerSymbol && (initializerSymbol.flags & ts.SymbolFlags.Enum) !== 0)
|
|
1513
|
+
continue;
|
|
1175
1514
|
const type = lowerer.irTypeOf(member.name);
|
|
1176
1515
|
if (type.kind === "void")
|
|
1177
1516
|
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
1178
|
-
if (type.kind === "dyn" && !isJsSourceFile(member.getSourceFile())
|
|
1517
|
+
if (type.kind === "dyn" && !isJsSourceFile(member.getSourceFile()) &&
|
|
1518
|
+
(member.type !== undefined || (lowerer.typeOf(member.name).flags & ts.TypeFlags.Any) === 0)) {
|
|
1179
1519
|
lowerer.unsupported("SC1090", member.name, "'unknown'-typed static fields");
|
|
1180
1520
|
}
|
|
1181
1521
|
staticFields.push({
|
|
@@ -1237,7 +1577,14 @@ mixin) {
|
|
|
1237
1577
|
// (every named member kind stores a PropertyName there).
|
|
1238
1578
|
const memberName = member.name;
|
|
1239
1579
|
if (errorRooted && memberName && classMemberNameOf(lowerer, memberName) === "cause") {
|
|
1240
|
-
|
|
1580
|
+
if (ts.isPropertyDeclaration(member) && !member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
1581
|
+
if (!isJsSourceFile(member.getSourceFile()))
|
|
1582
|
+
lowerer.unsupported("SC1090", memberName, "redeclaring Error.cause (pass the cause in the Error constructor options)");
|
|
1583
|
+
fieldOrder.push({ name: "cause", type: DYN, initializer: member.initializer,
|
|
1584
|
+
...(member.initializer ? {} : { undefinedInitializer: locOf(member) }), redeclared: true, errorCause: true });
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
lowerer.unsupported("SC1090", memberName, "Error.cause accessors");
|
|
1241
1588
|
}
|
|
1242
1589
|
// #PRIVATE members compile: their names ('#m') are unspellable by
|
|
1243
1590
|
// any public identifier, so they ride the ordinary fields/methods
|
|
@@ -1351,33 +1698,45 @@ mixin) {
|
|
|
1351
1698
|
// get/set pair — declare the field and accessors explicitly.
|
|
1352
1699
|
lowerer.unsupported("SC1090", member, "auto-accessor fields ('accessor x')");
|
|
1353
1700
|
}
|
|
1354
|
-
if (ts.isComputedPropertyName(member.name)) {
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1701
|
+
if (ts.isComputedPropertyName(member.name) || ts.isStringLiteralLike(member.name) || ts.isNumericLiteral(member.name)) {
|
|
1702
|
+
const symbolKey = ts.isComputedPropertyName(member.name) ? classSymbolKeyOf(lowerer, member.name.expression) : null;
|
|
1703
|
+
const stringKey = ts.isComputedPropertyName(member.name) ? lowerer.foldedStringKeyOf(member.name.expression)
|
|
1704
|
+
: ts.isNumericLiteral(member.name) ? String(Number(member.name.text)) : member.name.text;
|
|
1705
|
+
const fieldName = symbolKey?.fieldName ?? stringKey;
|
|
1706
|
+
if (fieldName === null || (!member.initializer && !isJsSourceFile(member.getSourceFile()))) {
|
|
1707
|
+
lowerer.unsupported("SC1090", member, "computed class fields without an initializer and a stable string, number, or module-level literal Symbol()/Symbol.for() key");
|
|
1358
1708
|
}
|
|
1359
1709
|
const declared = lowerer.typeOf(member);
|
|
1360
1710
|
const inferred = member.type || !member.initializer ? declared : lowerer.checker.getBaseTypeOfLiteralType(lowerer.typeOf(member.initializer));
|
|
1361
1711
|
const mapped = member.initializer ? lowerer.mapTypeOf(inferred) ?? dynFallbackType(lowerer, member, inferred) : DYN;
|
|
1362
|
-
|
|
1712
|
+
let type = mapped?.kind === "record" && isJsSourceFile(member.getSourceFile()) && !member.type
|
|
1713
|
+
? DYN : mapped ? jsOpenObjectType(member, mapped, lowerer.shapes, lowerer.unions) : null;
|
|
1363
1714
|
if (!type || type.kind === "void" || (type.kind === "dyn" && !isJsSourceFile(member.getSourceFile())))
|
|
1364
1715
|
lowerer.badType(member, inferred);
|
|
1716
|
+
if (isJsSourceFile(member.getSourceFile())) {
|
|
1717
|
+
type = inheritedJsFieldType(lowerer, decl, fieldName, type);
|
|
1718
|
+
if (base?.fields.get(fieldName)?.kind === "dyn")
|
|
1719
|
+
type = DYN;
|
|
1720
|
+
if (base && !fields.has(fieldName) && lowerer.dynConvertible(type))
|
|
1721
|
+
type = DYN;
|
|
1722
|
+
}
|
|
1365
1723
|
const undefinedInitializer = !member.initializer ? locOf(member) : undefined;
|
|
1366
|
-
const previous = fields.get(
|
|
1724
|
+
const previous = fields.get(fieldName);
|
|
1367
1725
|
if (previous) {
|
|
1368
|
-
if (!symbolFields.has(
|
|
1369
|
-
lowerer.unsupported("SC1090", member.name, `distinct keys sharing the printable name '${symbolFieldDisplayName(
|
|
1726
|
+
if (symbolKey && !symbolFields.has(symbolKey.identity)) {
|
|
1727
|
+
lowerer.unsupported("SC1090", member.name, `distinct keys sharing the printable name '${symbolFieldDisplayName(fieldName)}' in one class`);
|
|
1370
1728
|
}
|
|
1371
1729
|
if (!typeEquals(previous, type)) {
|
|
1372
|
-
lowerer.unsupported("SC1090", member.name, "redeclaring
|
|
1730
|
+
lowerer.unsupported("SC1090", member.name, "redeclaring computed fields at a different type");
|
|
1373
1731
|
}
|
|
1374
|
-
fieldOrder.push({ name:
|
|
1732
|
+
fieldOrder.push({ name: fieldName, type, initializer: member.initializer, ...(undefinedInitializer ? { undefinedInitializer } : {}), redeclared: true });
|
|
1375
1733
|
}
|
|
1376
1734
|
else {
|
|
1377
|
-
fields.set(
|
|
1378
|
-
fieldOrder.push({ name:
|
|
1735
|
+
fields.set(fieldName, type);
|
|
1736
|
+
fieldOrder.push({ name: fieldName, type, initializer: member.initializer, ...(undefinedInitializer ? { undefinedInitializer } : {}) });
|
|
1379
1737
|
}
|
|
1380
|
-
|
|
1738
|
+
if (symbolKey)
|
|
1739
|
+
symbolFields.set(symbolKey.identity, fieldName);
|
|
1381
1740
|
continue;
|
|
1382
1741
|
}
|
|
1383
1742
|
// #private fields ride the ordinary field machinery — the '#'
|
|
@@ -1426,11 +1785,36 @@ mixin) {
|
|
|
1426
1785
|
// as undefined on construction, verified), and reads/writes ride
|
|
1427
1786
|
// the ordinary undefined-armed union machinery.
|
|
1428
1787
|
let type = lowerer.irTypeOf(member.name);
|
|
1788
|
+
if (isJsSourceFile(member.getSourceFile()) && !member.initializer) {
|
|
1789
|
+
const constructor = decl.members.find((part) => ts.isConstructorDeclaration(part) && !!part.body);
|
|
1790
|
+
for (const statement of constructor?.body?.statements ?? []) {
|
|
1791
|
+
if (!ts.isExpressionStatement(statement) || !ts.isBinaryExpression(statement.expression) ||
|
|
1792
|
+
statement.expression.operatorToken.kind !== ts.SyntaxKind.EqualsToken)
|
|
1793
|
+
continue;
|
|
1794
|
+
const left = statement.expression.left;
|
|
1795
|
+
if (!ts.isPropertyAccessExpression(left) || left.expression.kind !== ts.SyntaxKind.ThisKeyword || left.name.text !== member.name.text)
|
|
1796
|
+
continue;
|
|
1797
|
+
type = statement.expression.right.kind === ts.SyntaxKind.NullKeyword ? DYN : jsFieldInitializerType(lowerer, type, statement.expression.right);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
// Unannotated JavaScript callback slots retain the original
|
|
1801
|
+
// callable and its receiver instead of adapting its signature.
|
|
1802
|
+
if (isJsSourceFile(member.getSourceFile()) && !member.type &&
|
|
1803
|
+
!/@type\b/.test(member.getSourceFile().text.slice(member.pos, member.getStart())) &&
|
|
1804
|
+
(type.kind === "func" || type.kind === "record") && lowerer.dynConvertible(type))
|
|
1805
|
+
type = DYN;
|
|
1429
1806
|
// JS permits reads before a constructor's first assignment even
|
|
1430
1807
|
// when its inferred field type omits undefined.
|
|
1431
1808
|
if (isJsSourceFile(member.getSourceFile()) && !member.initializer && type.kind !== "dyn") {
|
|
1432
1809
|
type = lowerer.withUndefinedArmOf(type) ?? DYN;
|
|
1433
1810
|
}
|
|
1811
|
+
if (isJsSourceFile(member.getSourceFile())) {
|
|
1812
|
+
type = inheritedJsFieldType(lowerer, decl, member.name.text, type);
|
|
1813
|
+
if (base?.fields.get(member.name.text)?.kind === "dyn")
|
|
1814
|
+
type = DYN;
|
|
1815
|
+
if (base && !fields.has(member.name.text) && lowerer.dynConvertible(type))
|
|
1816
|
+
type = DYN;
|
|
1817
|
+
}
|
|
1434
1818
|
if (type.kind === "void")
|
|
1435
1819
|
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
1436
1820
|
// Bundled JS declares fields without annotations before assigning
|
|
@@ -1650,7 +2034,7 @@ mixin) {
|
|
|
1650
2034
|
// a generic-class instantiation's member.
|
|
1651
2035
|
if (implicitMonoFile(decl.getSourceFile()) && !functionLocalClass(decl) &&
|
|
1652
2036
|
ts.isIdentifier(member.name) &&
|
|
1653
|
-
!lowerer.virtualJsMethods.has(member) &&
|
|
2037
|
+
!lowerer.virtualJsMethods.has(member) && !lowerer.prototypeMethodAccesses.has(member.name.text) &&
|
|
1654
2038
|
inst === undefined && decl.typeParameters === undefined &&
|
|
1655
2039
|
!fields.has(member.name.text) &&
|
|
1656
2040
|
!lowerer.findMethodOn(base, member.name.text) &&
|
|
@@ -1669,6 +2053,36 @@ mixin) {
|
|
|
1669
2053
|
}
|
|
1670
2054
|
}
|
|
1671
2055
|
const { shapes, funcType: ft } = lowerer.lambdaSignature(member);
|
|
2056
|
+
const fieldRet = ft.ret.kind === "promise" ? ft.ret.inner : ft.ret;
|
|
2057
|
+
if (returnsWidenedJsField(lowerer, decl, member, fields, fieldRet)) {
|
|
2058
|
+
ft.ret = ft.ret.kind === "promise" ? { ...ft.ret, inner: DYN } : DYN;
|
|
2059
|
+
}
|
|
2060
|
+
if (lowerer.checkedVirtualJsMethods.has(member)) {
|
|
2061
|
+
for (const shape of shapes) {
|
|
2062
|
+
shape.type = DYN;
|
|
2063
|
+
if (shape.bodyType)
|
|
2064
|
+
shape.bodyType = DYN;
|
|
2065
|
+
}
|
|
2066
|
+
ft.params = shapes.map((shape) => shape.type);
|
|
2067
|
+
if (ft.ret.kind !== "void" || lowerer.checkedVirtualJsValueReturns.has(member))
|
|
2068
|
+
ft.ret = DYN;
|
|
2069
|
+
}
|
|
2070
|
+
// JS inference may retain only a symbol sentinel while a method
|
|
2071
|
+
// also returns values from an untyped mutable field or local.
|
|
2072
|
+
if (isJsSourceFile(member.getSourceFile()) && !member.type && !hasExplicitJsDocReturn(member) && ft.ret.kind === "symbol")
|
|
2073
|
+
ft.ret = DYN;
|
|
2074
|
+
if (isJsSourceFile(member.getSourceFile()) && lowerer.prototypeMethodAccesses.has(mName) &&
|
|
2075
|
+
!lowerer.findMethodOn(base, mName)?.declarer.def.runtime &&
|
|
2076
|
+
shapes.every((p) => p.mode === "required" || p.mode === "omittable")) {
|
|
2077
|
+
for (const shape of shapes) {
|
|
2078
|
+
shape.type = DYN;
|
|
2079
|
+
if (shape.bodyType)
|
|
2080
|
+
shape.bodyType = DYN;
|
|
2081
|
+
}
|
|
2082
|
+
ft.params = shapes.map((shape) => shape.type);
|
|
2083
|
+
if (ft.ret.kind !== "void")
|
|
2084
|
+
ft.ret = DYN;
|
|
2085
|
+
}
|
|
1672
2086
|
// JS subclasses can add parameters to a method. Reserve checked
|
|
1673
2087
|
// argument slots in the base before constructing its vtable; shorter
|
|
1674
2088
|
// bodies ignore them and omitted call arguments become undefined.
|
|
@@ -1704,6 +2118,12 @@ mixin) {
|
|
|
1704
2118
|
// `m(x: number | undefined)` interchange soundly in overrides.
|
|
1705
2119
|
const overridden = lowerer.findMethodOn(base, mName);
|
|
1706
2120
|
if (overridden && isJsSourceFile(member.getSourceFile())) {
|
|
2121
|
+
// An unannotated JS override can return a checked value into
|
|
2122
|
+
// the inherited native ABI, just as its untyped parameters do.
|
|
2123
|
+
// Body lowering inserts the conversion at each return.
|
|
2124
|
+
if (!member.type && ft.ret.kind === "dyn" && overridden.sig.ret.kind !== "dyn" &&
|
|
2125
|
+
!overridden.sig.async && !overridden.sig.gen)
|
|
2126
|
+
ft.ret = overridden.sig.ret;
|
|
1707
2127
|
// Fluent overrides return the same native receiver. Keep the
|
|
1708
2128
|
// inherited return ABI and upcast `this` when lowering returns.
|
|
1709
2129
|
if (overridden.sig.ret.kind === "object" && base &&
|
|
@@ -1735,20 +2155,30 @@ mixin) {
|
|
|
1735
2155
|
shapes.push(...overridden.sig.params.slice(shapes.length).map((p) => ({ type: p.type, mode: "omittable" })));
|
|
1736
2156
|
}
|
|
1737
2157
|
if (overridden &&
|
|
2158
|
+
member.asteriskToken === undefined &&
|
|
1738
2159
|
(overridden.sig.params.length !== shapes.length ||
|
|
1739
2160
|
!overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type)) ||
|
|
1740
2161
|
!typeEquals(overridden.sig.ret, ft.ret))) {
|
|
1741
2162
|
const fencedJsOverride = isJsSourceFile(member.getSourceFile()) &&
|
|
1742
2163
|
overridden.declarer.decl !== null && isJsSourceFile(overridden.declarer.decl.getSourceFile()) &&
|
|
1743
|
-
|
|
1744
|
-
overridden.sig.async !== true &&
|
|
1745
|
-
member.asteriskToken === undefined && overridden.sig.gen === undefined;
|
|
2164
|
+
overridden.sig.gen === undefined;
|
|
1746
2165
|
if (fencedJsOverride) {
|
|
1747
2166
|
// A dormant JS method must not poison the whole class. Keep
|
|
1748
2167
|
// the inherited call/dispatch ABI and refuse at method entry;
|
|
1749
2168
|
// lowering this body with the base signature would change JS.
|
|
1750
2169
|
const returnOnly = overridden.sig.params.length === shapes.length &&
|
|
1751
|
-
overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type))
|
|
2170
|
+
overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type)) &&
|
|
2171
|
+
!member.parameters.some((parameter, index) => {
|
|
2172
|
+
if (!parameter.initializer)
|
|
2173
|
+
return false;
|
|
2174
|
+
const baseMember = overridden.declarer.decl?.members.find((candidate) => ts.isMethodDeclaration(candidate) && classMemberNameOf(lowerer, candidate.name) === mName);
|
|
2175
|
+
const inherited = baseMember && ts.isMethodDeclaration(baseMember) ? baseMember.parameters[index]?.initializer : undefined;
|
|
2176
|
+
if (!inherited)
|
|
2177
|
+
return false;
|
|
2178
|
+
const ownType = lowerer.mapTypeOf(lowerer.typeOf(parameter.initializer));
|
|
2179
|
+
const baseType = lowerer.mapTypeOf(lowerer.typeOf(inherited));
|
|
2180
|
+
return ownType !== null && baseType !== null && !typeEquals(ownType, baseType);
|
|
2181
|
+
});
|
|
1752
2182
|
methodEntryFences.set(mName, unsupportedDiag("SC1090", locOf(member.name), returnOnly
|
|
1753
2183
|
? `overriding method '${mName}' with a different return type (the native return type must match the base declaration exactly)`
|
|
1754
2184
|
: `overriding method '${mName}' with a different signature (the native parameter and return types must match the base declaration exactly)`));
|
|
@@ -1765,10 +2195,18 @@ mixin) {
|
|
|
1765
2195
|
// an override chain touching an async method on either end would
|
|
1766
2196
|
// put a spawn wrapper behind a virtual slot, so it fences.
|
|
1767
2197
|
if (overridden && (asyncMember || overridden.sig.async === true)) {
|
|
1768
|
-
|
|
2198
|
+
const message = `overriding ${overridden.sig.async === true ? "the async method" : "a method with an async method"} '${mName}' (async methods dispatch statically — the vtable slot machinery has no fiber-spawn story)`;
|
|
2199
|
+
if (isJsSourceFile(member.getSourceFile()))
|
|
2200
|
+
methodEntryFences.set(mName, unsupportedDiag("SC1090", locOf(member.name), message));
|
|
2201
|
+
else
|
|
2202
|
+
lowerer.unsupported("SC1090", member.name, message);
|
|
1769
2203
|
}
|
|
1770
2204
|
if (overridden && (member.asteriskToken !== undefined || overridden.sig.gen !== undefined)) {
|
|
1771
|
-
|
|
2205
|
+
const message = `overriding the generator method '${mName}' (generator methods dispatch statically — a virtual slot cannot enter their spawn wrapper)`;
|
|
2206
|
+
if (isJsSourceFile(member.getSourceFile()))
|
|
2207
|
+
methodEntryFences.set(mName, unsupportedDiag("SC1090", locOf(member.name), message));
|
|
2208
|
+
else
|
|
2209
|
+
lowerer.unsupported("SC1090", member.name, message);
|
|
1772
2210
|
}
|
|
1773
2211
|
// A GENERATOR method carries its channels on the sig:
|
|
1774
2212
|
// the body lowers as a generator IrFunction (`this` as param 0),
|
|
@@ -1803,9 +2241,19 @@ mixin) {
|
|
|
1803
2241
|
const isGet = ts.isGetAccessor(member);
|
|
1804
2242
|
// #private accessors collect as "get:#x"/"set:#x" — the same
|
|
1805
2243
|
// reserved spelling, one more unspellable segment.
|
|
1806
|
-
|
|
2244
|
+
const prop = classMemberNameOf(lowerer, member.name);
|
|
2245
|
+
if (prop === null) {
|
|
1807
2246
|
lowerer.unsupported("SC1090", member, "computed accessor names");
|
|
1808
2247
|
}
|
|
2248
|
+
if (ts.isComputedPropertyName(member.name)) {
|
|
2249
|
+
const key = classSymbolKeyOf(lowerer, member.name.expression);
|
|
2250
|
+
if (key) {
|
|
2251
|
+
if ([...symbolMethods, ...symbolFields].some(([identity, name]) => name === prop && identity !== key.identity)) {
|
|
2252
|
+
lowerer.unsupported("SC1090", member.name, `distinct symbol keys sharing the printable name '${symbolFieldDisplayName(prop)}' in one class`);
|
|
2253
|
+
}
|
|
2254
|
+
symbolMethods.set(key.identity, prop);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
1809
2257
|
// ABSTRACT accessors are the abstract-method story with property
|
|
1810
2258
|
// syntax: body-less by definition, they enter `methods` (marked
|
|
1811
2259
|
// abstract) as their "get:x"/"set:x" halves — slot declaration
|
|
@@ -1813,9 +2261,15 @@ mixin) {
|
|
|
1813
2261
|
const abstractAccessor = ts.getModifiers(member)?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword) === true;
|
|
1814
2262
|
if (!member.body && !abstractAccessor)
|
|
1815
2263
|
lowerer.unsupported("SC1090", member, "bodyless accessors");
|
|
1816
|
-
const prop = member.name.text;
|
|
1817
2264
|
const mName = `${isGet ? "get" : "set"}:${prop}`;
|
|
1818
|
-
if (fields.has(prop))
|
|
2265
|
+
if (fields.has(prop) && isJsSourceFile(decl.getSourceFile()) && base?.fields.has(prop) &&
|
|
2266
|
+
!fieldOrder.some((field) => field.name === prop)) {
|
|
2267
|
+
// JS may override an inherited data field with a prototype
|
|
2268
|
+
// accessor. Retain the base storage prefix for its constructor;
|
|
2269
|
+
// property dispatch on this subclass selects the accessor.
|
|
2270
|
+
fields.delete(prop);
|
|
2271
|
+
}
|
|
2272
|
+
if (fields.has(prop) && !((prop === "message" || prop === "name") && base && (base.def.name === "%Error" || lowerer.isSubclassOf(base.def.name, "%Error")))) {
|
|
1819
2273
|
// tsc rejects field/accessor mixing (TS2610/2611); defensive.
|
|
1820
2274
|
lowerer.unsupported("SC1090", member.name, "accessors sharing a name with a field");
|
|
1821
2275
|
}
|
|
@@ -1830,6 +2284,13 @@ mixin) {
|
|
|
1830
2284
|
// tsc rejects optional/default/rest setter params (TS1051-53).
|
|
1831
2285
|
sig = { params: lowerer.paramShapes(member.parameters), ret: VOID };
|
|
1832
2286
|
}
|
|
2287
|
+
if (lowerer.checkedVirtualJsAccessors.has(member)) {
|
|
2288
|
+
if (isGet)
|
|
2289
|
+
sig.ret = DYN;
|
|
2290
|
+
else
|
|
2291
|
+
for (const parameter of sig.params)
|
|
2292
|
+
parameter.type = DYN;
|
|
2293
|
+
}
|
|
1833
2294
|
// One property, ONE type: tsc (5.1+) admits get/set pairs with
|
|
1834
2295
|
// unrelated annotated types; a property slot here has a single
|
|
1835
2296
|
// IR type, so the pair must agree exactly.
|
|
@@ -2016,7 +2477,11 @@ mixin) {
|
|
|
2016
2477
|
if (fields.has(name) || hasAccessor(name))
|
|
2017
2478
|
continue;
|
|
2018
2479
|
if (methods.has(name) || lowerer.findMethodOn(base, name)) {
|
|
2019
|
-
|
|
2480
|
+
// An own JS callable shadows its prototype declaration.
|
|
2481
|
+
// Keep the method in the prototype and store the assigned
|
|
2482
|
+
// value in the instance property bag.
|
|
2483
|
+
lowerer.prototypeMethodAccesses.set(name, assign);
|
|
2484
|
+
continue;
|
|
2020
2485
|
}
|
|
2021
2486
|
const sym = lowerer.checker.getSymbolAtLocation(assign);
|
|
2022
2487
|
const t = sym ? lowerer.checker.getTypeOfSymbol(sym) : undefined;
|
|
@@ -2057,6 +2522,11 @@ mixin) {
|
|
|
2057
2522
|
type = widened;
|
|
2058
2523
|
}
|
|
2059
2524
|
}
|
|
2525
|
+
type = inheritedJsFieldType(lowerer, decl, name, type);
|
|
2526
|
+
// super() can invoke a subclass setter or method before its
|
|
2527
|
+
// constructor assignments run. These slots start undefined.
|
|
2528
|
+
if (base && lowerer.dynConvertible(type))
|
|
2529
|
+
type = DYN;
|
|
2060
2530
|
fields.set(name, type);
|
|
2061
2531
|
fieldOrder.push({ name, type, initializer: undefined });
|
|
2062
2532
|
continue;
|
|
@@ -2073,18 +2543,30 @@ mixin) {
|
|
|
2073
2543
|
if (lhs && ts.isElementAccessExpression(lhs) &&
|
|
2074
2544
|
lhs.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
2075
2545
|
const key = classSymbolKeyOf(lowerer, lhs.argumentExpression);
|
|
2076
|
-
|
|
2546
|
+
const stringKey = lowerer.foldedStringKeyOf(lhs.argumentExpression);
|
|
2547
|
+
const fieldName = key?.fieldName ?? stringKey;
|
|
2548
|
+
if (fieldName === null) {
|
|
2549
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(lhs.argumentExpression))?.kind === "symbol") {
|
|
2550
|
+
lowerer.unsupported("SC1090", lhs, "symbol-keyed class fields require a stable module-level key");
|
|
2551
|
+
}
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
if (!key && (fields.has(fieldName) || hasAccessor(fieldName)))
|
|
2077
2555
|
continue;
|
|
2556
|
+
if (!key && (methods.has(fieldName) || lowerer.findMethodOn(base, fieldName))) {
|
|
2557
|
+
lowerer.prototypeMethodAccesses.set(fieldName, lhs);
|
|
2558
|
+
continue;
|
|
2559
|
+
}
|
|
2078
2560
|
// A key already declared (own or inherited) makes later
|
|
2079
2561
|
// assignments writes, not declarations.
|
|
2080
|
-
if (symbolFields.has(key.identity))
|
|
2562
|
+
if (key && symbolFields.has(key.identity))
|
|
2081
2563
|
continue;
|
|
2082
|
-
if (fields.has(
|
|
2564
|
+
if (fields.has(fieldName)) {
|
|
2083
2565
|
// Two DISTINCT Symbol(...) consts with one description in
|
|
2084
2566
|
// one layout would need one printable name for two slots.
|
|
2085
|
-
lowerer.unsupported("SC1090", lhs, `distinct symbol keys sharing the printable name '${symbolFieldDisplayName(
|
|
2567
|
+
lowerer.unsupported("SC1090", lhs, `distinct symbol keys sharing the printable name '${symbolFieldDisplayName(fieldName)}' in one class`);
|
|
2086
2568
|
}
|
|
2087
|
-
const propSym = lateBoundByKey.get(key.sym);
|
|
2569
|
+
const propSym = key ? lateBoundByKey.get(key.sym) : instType ? lowerer.checker.getPropertyOfType(instType, fieldName) : undefined;
|
|
2088
2570
|
// tsgo does not synthesize the late-bound `__@name@id`
|
|
2089
2571
|
// property for a JS `this[k] = v` declaration (the finding-5
|
|
2090
2572
|
// family: no expando/late-bound synthesis in its stricter
|
|
@@ -2104,9 +2586,13 @@ mixin) {
|
|
|
2104
2586
|
type = jsOpenObjectType(lhs, type, lowerer.shapes, lowerer.unions);
|
|
2105
2587
|
if (!type || type.kind === "void")
|
|
2106
2588
|
lowerer.badType(lhs, t ?? lowerer.typeOf(lhs));
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2589
|
+
type = inheritedJsFieldType(lowerer, decl, fieldName, type);
|
|
2590
|
+
if (base && lowerer.dynConvertible(type))
|
|
2591
|
+
type = DYN;
|
|
2592
|
+
fields.set(fieldName, type);
|
|
2593
|
+
if (key)
|
|
2594
|
+
symbolFields.set(key.identity, fieldName);
|
|
2595
|
+
fieldOrder.push({ name: fieldName, type, initializer: undefined });
|
|
2110
2596
|
}
|
|
2111
2597
|
}
|
|
2112
2598
|
}
|
|
@@ -2152,7 +2638,8 @@ mixin) {
|
|
|
2152
2638
|
// TypeScript classes keep the loud fence too: an annotated
|
|
2153
2639
|
// program can spell `T | undefined` itself.
|
|
2154
2640
|
if (isJsSourceFile(decl.getSourceFile())) {
|
|
2155
|
-
const
|
|
2641
|
+
const original = undefArmedFieldType(lowerer, p);
|
|
2642
|
+
const armed = original === null ? null : inheritedJsFieldType(lowerer, decl, p.name, original);
|
|
2156
2643
|
if (armed !== null) {
|
|
2157
2644
|
// Unknown JS properties use the instance bag: they do not
|
|
2158
2645
|
// exist until assigned, and a subclass may declare the same
|
|
@@ -2197,7 +2684,13 @@ mixin) {
|
|
|
2197
2684
|
const ctorParams = ctor && !mixinForwarding
|
|
2198
2685
|
? lowerer.paramShapes(ctor.parameters)
|
|
2199
2686
|
: (base?.ctorParams ?? (callableBase ? [{ type: DYN, mode: "dynRest" }] : []));
|
|
2687
|
+
if (ctor && !mixinForwarding && isJsSourceFile(ctor.getSourceFile()) &&
|
|
2688
|
+
!ctorParams.some((shape) => shape.mode === "rest" || shape.mode === "dynRest" || shape.mode === "islandRest") && bodyReadsArguments(ctor)) {
|
|
2689
|
+
ctorParams.push({ type: DYN, mode: "arguments" });
|
|
2690
|
+
}
|
|
2200
2691
|
const info = {
|
|
2692
|
+
...(runtimeStatics.length || checkedStatics ? { runtimeStatics } : {}),
|
|
2693
|
+
...(runtimePrototypeMembers.length ? { runtimePrototypeMembers } : {}),
|
|
2201
2694
|
def: {
|
|
2202
2695
|
name: className,
|
|
2203
2696
|
// The JS-observable .name (the class object's name string and
|
|
@@ -2237,6 +2730,7 @@ mixin) {
|
|
|
2237
2730
|
...(paramProps.length > 0 ? { paramProps } : {}),
|
|
2238
2731
|
base,
|
|
2239
2732
|
...(callableBase ? { callableBase } : {}),
|
|
2733
|
+
...(factoryBaseExpression ? { factoryBaseExpression } : {}),
|
|
2240
2734
|
subclasses: [],
|
|
2241
2735
|
throwingSetters,
|
|
2242
2736
|
staticFields,
|
|
@@ -2297,7 +2791,15 @@ mixin) {
|
|
|
2297
2791
|
if (base)
|
|
2298
2792
|
base.subclasses.push(info);
|
|
2299
2793
|
lowerer.classes.set(className, info);
|
|
2300
|
-
if (
|
|
2794
|
+
if (computedBase && !functionLocalClass(decl)) {
|
|
2795
|
+
info.def.baseValueGlobal = `%g.${className}.computedBase`;
|
|
2796
|
+
lowerer.globalsList.push({ id: info.def.baseValueGlobal, name: `${className} base`, type: computedBase.value.type, mutable: false });
|
|
2797
|
+
}
|
|
2798
|
+
if (factoryBaseExpression && !functionLocalClass(decl)) {
|
|
2799
|
+
info.def.baseValueGlobal = `%g.${className}.computedBase`;
|
|
2800
|
+
lowerer.globalsList.push({ id: info.def.baseValueGlobal, name: `${className} base`, type: { kind: "classval", className: base.def.name }, mutable: false });
|
|
2801
|
+
}
|
|
2802
|
+
if (callableBase && !functionLocalClass(decl)) {
|
|
2301
2803
|
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
2804
|
}
|
|
2303
2805
|
// A NAMED class binds its name (declarations in their scope, class
|
|
@@ -2487,10 +2989,28 @@ function withInstanceBindings(lowerer, info, fn) {
|
|
|
2487
2989
|
* field, like fieldInitStmts; a block lowers as the block statement it
|
|
2488
2990
|
* is, so its statements poison individually inside lowerStmts. */
|
|
2489
2991
|
export function lowerStaticFieldInits(lowerer, info) {
|
|
2992
|
+
if (info.localClass)
|
|
2993
|
+
return [];
|
|
2490
2994
|
// Mixin instantiations lower their initializers under the
|
|
2491
2995
|
// instantiation's bindings/context (a no-op for everything else —
|
|
2492
2996
|
// generic FAMILIES own their statics and carry no genericInstance).
|
|
2493
|
-
|
|
2997
|
+
const diagsBefore = lowerer.diags.length;
|
|
2998
|
+
try {
|
|
2999
|
+
return withInstanceBindings(lowerer, info, () => lowerStaticFieldInitsInner(lowerer, info));
|
|
3000
|
+
}
|
|
3001
|
+
catch (error) {
|
|
3002
|
+
// Heritage and decorators run before the per-field recovery below.
|
|
3003
|
+
// Keep their refusal attached to the class declaration instead of
|
|
3004
|
+
// letting a poisoned initializer escape the module lowering pass.
|
|
3005
|
+
if (!(error instanceof PoisonError))
|
|
3006
|
+
throw error;
|
|
3007
|
+
if (info.decl && isJsSourceFile(info.decl.getSourceFile()) && lowerer.diagSink === null) {
|
|
3008
|
+
const fence = lowerer.deferToRuntimeFence(diagsBefore, info.decl, { kind: "statement" });
|
|
3009
|
+
if (fence)
|
|
3010
|
+
return [fence];
|
|
3011
|
+
}
|
|
3012
|
+
return [];
|
|
3013
|
+
}
|
|
2494
3014
|
}
|
|
2495
3015
|
function lowerStaticFieldInitsInner(lowerer, info) {
|
|
2496
3016
|
// Decoration first: TC39 evaluates decorator expressions, creates the
|
|
@@ -2498,14 +3018,41 @@ function lowerStaticFieldInitsInner(lowerer, info) {
|
|
|
2498
3018
|
// initializers and static blocks (verified against Node — the
|
|
2499
3019
|
// decorated result is what `this`/the class name mean inside them).
|
|
2500
3020
|
const out = [...lowerClassDecoration(lowerer, info)];
|
|
3021
|
+
const computedBase = info.decl ? lowerer.computedClassBases.get(lowerer.classNamer(info.decl)) : undefined;
|
|
3022
|
+
if (info.factoryBaseExpression && info.def.baseValueGlobal) {
|
|
3023
|
+
const loc = locOf(info.factoryBaseExpression);
|
|
3024
|
+
const type = { kind: "classval", className: info.base.def.name };
|
|
3025
|
+
out.push({ kind: "assign", localId: info.def.baseValueGlobal, value: lowerer.lowerExprExpecting(info.factoryBaseExpression, type), loc });
|
|
3026
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
3027
|
+
lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN),
|
|
3028
|
+
lowerer.coerceToExpected({ kind: "varRef", localId: info.def.baseValueGlobal, type, loc }, DYN),
|
|
3029
|
+
], type: DYN, loc }, loc });
|
|
3030
|
+
}
|
|
3031
|
+
if (computedBase && info.def.baseValueGlobal) {
|
|
3032
|
+
const loc = computedBase.value.loc;
|
|
3033
|
+
out.push({ kind: "assign", localId: info.def.baseValueGlobal, value: computedBase.value, loc });
|
|
3034
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
3035
|
+
lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN),
|
|
3036
|
+
lowerer.coerceToExpected({ kind: "varRef", localId: info.def.baseValueGlobal, type: computedBase.value.type, loc }, DYN),
|
|
3037
|
+
], type: DYN, loc }, loc });
|
|
3038
|
+
}
|
|
2501
3039
|
if (info.callableBase) {
|
|
2502
3040
|
const { expression, constructorId, prototypeId } = info.callableBase;
|
|
2503
3041
|
const loc = locOf(expression);
|
|
2504
|
-
out.push({ kind: "assign", localId: constructorId, value: lowerer.
|
|
3042
|
+
out.push({ kind: "assign", localId: constructorId, value: info.decl && lowerer.computedCallableBases.has(info.decl)
|
|
3043
|
+
? lowerer.coerceToExpected(lowerer.computedCallableBases.get(info.decl), DYN)
|
|
3044
|
+
: lowerer.lowerExprExpecting(expression, DYN), loc }, { kind: "assign", localId: prototypeId, value: { kind: "libCall", fn: "dyn.classBasePrototype",
|
|
2505
3045
|
args: [{ kind: "varRef", localId: constructorId, type: DYN, loc }], type: DYN, loc }, loc });
|
|
2506
3046
|
const prototype = classPrototypeData(lowerer, info, loc);
|
|
2507
3047
|
if (prototype)
|
|
2508
3048
|
out.push({ kind: "exprStmt", expr: prototype, loc });
|
|
3049
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
3050
|
+
lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN),
|
|
3051
|
+
{ kind: "varRef", localId: constructorId, type: DYN, loc },
|
|
3052
|
+
], type: DYN, loc }, loc });
|
|
3053
|
+
}
|
|
3054
|
+
if (info.runtimeStatics && !info.localClass && info.decl) {
|
|
3055
|
+
out.push({ kind: "exprStmt", expr: initializeRuntimeStatics(lowerer, info, classValueRef(lowerer, info, info.decl)), loc: locOf(info.decl) });
|
|
2509
3056
|
}
|
|
2510
3057
|
const items = [
|
|
2511
3058
|
...info.staticFields.map((f) => ({ pos: f.initializer.getStart(), kind: "field", f })),
|
|
@@ -2523,12 +3070,23 @@ function lowerStaticFieldInitsInner(lowerer, info) {
|
|
|
2523
3070
|
lowerer.bumpFileStat(locOf(f.initializer).file, "total");
|
|
2524
3071
|
const diagsBefore = lowerer.diags.length;
|
|
2525
3072
|
try {
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
3073
|
+
const previousThis = lowerer.ctx.thisLocal;
|
|
3074
|
+
const receiver = f.initializer.kind === ts.SyntaxKind.ThisKeyword || hasStaticThis(f.initializer)
|
|
3075
|
+
? lowerer.declareHiddenLocal("%staticThis", { kind: "classval", className: info.def.name }) : null;
|
|
3076
|
+
if (receiver) {
|
|
3077
|
+
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
3078
|
+
out.push({ kind: "varDecl", localId: receiver.id, init: { kind: "classRef", className: info.def.name,
|
|
3079
|
+
type: receiver.type, loc: locOf(f.initializer) }, loc: locOf(f.initializer) });
|
|
3080
|
+
}
|
|
3081
|
+
let value;
|
|
3082
|
+
try {
|
|
3083
|
+
if (receiver)
|
|
3084
|
+
lowerer.ctx.thisLocal = receiver;
|
|
3085
|
+
value = lowerer.lowerExprExpecting(f.initializer, f.type);
|
|
3086
|
+
}
|
|
3087
|
+
finally {
|
|
3088
|
+
lowerer.ctx.thisLocal = previousThis;
|
|
3089
|
+
}
|
|
2532
3090
|
out.push({ kind: "assign", localId: f.globalId, value, loc: locOf(f.initializer) });
|
|
2533
3091
|
}
|
|
2534
3092
|
catch (e) {
|
|
@@ -2793,6 +3351,9 @@ export function findGenericStaticOn(lowerer, info, name) {
|
|
|
2793
3351
|
* module function — the declared-function-as-value rule verbatim
|
|
2794
3352
|
* (interned by the backend, so `C.m === C.m` holds). */
|
|
2795
3353
|
function staticMethodValue(lowerer, declarer, name, sig, blame, loc) {
|
|
3354
|
+
const reflected = reflectedClassStaticMethodValue(lowerer, declarer, name);
|
|
3355
|
+
if (reflected)
|
|
3356
|
+
return reflected;
|
|
2796
3357
|
const fnName = `%${declarer.def.name}.static:${name}`;
|
|
2797
3358
|
lowerer.noteEdge(fnName);
|
|
2798
3359
|
const funcType = {
|
|
@@ -2815,6 +3376,11 @@ function staticMethodValue(lowerer, declarer, name, sig, blame, loc) {
|
|
|
2815
3376
|
export function classValueRef(lowerer, info, blame) {
|
|
2816
3377
|
const display = info.def.name.replace(/^%|^%m\d+\./, "");
|
|
2817
3378
|
fenceDecorationThrows(lowerer, info, blame);
|
|
3379
|
+
if (info.localClass && ts.isIdentifier(blame)) {
|
|
3380
|
+
const stored = lowerer.resolveLocal(blame);
|
|
3381
|
+
if (stored)
|
|
3382
|
+
return lowerer.coerceToExpected({ kind: "varRef", localId: stored.id, type: stored.type, loc: locOf(blame) }, { kind: "classval", className: info.def.name });
|
|
3383
|
+
}
|
|
2818
3384
|
if (info.localClass && !ts.isClassExpression(blame) && !ts.isClassDeclaration(blame)) {
|
|
2819
3385
|
lowerer.unsupported("SC1090", blame, "named self references inside function-local classes");
|
|
2820
3386
|
}
|
|
@@ -2879,9 +3445,12 @@ export function staticFieldWriteTarget(lowerer, access) {
|
|
|
2879
3445
|
* fall through to the ordinary chain so the static fence or the
|
|
2880
3446
|
* generic member rejection names the site. */
|
|
2881
3447
|
export function lowerStaticFieldRead(lowerer, expr) {
|
|
3448
|
+
if (expr.name.text === "prototype")
|
|
3449
|
+
return null;
|
|
2882
3450
|
if (expr.questionDotToken)
|
|
2883
3451
|
return null;
|
|
2884
|
-
if (!ts.isIdentifier(expr.expression) && expr.expression.kind !== ts.SyntaxKind.ThisKeyword
|
|
3452
|
+
if (!ts.isIdentifier(expr.expression) && expr.expression.kind !== ts.SyntaxKind.ThisKeyword &&
|
|
3453
|
+
exactClassOfReceiver(lowerer, expr.expression) === null)
|
|
2885
3454
|
return null;
|
|
2886
3455
|
const symbol = ts.isIdentifier(expr.expression) ? lowerer.resolveValueSymbol(expr.expression) : null;
|
|
2887
3456
|
const info = (symbol ? lowerer.classBySymbol.get(symbol) : undefined) ??
|
|
@@ -2903,6 +3472,9 @@ export function lowerStaticFieldRead(lowerer, expr) {
|
|
|
2903
3472
|
return null;
|
|
2904
3473
|
const loc = locOf(expr);
|
|
2905
3474
|
const found = findStaticOn(lowerer, info, expr.name.text);
|
|
3475
|
+
if (hasRuntimeStatics(info) && !found?.field)
|
|
3476
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(classValueRef(lowerer, info, expr.expression), DYN),
|
|
3477
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
2906
3478
|
// A #private static resolves only through the DECLARING class's own
|
|
2907
3479
|
// name: in JS the brand lives on that one constructor object, so
|
|
2908
3480
|
// `D.#s` (a subclass receiver) throws Node's TypeError instead of
|
|
@@ -2930,9 +3502,18 @@ export function lowerStaticFieldRead(lowerer, expr) {
|
|
|
2930
3502
|
if (expr.name.text === "name" && info.def.jsName !== undefined) {
|
|
2931
3503
|
return { kind: "strLit", value: info.def.jsName, type: STRING, loc };
|
|
2932
3504
|
}
|
|
3505
|
+
if (info.callableBase)
|
|
3506
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(classValueRef(lowerer, info, expr.expression), DYN),
|
|
3507
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
2933
3508
|
const data = classStaticDataFor(lowerer, info, expr.name.text, loc);
|
|
2934
|
-
if (data)
|
|
2935
|
-
|
|
3509
|
+
if (data) {
|
|
3510
|
+
const value = { kind: "dynKeyGet", value: data, key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
3511
|
+
// JavaScript often assigns public statics after the class declaration.
|
|
3512
|
+
// Their storage stays mutable and checked, while reads retain the
|
|
3513
|
+
// checker's concrete type instead of losing the method receiver.
|
|
3514
|
+
const type = isJsSourceFile(expr.getSourceFile()) ? null : lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
3515
|
+
return type ? lowerer.coerceToExpected(value, type) : value;
|
|
3516
|
+
}
|
|
2936
3517
|
return null;
|
|
2937
3518
|
}
|
|
2938
3519
|
/** NamedEvaluation's answer for a class expression's `.name`: its own
|
|
@@ -2987,13 +3568,29 @@ export function lowerClassExpressionInfo(lowerer, expr) {
|
|
|
2987
3568
|
lowerer.unsupported("SC1090", expr, "function-local classes with private brands");
|
|
2988
3569
|
}
|
|
2989
3570
|
if (local && (expr.typeParameters?.length ||
|
|
2990
|
-
decoratorNodesOf(expr).length || expr.members.some((member) =>
|
|
2991
|
-
|
|
3571
|
+
decoratorNodesOf(expr).length || expr.members.some((member) => decoratorNodesOf(member).length ||
|
|
3572
|
+
(!isJsSourceFile(expr.getSourceFile()) && (ts.isClassStaticBlockDeclaration(member) ||
|
|
3573
|
+
ts.getModifiers(member)?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))) ||
|
|
2992
3574
|
(ts.isMethodDeclaration(member) && member.typeParameters?.length)))) {
|
|
2993
3575
|
lowerer.unsupported("SC1090", expr, "function-local classes with statics, decorators, or generic members");
|
|
2994
3576
|
}
|
|
2995
3577
|
lowerer.collectingExprClasses.add(expr);
|
|
2996
3578
|
try {
|
|
3579
|
+
if (local && lowerer.fnStack.length > 0 && !lowerer.computedClassBases.has(lowerer.classNamer(expr))) {
|
|
3580
|
+
const heritage = expr.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)?.types[0]?.expression;
|
|
3581
|
+
if (heritage && (ts.isCallExpression(heritage) ||
|
|
3582
|
+
ts.isIdentifier(heritage) && (lowerer.peekLocal(heritage) ?? lowerer.globalOf(heritage)) != null)) {
|
|
3583
|
+
let value = lowerer.lowerExpr(heritage);
|
|
3584
|
+
const known = value.type.kind === "dyn" ? storedClassValueType(lowerer, heritage) : null;
|
|
3585
|
+
if (known?.kind === "classval")
|
|
3586
|
+
value = lowerer.coerceToExpected(value, known);
|
|
3587
|
+
const base = value.type.kind === "classval" ? lowerer.classes.get(value.type.className) : undefined;
|
|
3588
|
+
if (base)
|
|
3589
|
+
lowerer.computedClassBases.set(lowerer.classNamer(expr), { value, classInfo: base });
|
|
3590
|
+
else if (value.type.kind === "dyn" || value.type.kind === "func")
|
|
3591
|
+
lowerer.computedCallableBases.set(expr, value);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
2997
3594
|
lowerer.collectClassShapeInner(expr, namedEvaluationName(expr));
|
|
2998
3595
|
}
|
|
2999
3596
|
finally {
|
|
@@ -3022,7 +3619,7 @@ export function lowerClassExpressionInfo(lowerer, expr) {
|
|
|
3022
3619
|
// drains it), which is JS's order for whole-initializer positions.
|
|
3023
3620
|
// Anything subtler (multi-declarator statements, arguments evaluated
|
|
3024
3621
|
// after other side effects) is a named fence, never a reordering.
|
|
3025
|
-
if (info.staticFields.length > 0 || (info.staticBlocks?.length ?? 0) > 0) {
|
|
3622
|
+
if (!local && (info.callableBase || info.def.baseValueGlobal || info.runtimeStatics || info.staticFields.length > 0 || (info.staticBlocks?.length ?? 0) > 0)) {
|
|
3026
3623
|
let holder = expr.parent;
|
|
3027
3624
|
while (ts.isParenthesizedExpression(holder) || ts.isClassExpression(holder) ||
|
|
3028
3625
|
ts.isHeritageClause(holder) || ts.isExpressionWithTypeArguments(holder)) {
|
|
@@ -3042,7 +3639,6 @@ export function lowerClassExpressionInfo(lowerer, expr) {
|
|
|
3042
3639
|
if (!wholeInit) {
|
|
3043
3640
|
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
3641
|
}
|
|
3045
|
-
lowerer.pendingClassExprInits.push(...lowerer.lowerStaticFieldInits(info));
|
|
3046
3642
|
}
|
|
3047
3643
|
return info;
|
|
3048
3644
|
}
|
|
@@ -3068,6 +3664,11 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3068
3664
|
}
|
|
3069
3665
|
}
|
|
3070
3666
|
const info = lowerClassExpressionInfo(lowerer, expr);
|
|
3667
|
+
if (!info.localClass && !info.expressionInitializationEmitted &&
|
|
3668
|
+
(info.callableBase || info.def.baseValueGlobal || info.runtimeStatics || info.staticFields.length > 0 || (info.staticBlocks?.length ?? 0) > 0)) {
|
|
3669
|
+
info.expressionInitializationEmitted = true;
|
|
3670
|
+
lowerer.pendingClassExprInits.push(...lowerer.lowerStaticFieldInits(info));
|
|
3671
|
+
}
|
|
3071
3672
|
if (info.localClass && !info.localClass.ready) {
|
|
3072
3673
|
const context = newFnCtx(true, null, null, VOID);
|
|
3073
3674
|
info.localClass.context = context;
|
|
@@ -3083,6 +3684,7 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3083
3684
|
info.def.localCaptures.push({ localId: "%base", name: "base", type: { kind: "classval", className: info.base.def.name } });
|
|
3084
3685
|
}
|
|
3085
3686
|
info.localClass.ready = true;
|
|
3687
|
+
lowerer.onExprClassCollected?.(info);
|
|
3086
3688
|
}
|
|
3087
3689
|
finally {
|
|
3088
3690
|
lowerer.fnStack.pop();
|
|
@@ -3090,9 +3692,27 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3090
3692
|
}
|
|
3091
3693
|
}
|
|
3092
3694
|
const value = classValueRef(lowerer, info, expr);
|
|
3695
|
+
if (info.localClass && info.callableBase && value.kind === "classRef") {
|
|
3696
|
+
const loc = locOf(expr);
|
|
3697
|
+
const base = lowerer.lowerExprExpecting(info.callableBase.expression, DYN);
|
|
3698
|
+
const baseLocal = lowerer.declareHiddenLocal("%classBase", DYN);
|
|
3699
|
+
const clsLocal = lowerer.declareHiddenLocal("%classValue", value.type);
|
|
3700
|
+
const baseRef = { kind: "varRef", localId: baseLocal.id, type: DYN, loc };
|
|
3701
|
+
const clsRef = { kind: "varRef", localId: clsLocal.id, type: value.type, loc };
|
|
3702
|
+
const boxed = { kind: "dynFrom", value: clsRef, type: DYN, loc };
|
|
3703
|
+
info.localPrototypeData = true;
|
|
3704
|
+
return initializeRuntimeStatics(lowerer, info, { kind: "seqExpr", stmts: [
|
|
3705
|
+
{ kind: "varDecl", localId: baseLocal.id, init: base, loc },
|
|
3706
|
+
{ kind: "varDecl", localId: clsLocal.id, init: { ...value, captures: info.localClass.captureSources }, loc },
|
|
3707
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [boxed, baseRef], type: DYN, loc }, loc },
|
|
3708
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classPrototype", args: [boxed,
|
|
3709
|
+
{ kind: "libCall", fn: "dyn.classBasePrototype", args: [baseRef], type: DYN, loc },
|
|
3710
|
+
], type: DYN, loc }, loc },
|
|
3711
|
+
], result: clsRef, type: value.type, loc });
|
|
3712
|
+
}
|
|
3093
3713
|
if (info.localClass?.baseExpression && info.base && value.kind === "classRef") {
|
|
3094
3714
|
const loc = locOf(expr);
|
|
3095
|
-
const base = lowerer.
|
|
3715
|
+
const base = lowerer.lowerExprExpecting(info.localClass.baseExpression, { kind: "classval", className: info.base.def.name });
|
|
3096
3716
|
const baseLocal = lowerer.declareHiddenLocal("%classBase", base.type);
|
|
3097
3717
|
baseLocal.boxed = true;
|
|
3098
3718
|
const clsLocal = lowerer.declareHiddenLocal("%classValue", value.type);
|
|
@@ -3100,17 +3720,19 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3100
3720
|
const clsRef = { kind: "varRef", localId: clsLocal.id, type: value.type, loc };
|
|
3101
3721
|
const basePrototype = classPrototypeData(lowerer, info.base, loc, baseRef);
|
|
3102
3722
|
info.localPrototypeData = true;
|
|
3103
|
-
return { kind: "seqExpr", stmts: [
|
|
3723
|
+
return initializeRuntimeStatics(lowerer, info, { kind: "seqExpr", stmts: [
|
|
3104
3724
|
{ kind: "varDecl", localId: baseLocal.id, init: base, loc },
|
|
3105
3725
|
{ kind: "varDecl", localId: clsLocal.id, init: { ...value, captures: [...info.localClass.captureSources, baseLocal.id] }, loc },
|
|
3726
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classInherit", args: [
|
|
3727
|
+
{ kind: "dynFrom", value: clsRef, type: DYN, loc }, { kind: "dynFrom", value: baseRef, type: DYN, loc },
|
|
3728
|
+
], type: DYN, loc }, loc },
|
|
3106
3729
|
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classPrototype", args: [
|
|
3107
3730
|
{ kind: "dynFrom", value: clsRef, type: DYN, loc }, basePrototype,
|
|
3108
3731
|
], type: DYN, loc }, loc },
|
|
3109
|
-
], result: clsRef, type: value.type, loc };
|
|
3732
|
+
], result: clsRef, type: value.type, loc });
|
|
3110
3733
|
}
|
|
3111
|
-
return info.localClass
|
|
3112
|
-
? { ...value, captures: info.localClass.captureSources }
|
|
3113
|
-
: value;
|
|
3734
|
+
return info.localClass ? initializeRuntimeStatics(lowerer, info, value.kind === "classRef"
|
|
3735
|
+
? { ...value, captures: info.localClass.captureSources } : value) : value;
|
|
3114
3736
|
}
|
|
3115
3737
|
/** The EXACT class a receiver expression is statically known to BE (not
|
|
3116
3738
|
* merely be typed by): the class name itself, or a `const` binding
|
|
@@ -3119,6 +3741,11 @@ export function lowerClassExpression(lowerer, expr) {
|
|
|
3119
3741
|
* hit the declaring class's storage exactly (the shadowing hazards of
|
|
3120
3742
|
* general class values don't arise). Null for everything else. */
|
|
3121
3743
|
export function exactClassOfReceiver(lowerer, expr) {
|
|
3744
|
+
if (ts.isPropertyAccessExpression(expr) && !expr.questionDotToken) {
|
|
3745
|
+
const member = nsMemberIdentOf(lowerer, expr);
|
|
3746
|
+
if (member)
|
|
3747
|
+
return exactClassOfReceiver(lowerer, member);
|
|
3748
|
+
}
|
|
3122
3749
|
// A class-valued lexical this is introduced only for an exact static
|
|
3123
3750
|
// receiver specialization; ordinary instance this remains an object.
|
|
3124
3751
|
if (expr.kind === ts.SyntaxKind.ThisKeyword) {
|
|
@@ -3274,7 +3901,12 @@ export function lowerStaticMethodCall(lowerer, call, access) {
|
|
|
3274
3901
|
// export assignment pins it) — the direct-name rules apply.
|
|
3275
3902
|
if (!ts.isIdentifier(access.expression)) {
|
|
3276
3903
|
if (!isModuleExportsAccess(access.expression) || !isCjsJsFile(access.getSourceFile(), lowerer.program)) {
|
|
3277
|
-
|
|
3904
|
+
let receiverType = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
3905
|
+
if (receiverType?.kind !== "classval" && isJsSourceFile(call.getSourceFile())) {
|
|
3906
|
+
const receiver = tryLowerExpression(lowerer, access.expression);
|
|
3907
|
+
if (receiver?.type.kind === "classval")
|
|
3908
|
+
receiverType = receiver.type;
|
|
3909
|
+
}
|
|
3278
3910
|
if (receiverType?.kind !== "classval")
|
|
3279
3911
|
return null;
|
|
3280
3912
|
const info = lowerer.classes.get(receiverType.className);
|
|
@@ -3331,6 +3963,10 @@ function classValueIsExactlyOwn(lowerer, recv, info) {
|
|
|
3331
3963
|
}
|
|
3332
3964
|
function staticCallOn(lowerer, call, access, info, throughValue) {
|
|
3333
3965
|
const loc = locOf(call);
|
|
3966
|
+
if (hasRuntimeStatics(info) && !findStaticOn(lowerer, info, access.name.text)?.field || info.callableBase && !findStaticOn(lowerer, info, access.name.text) && !findGenericStaticOn(lowerer, info, access.name.text))
|
|
3967
|
+
return { kind: "dynInvoke", recv: lowerer.lowerExprExpecting(access.expression, DYN),
|
|
3968
|
+
method: access.name.text, calleeName: access.name.text,
|
|
3969
|
+
args: call.arguments.map((argument) => lowerer.lowerExprExpecting(argument, DYN)), type: DYN, loc };
|
|
3334
3970
|
// #private statics: through-a-VALUE receiver the call devirtualizes
|
|
3335
3971
|
// exactly when the value can only BE the declaring class
|
|
3336
3972
|
// (classValueIsExactlyOwn — a slot that could hold a descendant at
|
|
@@ -3382,7 +4018,7 @@ function staticCallOn(lowerer, call, access, info, throughValue) {
|
|
|
3382
4018
|
}
|
|
3383
4019
|
args.push(absent);
|
|
3384
4020
|
}
|
|
3385
|
-
return { kind: "callValue", callee, args, type: found.field.type.ret, loc };
|
|
4021
|
+
return { kind: "callValue", callee, receiver: lowerer.lowerExprExpecting(access.expression, DYN), args, type: found.field.type.ret, loc };
|
|
3386
4022
|
}
|
|
3387
4023
|
if (found.method.member.body && hasStaticThis(found.method.member)) {
|
|
3388
4024
|
const generic = found.method.receiverInfo ??= {
|
|
@@ -3435,6 +4071,10 @@ export function lowerClassValueProperty(lowerer, expr) {
|
|
|
3435
4071
|
return null;
|
|
3436
4072
|
const loc = locOf(expr);
|
|
3437
4073
|
const member = expr.name.text;
|
|
4074
|
+
const info = lowerer.classes.get(recvT.className);
|
|
4075
|
+
if (info && (hasRuntimeStatics(info) && !findStaticOn(lowerer, info, member)?.field || info.callableBase && !findStaticOn(lowerer, info, member)))
|
|
4076
|
+
return { kind: "dynKeyGet", value: lowerer.lowerExprExpecting(expr.expression, DYN),
|
|
4077
|
+
key: { kind: "strLit", value: member, type: STRING, loc }, type: DYN, loc };
|
|
3438
4078
|
// `X.name` reads the RUNTIME class object's stored name — the one
|
|
3439
4079
|
// genuinely dynamic member. It CONSUMES the receiver (no evaluation
|
|
3440
4080
|
// is discarded), so any receiver expression is fine here.
|
|
@@ -3444,6 +4084,8 @@ export function lowerClassValueProperty(lowerer, expr) {
|
|
|
3444
4084
|
return null;
|
|
3445
4085
|
return { kind: "libCall", fn: "class.name", args: [recv], type: STRING, loc };
|
|
3446
4086
|
}
|
|
4087
|
+
if (!info)
|
|
4088
|
+
return null;
|
|
3447
4089
|
if (!ts.isIdentifier(expr.expression) &&
|
|
3448
4090
|
expr.expression.kind !== ts.SyntaxKind.ThisKeyword &&
|
|
3449
4091
|
// `module.exports.label` in a class-replaced CJS module: the
|
|
@@ -3457,9 +4099,6 @@ export function lowerClassValueProperty(lowerer, expr) {
|
|
|
3457
4099
|
}
|
|
3458
4100
|
// The direct class-name spelling resolved in lowerStaticFieldRead;
|
|
3459
4101
|
// 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
4102
|
const found = findStaticOn(lowerer, info, member);
|
|
3464
4103
|
if (!found) {
|
|
3465
4104
|
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)`);
|
|
@@ -3554,15 +4193,153 @@ export function findGenericMethodOn(lowerer, info, name) {
|
|
|
3554
4193
|
export function genericOverrideBelow(lowerer, info, name) {
|
|
3555
4194
|
return info.subclasses.some((s) => s.genericMethods?.has(name) === true || genericOverrideBelow(lowerer, s, name));
|
|
3556
4195
|
}
|
|
4196
|
+
/** Discover inherited data writes before any base storage ABI is chosen.
|
|
4197
|
+
* The checker does not widen a base field for assignments in JS subclasses. */
|
|
4198
|
+
export function collectInheritedJsFieldWrites(lowerer, files) {
|
|
4199
|
+
for (const file of files) {
|
|
4200
|
+
if (!isJsSourceFile(file))
|
|
4201
|
+
continue;
|
|
4202
|
+
ts.walkPreorder(file, (node) => {
|
|
4203
|
+
if ((!ts.isClassDeclaration(node) && !ts.isClassExpression(node)) || !node.heritageClauses?.length)
|
|
4204
|
+
return;
|
|
4205
|
+
const writes = new Map();
|
|
4206
|
+
const addWrite = (name, expression) => {
|
|
4207
|
+
const existing = writes.get(name);
|
|
4208
|
+
if (existing)
|
|
4209
|
+
existing.push(expression);
|
|
4210
|
+
else
|
|
4211
|
+
writes.set(name, [expression]);
|
|
4212
|
+
};
|
|
4213
|
+
const scan = (part) => {
|
|
4214
|
+
if (ts.isClassDeclaration(part) || ts.isClassExpression(part) ||
|
|
4215
|
+
ts.isFunctionLike(part) && !ts.isArrowFunction(part))
|
|
4216
|
+
return;
|
|
4217
|
+
if (ts.isBinaryExpression(part) && part.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
4218
|
+
const left = part.left;
|
|
4219
|
+
if (ts.isPropertyAccessExpression(left) && left.expression.kind === ts.SyntaxKind.ThisKeyword)
|
|
4220
|
+
addWrite(left.name.text, part.right);
|
|
4221
|
+
else if (ts.isElementAccessExpression(left) && left.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
4222
|
+
const name = lowerer.foldedStringKeyOf(left.argumentExpression);
|
|
4223
|
+
if (name !== null)
|
|
4224
|
+
addWrite(name, part.right);
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
part.forEachChild(scan);
|
|
4228
|
+
};
|
|
4229
|
+
for (const member of node.members) {
|
|
4230
|
+
if (member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))
|
|
4231
|
+
continue;
|
|
4232
|
+
if (ts.isPropertyDeclaration(member) && member.initializer) {
|
|
4233
|
+
const name = ts.isPrivateIdentifier(member.name) ? null : classMemberNameOf(lowerer, member.name);
|
|
4234
|
+
if (name !== null)
|
|
4235
|
+
addWrite(name, member.initializer);
|
|
4236
|
+
scan(member.initializer);
|
|
4237
|
+
}
|
|
4238
|
+
if (ts.isConstructorDeclaration(member) || ts.isMethodDeclaration(member) || ts.isAccessor(member)) {
|
|
4239
|
+
if (member.body)
|
|
4240
|
+
scan(member.body);
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
if (writes.size === 0)
|
|
4244
|
+
return;
|
|
4245
|
+
const symbol = lowerer.typeOf(node).getSymbol();
|
|
4246
|
+
const instance = symbol ? lowerer.checker.getDeclaredTypeOfSymbol(symbol) : null;
|
|
4247
|
+
const seen = new Set();
|
|
4248
|
+
const markBases = (type) => {
|
|
4249
|
+
const target = type.isTypeReference() ? type.getTarget() : type;
|
|
4250
|
+
if (!target?.isClassOrInterface() || seen.has(target))
|
|
4251
|
+
return;
|
|
4252
|
+
seen.add(target);
|
|
4253
|
+
for (const base of lowerer.checker.getBaseTypes(target)) {
|
|
4254
|
+
for (const [name, expressions] of writes) {
|
|
4255
|
+
const property = lowerer.checker.getPropertyOfType(base, name);
|
|
4256
|
+
for (const declaration of property ? lowerer.checker.declarationsOf(property) : []) {
|
|
4257
|
+
if (ts.isMethodDeclaration(declaration) || ts.isAccessor(declaration))
|
|
4258
|
+
continue;
|
|
4259
|
+
let owner = declaration.parent;
|
|
4260
|
+
while (owner && !ts.isClassDeclaration(owner) && !ts.isClassExpression(owner))
|
|
4261
|
+
owner = owner.parent;
|
|
4262
|
+
if (!owner || (!ts.isClassDeclaration(owner) && !ts.isClassExpression(owner)) || !isJsSourceFile(owner.getSourceFile()))
|
|
4263
|
+
continue;
|
|
4264
|
+
let fields = lowerer.inheritedJsFieldWrites.get(owner);
|
|
4265
|
+
if (!fields)
|
|
4266
|
+
lowerer.inheritedJsFieldWrites.set(owner, fields = new Map());
|
|
4267
|
+
const existing = fields.get(name);
|
|
4268
|
+
if (existing)
|
|
4269
|
+
existing.push(...expressions);
|
|
4270
|
+
else
|
|
4271
|
+
fields.set(name, [...expressions]);
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
markBases(base);
|
|
4275
|
+
}
|
|
4276
|
+
};
|
|
4277
|
+
if (instance)
|
|
4278
|
+
markBases(instance);
|
|
4279
|
+
});
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
3557
4282
|
/** Discover overrides before choosing inferred method ABIs. Class collection
|
|
3558
4283
|
* visits bases first, including imported bases and class expressions, so the
|
|
3559
4284
|
* collected subclass graph cannot answer this question yet. The checker
|
|
3560
4285
|
* hierarchy identifies the original declarations through aliases as well. */
|
|
3561
4286
|
export function collectVirtualJsMethods(lowerer, files) {
|
|
3562
|
-
|
|
3563
|
-
|
|
4287
|
+
const families = [];
|
|
4288
|
+
// Record named prototype accesses before collecting method signatures.
|
|
4289
|
+
// Those slots need a callable ABI even when JavaScript would otherwise
|
|
4290
|
+
// specialize each call independently.
|
|
4291
|
+
for (const file of files)
|
|
4292
|
+
ts.walkPreorder(file, (node) => {
|
|
4293
|
+
if (!ts.isPropertyAccessExpression(node) && !ts.isElementAccessExpression(node))
|
|
4294
|
+
return;
|
|
4295
|
+
const name = ts.isPropertyAccessExpression(node) ? node.name.text
|
|
4296
|
+
: ts.isStringLiteralLike(node.argumentExpression) ? node.argumentExpression.text : null;
|
|
4297
|
+
if (name === null)
|
|
4298
|
+
return;
|
|
4299
|
+
const prototype = ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "prototype";
|
|
4300
|
+
const assigned = ts.isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
|
|
4301
|
+
const symbol = !assigned ? undefined : ts.isPropertyAccessExpression(node)
|
|
4302
|
+
? lowerer.checker.getSymbolAtLocation(node.name)
|
|
4303
|
+
: lowerer.checker.getPropertyOfType(lowerer.typeOf(node.expression), name);
|
|
4304
|
+
if (prototype || symbol && lowerer.checker.declarationsOf(symbol).some(ts.isMethodDeclaration)) {
|
|
4305
|
+
lowerer.prototypeMethodAccesses.set(name, node);
|
|
4306
|
+
}
|
|
4307
|
+
});
|
|
3564
4308
|
const visit = (node) => {
|
|
3565
4309
|
if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
|
|
4310
|
+
const accessors = node.members.filter((member) => (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) && ts.isIdentifier(member.name) &&
|
|
4311
|
+
!member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword));
|
|
4312
|
+
if (node.heritageClauses?.length && accessors.length && isJsSourceFile(node.getSourceFile())) {
|
|
4313
|
+
const symbol = lowerer.typeOf(node).getSymbol();
|
|
4314
|
+
const instance = symbol ? lowerer.checker.getDeclaredTypeOfSymbol(symbol) : null;
|
|
4315
|
+
const seen = new Set();
|
|
4316
|
+
const markAccessors = (type) => {
|
|
4317
|
+
const target = type.isTypeReference() ? type.getTarget() : type;
|
|
4318
|
+
if (!target?.isClassOrInterface() || seen.has(target))
|
|
4319
|
+
return;
|
|
4320
|
+
seen.add(target);
|
|
4321
|
+
for (const base of lowerer.checker.getBaseTypes(target)) {
|
|
4322
|
+
for (const member of accessors) {
|
|
4323
|
+
if (!ts.isIdentifier(member.name))
|
|
4324
|
+
continue;
|
|
4325
|
+
const property = lowerer.checker.getPropertyOfType(base, member.name.text);
|
|
4326
|
+
for (const declaration of property ? lowerer.checker.declarationsOf(property) : []) {
|
|
4327
|
+
if ((ts.isGetAccessorDeclaration(declaration) || ts.isSetAccessorDeclaration(declaration)) && isJsSourceFile(declaration.getSourceFile())) {
|
|
4328
|
+
lowerer.checkedVirtualJsAccessors.add(declaration);
|
|
4329
|
+
lowerer.checkedVirtualJsAccessors.add(member);
|
|
4330
|
+
for (const twin of node.members)
|
|
4331
|
+
if ((ts.isGetAccessorDeclaration(twin) || ts.isSetAccessorDeclaration(twin)) &&
|
|
4332
|
+
ts.isIdentifier(twin.name) && twin.name.text === member.name.text)
|
|
4333
|
+
lowerer.checkedVirtualJsAccessors.add(twin);
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
markAccessors(base);
|
|
4338
|
+
}
|
|
4339
|
+
};
|
|
4340
|
+
if (instance)
|
|
4341
|
+
markAccessors(instance);
|
|
4342
|
+
}
|
|
3566
4343
|
const members = node.members.filter((member) => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
|
|
3567
4344
|
!member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword));
|
|
3568
4345
|
if (node.heritageClauses?.length && members.length > 0) {
|
|
@@ -3585,8 +4362,17 @@ export function collectVirtualJsMethods(lowerer, files) {
|
|
|
3585
4362
|
if (isJsSourceFile(member.getSourceFile()) && !member.typeParameters &&
|
|
3586
4363
|
!member.asteriskToken && !member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) &&
|
|
3587
4364
|
!bodyReadsArguments(member) &&
|
|
3588
|
-
member.parameters.every((p) => !p.
|
|
4365
|
+
member.parameters.every((p) => !p.dotDotDotToken)) {
|
|
3589
4366
|
lowerer.virtualJsMethodArity.set(declaration, Math.max(lowerer.virtualJsMethodArity.get(declaration) ?? 0, member.parameters.length));
|
|
4367
|
+
const checked = !declaration.typeParameters && !declaration.asteriskToken &&
|
|
4368
|
+
!declaration.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) &&
|
|
4369
|
+
!bodyReadsArguments(declaration) &&
|
|
4370
|
+
[...member.parameters, ...declaration.parameters].every((p) => !p.dotDotDotToken);
|
|
4371
|
+
if (checked) {
|
|
4372
|
+
lowerer.checkedVirtualJsMethods.add(declaration);
|
|
4373
|
+
lowerer.checkedVirtualJsMethods.add(member);
|
|
4374
|
+
families.push([declaration, member]);
|
|
4375
|
+
}
|
|
3590
4376
|
}
|
|
3591
4377
|
}
|
|
3592
4378
|
}
|
|
@@ -3601,6 +4387,29 @@ export function collectVirtualJsMethods(lowerer, files) {
|
|
|
3601
4387
|
};
|
|
3602
4388
|
for (const file of files)
|
|
3603
4389
|
ts.walkPreorder(file, visit);
|
|
4390
|
+
for (const member of lowerer.checkedVirtualJsMethods) {
|
|
4391
|
+
const scan = (node) => {
|
|
4392
|
+
if (node !== member && ts.isFunctionLike(node))
|
|
4393
|
+
return;
|
|
4394
|
+
if (ts.isReturnStatement(node) && node.expression)
|
|
4395
|
+
lowerer.checkedVirtualJsValueReturns.add(member);
|
|
4396
|
+
node.forEachChild(scan);
|
|
4397
|
+
};
|
|
4398
|
+
scan(member);
|
|
4399
|
+
}
|
|
4400
|
+
let changed = true;
|
|
4401
|
+
while (changed) {
|
|
4402
|
+
changed = false;
|
|
4403
|
+
for (const [base, derived] of families) {
|
|
4404
|
+
if (!lowerer.checkedVirtualJsValueReturns.has(base) && !lowerer.checkedVirtualJsValueReturns.has(derived))
|
|
4405
|
+
continue;
|
|
4406
|
+
for (const member of [base, derived])
|
|
4407
|
+
if (!lowerer.checkedVirtualJsValueReturns.has(member)) {
|
|
4408
|
+
lowerer.checkedVirtualJsValueReturns.add(member);
|
|
4409
|
+
changed = true;
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
3604
4413
|
}
|
|
3605
4414
|
/** The receiver's EXACT runtime class, when the expression proves it: a
|
|
3606
4415
|
* `new C(...)` expression directly, or a const binding initialized with
|
|
@@ -3786,12 +4595,22 @@ export function lowerClassMembers(lowerer, info) {
|
|
|
3786
4595
|
// signature-blocked function (lowerStaticMethod's rule) instead of
|
|
3787
4596
|
// crashing the whole lowering.
|
|
3788
4597
|
if (!info.generic && (always || lowerer.wantBody(`%${className}.constructor`))) {
|
|
4598
|
+
const diagsBefore = lowerer.diags.length;
|
|
3789
4599
|
try {
|
|
3790
4600
|
out.push(lowerer.lowerClassCtor(info));
|
|
3791
4601
|
}
|
|
3792
4602
|
catch (e) {
|
|
3793
4603
|
if (!(e instanceof PoisonError))
|
|
3794
4604
|
throw e;
|
|
4605
|
+
if (info.decl && isJsSourceFile(info.decl.getSourceFile())) {
|
|
4606
|
+
const types = [{ kind: "object", className }, ...info.ctorParams.map((param) => param.type)];
|
|
4607
|
+
const fallback = lowerer.deferToRuntimeFence(diagsBefore, info.ctor ?? info.decl, {
|
|
4608
|
+
kind: "function", name: `%${className}.constructor`, returnType: VOID,
|
|
4609
|
+
params: types.map((type, index) => ({ localId: `p.${index}`, name: index === 0 ? "this" : `p${index}`, type })),
|
|
4610
|
+
});
|
|
4611
|
+
if (fallback)
|
|
4612
|
+
out.push(fallback);
|
|
4613
|
+
}
|
|
3795
4614
|
}
|
|
3796
4615
|
}
|
|
3797
4616
|
for (const { mName, member } of lowerer.classMethodMembers(info)) {
|
|
@@ -3877,6 +4696,11 @@ function lowerClassCtorInner(lowerer, info) {
|
|
|
3877
4696
|
// defaults on entry, ahead of everything the body does.
|
|
3878
4697
|
const declared = lowerer.declareParams(info.ctor.parameters, info.ctorParams);
|
|
3879
4698
|
params.push(...declared.params);
|
|
4699
|
+
if (info.ctorParams.at(-1)?.mode === "arguments") {
|
|
4700
|
+
const local = lowerer.declareHiddenLocal("%arguments", DYN);
|
|
4701
|
+
params.push({ localId: local.id, name: local.name, type: DYN });
|
|
4702
|
+
lowerer.ctx.argumentsLocal = local;
|
|
4703
|
+
}
|
|
3880
4704
|
body.push(...declared.prologue);
|
|
3881
4705
|
if (!ctorBase && !info.callableBase) {
|
|
3882
4706
|
// Node's base-class order: field initializers run at the start
|
|
@@ -3945,6 +4769,10 @@ function lowerClassCtorInner(lowerer, info) {
|
|
|
3945
4769
|
export function classMemberNameOf(lowerer, name) {
|
|
3946
4770
|
if (ts.isIdentifier(name))
|
|
3947
4771
|
return name.text;
|
|
4772
|
+
if (ts.isStringLiteralLike(name))
|
|
4773
|
+
return name.text;
|
|
4774
|
+
if (ts.isNumericLiteral(name))
|
|
4775
|
+
return String(Number(name.text));
|
|
3948
4776
|
// #private methods key by their spelled name ('#m') — '#' is
|
|
3949
4777
|
// unspellable in public identifiers, the accessor-colon precedent.
|
|
3950
4778
|
if (ts.isPrivateIdentifier(name))
|
|
@@ -4174,7 +5002,7 @@ export function lowerClassMethodMember(lowerer, info, fnLike) {
|
|
|
4174
5002
|
function lowerClassMethodMemberInner(lowerer, info, fnLike) {
|
|
4175
5003
|
const className = info.def.name;
|
|
4176
5004
|
const thisType = { kind: "object", className };
|
|
4177
|
-
const memberName =
|
|
5005
|
+
const memberName = classMemberNameOf(lowerer, fnLike.name);
|
|
4178
5006
|
if (memberName === null)
|
|
4179
5007
|
return null;
|
|
4180
5008
|
const mName = ts.isMethodDeclaration(fnLike)
|
|
@@ -4383,6 +5211,13 @@ export function fieldInitStmts(lowerer, info, thisLocal) {
|
|
|
4383
5211
|
const out = [];
|
|
4384
5212
|
const thisType = { kind: "object", className: info.def.name };
|
|
4385
5213
|
for (const f of info.fieldOrder) {
|
|
5214
|
+
if (f.errorCause) {
|
|
5215
|
+
const loc = f.initializer ? locOf(f.initializer) : f.undefinedInitializer;
|
|
5216
|
+
const value = f.initializer ? lowerer.lowerExprExpecting(f.initializer, DYN) : dynUndefinedExpr(loc);
|
|
5217
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "error.defineCause",
|
|
5218
|
+
args: [{ kind: "varRef", localId: thisLocal.id, type: thisType, loc }, value], type: VOID, loc }, loc });
|
|
5219
|
+
continue;
|
|
5220
|
+
}
|
|
4386
5221
|
if (f.undefinedInitializer) {
|
|
4387
5222
|
const loc = f.undefinedInitializer;
|
|
4388
5223
|
out.push({
|
|
@@ -4455,100 +5290,93 @@ function paramPropInitStmts(lowerer, info, thisLocal) {
|
|
|
4455
5290
|
}
|
|
4456
5291
|
return out;
|
|
4457
5292
|
}
|
|
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 = [];
|
|
5293
|
+
/** Super statements initialize the base and then the derived fields on
|
|
5294
|
+
* whichever control-flow path executes. Repeated calls are explicitly
|
|
5295
|
+
* refused until native layouts can allocate a second receiver. */
|
|
5296
|
+
export function lowerDerivedCtorBody(lowerer, info, thisLocal, forward) {
|
|
4471
5297
|
let superSeen = false;
|
|
4472
|
-
const
|
|
4473
|
-
|
|
4474
|
-
lowerer.
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
5298
|
+
const initialized = lowerer.declareHiddenLocal("%superInitialized", BOOL);
|
|
5299
|
+
initialized.mutable = true;
|
|
5300
|
+
const context = lowerer.ctx;
|
|
5301
|
+
const previous = context.superCall;
|
|
5302
|
+
const previousInitialized = context.superInitialized;
|
|
5303
|
+
context.superInitialized = initialized;
|
|
5304
|
+
context.superCall = (superCall) => {
|
|
5305
|
+
if (!ts.isExpressionStatement(superCall.parent))
|
|
5306
|
+
lowerer.unsupported("SC1090", superCall, "super() in an expression position");
|
|
5307
|
+
superSeen = true;
|
|
5308
|
+
const loc = locOf(superCall);
|
|
5309
|
+
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 }];
|
|
5310
|
+
if (info.callableBase) {
|
|
5311
|
+
if (superCall.arguments.some(ts.isSpreadElement))
|
|
5312
|
+
lowerer.unsupported("SC1090", superCall, "spreading explicit super arguments into an ordinary constructor");
|
|
5313
|
+
const pack = { kind: "dynArrLit", elems: superCall.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc };
|
|
5314
|
+
statements.push(lowerer.superCallStmt(info, thisLocal, [pack], loc));
|
|
5315
|
+
}
|
|
5316
|
+
else {
|
|
5317
|
+
const base = superBaseOf(info);
|
|
5318
|
+
if (base.builtinEmitter && superCall.arguments.length > 0)
|
|
5319
|
+
lowerer.unsupported("SC1090", superCall, "EventEmitter constructor options ('captureRejections')");
|
|
5320
|
+
if (base.builtinStream) {
|
|
5321
|
+
statements.push(...lowerStreamSuperCall(lowerer, info, base, superCall.arguments, thisLocal, loc, superCall));
|
|
4491
5322
|
}
|
|
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;
|
|
5323
|
+
else {
|
|
5324
|
+
const surplusSpread = forward === undefined && !base.builtinError && !base.builtinEmitter &&
|
|
5325
|
+
base.ctorParams.length === 0 && superCall.arguments.some(ts.isSpreadElement);
|
|
5326
|
+
if (surplusSpread) {
|
|
5327
|
+
const evaluated = lowerer.completeArgs(superCall.arguments, [{ mode: "dynRest", type: DYN }], loc, superCall);
|
|
5328
|
+
for (const value of evaluated)
|
|
5329
|
+
statements.push({ kind: "exprStmt", expr: value, loc });
|
|
4518
5330
|
}
|
|
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");
|
|
5331
|
+
const surplus = forward === undefined && !base.builtinError && !base.builtinEmitter &&
|
|
5332
|
+
!base.ctorParams.some((param) => param.mode === "rest" || param.mode === "dynRest" || param.mode === "islandRest") &&
|
|
5333
|
+
!superCall.arguments.some(ts.isSpreadElement) && superCall.arguments.length > base.ctorParams.length;
|
|
5334
|
+
let args = surplusSpread ? [] : forward !== undefined ? forward : base.builtinError
|
|
5335
|
+
? lowerer.errorConstructorArgs(superCall.arguments, loc, superCall) : base.builtinEmitter ? []
|
|
5336
|
+
: lowerer.completeArgs(surplus ? superCall.arguments.slice(0, base.ctorParams.length) : superCall.arguments, base.ctorParams, loc, superCall);
|
|
5337
|
+
if (surplus) {
|
|
5338
|
+
args = args.map((arg) => {
|
|
5339
|
+
const local = lowerer.declareHiddenLocal("%superArgument", arg.type);
|
|
5340
|
+
statements.push({ kind: "varDecl", localId: local.id, init: arg, loc });
|
|
5341
|
+
return { kind: "varRef", localId: local.id, type: arg.type, loc };
|
|
5342
|
+
});
|
|
5343
|
+
for (const argument of superCall.arguments.slice(base.ctorParams.length)) {
|
|
5344
|
+
statements.push({ kind: "exprStmt", expr: lowerer.lowerExpr(argument), loc: locOf(argument) });
|
|
5345
|
+
}
|
|
4538
5346
|
}
|
|
5347
|
+
statements.push(lowerer.superCallStmt(info, thisLocal, args, loc));
|
|
4539
5348
|
}
|
|
4540
5349
|
}
|
|
5350
|
+
statements.push({ kind: "assign", localId: initialized.id, value: { kind: "boolLit", value: true, type: BOOL, loc }, loc });
|
|
5351
|
+
delete context.superInitialized;
|
|
5352
|
+
try {
|
|
5353
|
+
statements.push(...lowerer.fieldInitStmts(info, thisLocal), ...paramPropInitStmts(lowerer, info, thisLocal));
|
|
5354
|
+
}
|
|
5355
|
+
finally {
|
|
5356
|
+
context.superInitialized = initialized;
|
|
5357
|
+
}
|
|
5358
|
+
const result = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
|
|
5359
|
+
return { kind: "seqExpr", stmts: statements.map((statement) => statement.kind === "runtimeFence"
|
|
5360
|
+
? { kind: "exprStmt", expr: nodeThrowExpr(0, statement.code, statement.message, DYN, statement.loc), loc: statement.loc }
|
|
5361
|
+
: statement), result, type: result.type, loc };
|
|
5362
|
+
};
|
|
5363
|
+
try {
|
|
5364
|
+
const result = lowerer.lowerStmts(info.ctor.body.statements);
|
|
5365
|
+
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) });
|
|
5366
|
+
if (!superSeen)
|
|
5367
|
+
lowerer.pushDiag(unsupportedDiag("SC1090", locOf(info.ctor), "derived constructors without a lowered super() call"));
|
|
5368
|
+
return [{ kind: "varDecl", localId: initialized.id, init: { kind: "boolLit", value: false, type: BOOL, loc: locOf(info.ctor) }, loc: locOf(info.ctor) }, ...result];
|
|
4541
5369
|
}
|
|
4542
5370
|
finally {
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
5371
|
+
if (previous)
|
|
5372
|
+
context.superCall = previous;
|
|
5373
|
+
else
|
|
5374
|
+
delete context.superCall;
|
|
5375
|
+
if (previousInitialized)
|
|
5376
|
+
context.superInitialized = previousInitialized;
|
|
5377
|
+
else
|
|
5378
|
+
delete context.superInitialized;
|
|
4550
5379
|
}
|
|
4551
|
-
return out;
|
|
4552
5380
|
}
|
|
4553
5381
|
/** `super(args)` → direct call of the base constructor with the SAME
|
|
4554
5382
|
* `this` (upcast; retained by the varRef read — the callee owns and
|
|
@@ -4564,7 +5392,10 @@ export function superCallStmt(lowerer, info, thisLocal, args, loc) {
|
|
|
4564
5392
|
if (info.callableBase)
|
|
4565
5393
|
return {
|
|
4566
5394
|
kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.classSuper",
|
|
4567
|
-
args: [{ kind: "
|
|
5395
|
+
args: [info.localClass ? { kind: "libCall", fn: "dyn.getPrototype", args: [
|
|
5396
|
+
{ kind: "dynFrom", value: { kind: "fieldGet", obj: thisRef, className: info.def.name,
|
|
5397
|
+
field: `%classEnvironment:${info.def.name}`, type: { kind: "classval", className: info.def.name }, loc }, type: DYN, loc },
|
|
5398
|
+
], type: DYN, loc } : { kind: "varRef", localId: info.callableBase.constructorId, type: DYN, loc },
|
|
4568
5399
|
lowerer.coerceToExpected(thisRef, DYN), args[0]], type: VOID, loc }, loc,
|
|
4569
5400
|
};
|
|
4570
5401
|
if (base.builtinError) {
|
|
@@ -4682,6 +5513,16 @@ export function lowerSuperMethodCall(lowerer, call, access) {
|
|
|
4682
5513
|
lowerer.unsupported("SC1080", access);
|
|
4683
5514
|
const loc = locOf(call);
|
|
4684
5515
|
const thisRef = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
|
|
5516
|
+
if (lowerer.prototypeMethodAccesses.has(access.name.text)) {
|
|
5517
|
+
const prototype = classPrototypeData(lowerer, cls.base, loc);
|
|
5518
|
+
if (prototype) {
|
|
5519
|
+
const callee = { kind: "dynKeyGet", value: prototype,
|
|
5520
|
+
key: { kind: "strLit", value: access.name.text, type: STRING, loc }, type: DYN, loc };
|
|
5521
|
+
const value = { kind: "dynCall", callee, receiver: lowerer.coerceToExpected(thisRef, DYN),
|
|
5522
|
+
calleeName: access.getText(), args: call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc };
|
|
5523
|
+
return found.sig.ret.kind === "void" ? value : lowerer.coerceToExpected(value, found.sig.ret);
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
4685
5526
|
if (found.declarer.builtinError) {
|
|
4686
5527
|
return { kind: "libCall", fn: "error.toString", args: [lowerer.upcastTo(thisRef, "%Error")], type: STRING, loc };
|
|
4687
5528
|
}
|
|
@@ -4865,6 +5706,11 @@ function lowerProgramClassNew(lowerer, expr, declaredInfo, loc) {
|
|
|
4865
5706
|
const args = lowerer.inheritsBuiltinErrorCtor(info)
|
|
4866
5707
|
? lowerer.errorConstructorArgs(expr.arguments ?? [], loc, expr)
|
|
4867
5708
|
: lowerer.completeArgs(expr.arguments ?? [], info.ctorParams, loc, expr);
|
|
5709
|
+
for (let current = info; current; current = current.base) {
|
|
5710
|
+
if (current.def.baseValueGlobal)
|
|
5711
|
+
return { kind: "newValue", callee: classValueRef(lowerer, info, expr),
|
|
5712
|
+
args, type: { kind: "object", className: info.def.name }, loc };
|
|
5713
|
+
}
|
|
4868
5714
|
return {
|
|
4869
5715
|
kind: "new",
|
|
4870
5716
|
className: info.def.name,
|
|
@@ -4886,12 +5732,32 @@ function assignedThisFieldType(lowerer, expr) {
|
|
|
4886
5732
|
}
|
|
4887
5733
|
export function lowerNew(lowerer, expr) {
|
|
4888
5734
|
const loc = locOf(expr);
|
|
5735
|
+
let intlConstructor = expr.expression;
|
|
5736
|
+
if (ts.isIdentifier(intlConstructor)) {
|
|
5737
|
+
const symbol = lowerer.resolveValueSymbol(intlConstructor);
|
|
5738
|
+
const declaration = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
|
5739
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer)
|
|
5740
|
+
intlConstructor = declaration.initializer;
|
|
5741
|
+
}
|
|
5742
|
+
const intl = ts.isPropertyAccessExpression(intlConstructor) ? lowerer.stdlibGlobalMember(intlConstructor, "Intl") : null;
|
|
5743
|
+
if (intl && intl !== "Segmenter")
|
|
5744
|
+
lowerer.noLowering(`new Intl.${intl}`, expr, undefined, lowerer.checker.getSymbolAtLocation(expr.expression));
|
|
4889
5745
|
const instanceConstructor = lowerInstanceConstructorNew(lowerer, expr);
|
|
4890
5746
|
if (instanceConstructor)
|
|
4891
5747
|
return instanceConstructor;
|
|
4892
5748
|
const objectFactory = lowerObjectFactoryNew(lowerer, expr);
|
|
4893
5749
|
if (objectFactory)
|
|
4894
5750
|
return objectFactory;
|
|
5751
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
5752
|
+
const symbol = lowerer.resolveValueSymbol(expr.expression);
|
|
5753
|
+
const declaration = symbol && lowerer.checker.declarationsOf(symbol).find((node) => ts.isFunctionDeclaration(node) && node.body !== undefined);
|
|
5754
|
+
if (declaration && ts.isFunctionDeclaration(declaration) && declaration.body) {
|
|
5755
|
+
const last = declaration.body.statements.at(-1);
|
|
5756
|
+
if (last && ts.isReturnStatement(last) && last.expression && ts.isObjectLiteralExpression(last.expression)) {
|
|
5757
|
+
lowerer.unsupported("SC1090", expr, "object-returning constructors that observe this or return mixed primitive/object values");
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
4895
5761
|
let selected = expr.expression;
|
|
4896
5762
|
while (ts.isParenthesizedExpression(selected))
|
|
4897
5763
|
selected = selected.expression;
|
|
@@ -5004,9 +5870,11 @@ export function lowerNew(lowerer, expr) {
|
|
|
5004
5870
|
if (info?.classDecorators?.valueGlobalId !== undefined) {
|
|
5005
5871
|
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
5872
|
}
|
|
5007
|
-
if (info)
|
|
5873
|
+
if (info && !info.localClass)
|
|
5008
5874
|
return lowerProgramClassNew(lowerer, expr, info, loc);
|
|
5009
|
-
|
|
5875
|
+
// A namespace export can hold a class returned by a factory.
|
|
5876
|
+
// Let the class-value path below preserve that constructor's
|
|
5877
|
+
// lexical environment instead of requiring a class declaration.
|
|
5010
5878
|
}
|
|
5011
5879
|
// `new B.C()` where B is an AMBIENT namespace (fundule merges
|
|
5012
5880
|
// included): Node evaluates the callee first and throws
|
|
@@ -5092,11 +5960,17 @@ export function lowerNew(lowerer, expr) {
|
|
|
5092
5960
|
if (agent)
|
|
5093
5961
|
return agent;
|
|
5094
5962
|
}
|
|
5095
|
-
if (ts.isIdentifier(expr.expression))
|
|
5963
|
+
if (ts.isIdentifier(expr.expression) || ts.isPropertyAccessExpression(expr.expression) && ts.isIdentifier(expr.expression.name) &&
|
|
5964
|
+
lowerer.isStdlibGlobal(expr.expression.expression, "globalThis")) {
|
|
5096
5965
|
// `import C = N.C; new C()` — the alias's own source-order guards
|
|
5097
5966
|
// (a no-op for every non-import= binding).
|
|
5098
|
-
|
|
5099
|
-
|
|
5967
|
+
if (ts.isIdentifier(expr.expression))
|
|
5968
|
+
fenceEarlyAliasUse(lowerer, expr.expression, expr);
|
|
5969
|
+
const identifier = ts.isIdentifier(expr.expression) ? expr.expression : expr.expression.name;
|
|
5970
|
+
const symbol = ts.isIdentifier(identifier) ? lowerer.resolveValueSymbol(identifier) : null;
|
|
5971
|
+
if (ts.isIdentifier(identifier) && lowerer.isStdlibGlobal(identifier, "Array")) {
|
|
5972
|
+
return lowerArrayConstructor(lowerer, expr, expr.arguments ?? []);
|
|
5973
|
+
}
|
|
5100
5974
|
if (isNativeProxyInitializer(lowerer, expr))
|
|
5101
5975
|
return lowerNativeProxy(lowerer, expr);
|
|
5102
5976
|
// `new Error(msg?)` and its standard subclasses: the
|
|
@@ -5307,20 +6181,10 @@ export function lowerNew(lowerer, expr) {
|
|
|
5307
6181
|
// types get their half named specifically instead of the component
|
|
5308
6182
|
// fence (SC2009, which names Map slots at value positions elsewhere).
|
|
5309
6183
|
// 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
|
-
};
|
|
6184
|
+
if (symbol?.name === "Object" && lowerer.isStdlibSymbol(symbol)) {
|
|
6185
|
+
const object = lowerObjectConstructor(lowerer, expr.arguments ?? [], loc);
|
|
6186
|
+
if (object)
|
|
6187
|
+
return object;
|
|
5324
6188
|
}
|
|
5325
6189
|
if (symbol?.name === "Array" && lowerer.isStdlibSymbol(symbol)) {
|
|
5326
6190
|
return lowerArrayConstructor(lowerer, expr, expr.arguments ?? []);
|
|
@@ -5440,6 +6304,10 @@ export function lowerNew(lowerer, expr) {
|
|
|
5440
6304
|
if (seeded)
|
|
5441
6305
|
return seeded;
|
|
5442
6306
|
}
|
|
6307
|
+
if (!mapped && isJsSourceFile(expr.getSourceFile()) && (expr.arguments?.length ?? 0) <= 1 && !expr.arguments?.some(ts.isSpreadElement)) {
|
|
6308
|
+
return { kind: "libCall", fn: "dyn.nativeSetNew", args: [expr.arguments?.[0]
|
|
6309
|
+
? lowerer.lowerExprExpecting(expr.arguments[0], DYN) : dynUndefinedExpr(loc)], type: DYN, loc };
|
|
6310
|
+
}
|
|
5443
6311
|
// JavaScript's identity-Set idiom: `new Set([setTimeout, atob,
|
|
5444
6312
|
// ...])` — the element TYPE (a union of stdlib signatures) has no
|
|
5445
6313
|
// mapping, but the element VALUES all lower to identity tokens
|
|
@@ -5583,7 +6451,7 @@ export function lowerNew(lowerer, expr) {
|
|
|
5583
6451
|
Proxy: "native Proxy construction requires checked-native plain targets and handlers",
|
|
5584
6452
|
Function: "runtime code generation cannot be compiled ahead of time (the eval stance) — write the function",
|
|
5585
6453
|
};
|
|
5586
|
-
lowerer.noLowering(`new ${expr.expression.text}`, expr, ctorHints[expr.expression.text], symbol);
|
|
6454
|
+
lowerer.noLowering(`new ${expr.expression.text}`, expr, ctorHints[ts.isIdentifier(expr.expression) ? expr.expression.text : expr.expression.name.text], symbol);
|
|
5587
6455
|
}
|
|
5588
6456
|
}
|
|
5589
6457
|
// `new crypto.X509Certificate(data)` — the Dirent-style data record:
|
|
@@ -5696,6 +6564,8 @@ export function lowerNew(lowerer, expr) {
|
|
|
5696
6564
|
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
6565
|
}
|
|
5698
6566
|
const callee = lowerer.lowerExpr(expr.expression);
|
|
6567
|
+
if (callee.type.kind === "dyn")
|
|
6568
|
+
return lowerCheckedConstructor(lowerer, expr, callee);
|
|
5699
6569
|
if (callee.type.kind !== "classval")
|
|
5700
6570
|
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
5701
6571
|
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
@@ -5720,6 +6590,10 @@ export function lowerNew(lowerer, expr) {
|
|
|
5720
6590
|
if (calleeT?.kind === "union") {
|
|
5721
6591
|
const def = lowerer.unions.get(calleeT.unionId);
|
|
5722
6592
|
if (def?.arms.some((a) => a.kind === "classval")) {
|
|
6593
|
+
if (def.arms.every((arm) => arm.kind === "classval" || isJsSourceFile(expr.getSourceFile()) && isUnitType(arm)) && lowerer.dynConvertible(calleeT)) {
|
|
6594
|
+
const callee = lowerer.lowerExprExpecting(expr.expression, DYN);
|
|
6595
|
+
return lowerCheckedConstructor(lowerer, expr, callee);
|
|
6596
|
+
}
|
|
5723
6597
|
lowerer.unsupported("SC1090", expr, "constructing through a union of class values (annotate the slot with the common constructor type — e.g. `new () => Base` — or narrow first)");
|
|
5724
6598
|
}
|
|
5725
6599
|
}
|
|
@@ -5745,8 +6619,22 @@ export function lowerNew(lowerer, expr) {
|
|
|
5745
6619
|
}
|
|
5746
6620
|
}
|
|
5747
6621
|
}
|
|
6622
|
+
// Checked construction supports emitted classes and builtin typed arrays.
|
|
6623
|
+
const callee = tryLowerExpression(lowerer, expr.expression);
|
|
6624
|
+
if (callee && (callee.type.kind === "dyn" || (callee.type.kind === "func" || callee.type.kind === "classval") && lowerer.dynConvertible(callee.type))) {
|
|
6625
|
+
return lowerCheckedConstructor(lowerer, expr, lowerer.coerceToExpected(callee, DYN));
|
|
6626
|
+
}
|
|
5748
6627
|
lowerer.unsupported("SC1090", expr, "constructing values other than classes declared in the program");
|
|
5749
6628
|
}
|
|
6629
|
+
function lowerCheckedConstructor(lowerer, expr, callee) {
|
|
6630
|
+
const loc = locOf(expr);
|
|
6631
|
+
const args = expr.arguments ?? [];
|
|
6632
|
+
if (args.some(ts.isSpreadElement)) {
|
|
6633
|
+
const packed = lowerer.completeArgs(args, [{ type: DYN, mode: "dynRest" }], loc, expr)[0];
|
|
6634
|
+
return checkedClassConstructionPacked(lowerer, callee, packed, loc);
|
|
6635
|
+
}
|
|
6636
|
+
return checkedClassConstruction(lowerer, callee, args.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), loc);
|
|
6637
|
+
}
|
|
5750
6638
|
/** A getter/setter invocation over an accessor target's receiver — the
|
|
5751
6639
|
* same whole-program devirtualization as method calls: a virtualCall
|
|
5752
6640
|
* when some strict subclass of the receiver's static class overrides
|