@scriptc/compiler 0.2.0 → 0.2.2

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