@scriptc/compiler 0.2.0 → 0.2.1

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