@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
@@ -1,4 +1,5 @@
1
1
  import { InternalCompilerError } from "../../errors.js";
2
+ import { SYMBOL_T } from "../../ir/ir.js";
2
3
  import { literalValues } from "../literal-values.js";
3
4
  import { literalUnionArm } from "../union-discriminants.js";
4
5
  /* Expression lowering: the expression dispatch (lowerExpr), literals
@@ -11,13 +12,14 @@ import * as ts from "../ts7/adapter.js";
11
12
  import { dirname } from "node:path";
12
13
  import * as posix from "node:path/posix";
13
14
  import { pathToFileURL } from "node:url";
15
+ import { captureContextArguments } from "./function-context.js";
14
16
  import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
15
17
  import { wasiGuestPath } from "../../wasi-paths.js";
16
18
  import { BIGINT_T, BYTES_ELEMENT_NAME, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canDynCheckTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionContainerArmsOk } from "../../ir/ir.js";
17
19
  import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
18
20
  import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STRING_INDEX_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
19
21
  import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
20
- import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
22
+ import { PoisonError, dynFallbackType, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
21
23
  import { lowerCollectionSpread, lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
22
24
  import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
23
25
  import { lowerOptionalStringIndex } from "./string-index.js";
@@ -29,13 +31,14 @@ import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUn
29
31
  import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
30
32
  import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
31
33
  import { findGenericMethodOn, lowerStaticFieldRead, staticFieldWriteTarget, storedClassValueType } from "./lower-classes.js";
34
+ import { hasRuntimeStatics } from "./class-runtime-statics.js";
32
35
  import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
33
36
  import { mixinFnOfCallee } from "./lower-mixins.js";
34
37
  import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion, withUnitArm } from "../type-mapper.js";
35
38
  import { lowerYield } from "./lower-generators.js";
36
- import { errorToStringCall } from "./error-methods.js";
39
+ import { errorPropertyRead, errorPropertyWrite, errorToStringCall } from "./error-methods.js";
37
40
  import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "./lower-stream.js";
38
- import { countedFor, numLit, varRef } from "../../ir/build.js";
41
+ import { countedFor, numLit, strLit, varRef } from "../../ir/build.js";
39
42
  import { unionWideningTags } from "../../ir/analysis.js";
40
43
  import { isSafeToDiscard, isSafeToMoveConditionEarlier, isSafeToRepeat } from "./expressions/evaluation-safety.js";
41
44
  import { globalSymbolKey } from "./expressions/global-symbols.js";
@@ -51,9 +54,14 @@ import { classSymbolKeyOf } from "./symbol-fields.js";
51
54
  import { lowerClassMethodValue } from "./class-method-values.js";
52
55
  import { classInstanceOf } from "./class-dynamic-dispatch.js";
53
56
  import { lowerGlobalValue } from "./lower-global-value.js";
54
- import { lowerClassPrototypeData } from "./class-prototypes.js";
55
- import { lowerArrayIsArrayValue } from "./lower-builtin-values.js";
57
+ import { lowerClassPrototypeData, lowerClassPrototypeComputedAssignment } from "./class-prototypes.js";
58
+ import { isClassCallback } from "./class-callbacks.js";
59
+ import { builtinPrototypeMethod, lowerArrayIsArrayValue, lowerCheckedPredicateValue, lowerNumberParserValue, lowerObjectAssignValue, lowerStringCodesValue } from "./lower-builtin-values.js";
60
+ import { lowerArrayFromValue } from "./lower-containers.js";
56
61
  import { lowerPerfHooksTypeof } from "./lower-builtins.js";
62
+ import { jsBindingHasOpenWrites } from "./lower-stmts.js";
63
+ import { checkedClassInstanceOf } from "./class-construction.js";
64
+ import { lowerModuleNamespaceElement } from "./module-namespace-elements.js";
57
65
  /** A template piece's RAW text (String.raw's contract: escapes stay
58
66
  * characters). 7's client AST ships no rawText at runtime (the typing
59
67
  * declares it; the serialized node data omits it), so the raw span comes
@@ -580,6 +588,8 @@ function lowerExprInner(lowerer, expr) {
580
588
  if (ts.isTypeOfExpression(expr)) {
581
589
  if (lowerer.isStdlibGlobal(expr.expression, "process"))
582
590
  return { kind: "strLit", value: "object", type: STRING, loc };
591
+ if (lowerer.isStdlibGlobal(expr.expression, "fetch"))
592
+ return { kind: "strLit", value: "function", type: STRING, loc };
583
593
  const performance = lowerPerfHooksTypeof(lowerer, expr.expression);
584
594
  if (performance)
585
595
  return performance;
@@ -704,8 +714,10 @@ function lowerExprInner(lowerer, expr) {
704
714
  // `arguments` in a variadic JS function (the rest-marked form): the
705
715
  // synthetic trailing dyn-array param — lambdaSignature marked the
706
716
  // type and lowerLambda declared the local.
707
- if (expr.text === "arguments" && lowerer.ctx.argumentsLocal) {
708
- return { kind: "varRef", localId: lowerer.ctx.argumentsLocal.id, type: DYN, loc };
717
+ if (expr.text === "arguments") {
718
+ const binding = captureContextArguments(lowerer.fnStack);
719
+ if (binding)
720
+ return { kind: "varRef", localId: binding.id, type: DYN, loc };
709
721
  }
710
722
  // A compiler-projected adapter as a VALUE: the adapter has no
711
723
  // runtime function object (its calls rewrite directly) — call it
@@ -769,7 +781,7 @@ function lowerExprInner(lowerer, expr) {
769
781
  if (use?.kind === "element") {
770
782
  const key = runtimeOptionalElementKey(use.access.argumentExpression);
771
783
  if (key === null) {
772
- lowerer.unsupported("SC1090", use.access, "a computed read from a runtime-optional capture (use a literal key)");
784
+ return varRef(local.id, local.type, loc);
773
785
  }
774
786
  return runtimeOptionalReceiverRead(lowerer, expr, local, key, loc);
775
787
  }
@@ -1052,14 +1064,38 @@ function lowerExprInner(lowerer, expr) {
1052
1064
  // type-mapper.ts pins the one concrete signature `(value: string) =>
1053
1065
  // primitive`; direct calls `String(x)` never reach here — the call
1054
1066
  // lowering intercepts them with the wider static coercions).
1055
- const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile()) &&
1056
- expr.parent !== undefined &&
1057
- ts.isArrowFunction(expr.parent) &&
1058
- expr.parent.body === expr;
1059
- if ((expr.text === "String" || expr.text === "Number" || expr.text === "Boolean") &&
1060
- (!isJsSourceFile(expr.getSourceFile()) || jsPrimitiveCtorSelector) &&
1061
- lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
1062
- return primitiveCtorClosure(lowerer, expr.text, loc, jsPrimitiveCtorSelector);
1067
+ const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile());
1068
+ const primitiveName = stdlibGlobalNameOf(lowerer, expr);
1069
+ if (primitiveName === "String" || primitiveName === "Number" || primitiveName === "Boolean" || primitiveName === "BigInt") {
1070
+ return primitiveCtorClosure(lowerer, primitiveName, loc, jsPrimitiveCtorSelector);
1071
+ }
1072
+ if (lowerer.isStdlibGlobal(expr, "ArrayBuffer"))
1073
+ return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
1074
+ if (isJsSourceFile(expr.getSourceFile()) && lowerer.isStdlibGlobal(expr, "Array"))
1075
+ return { kind: "libCall", fn: "dyn.arrayConstructor", args: [], type: DYN, loc };
1076
+ if (Object.values(BYTES_ELEMENT_NAME).includes(expr.text) && lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
1077
+ return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: expr.text, type: STRING, loc }], type: DYN, loc };
1078
+ }
1079
+ if ((expr.text === "encodeURIComponent" || expr.text === "encodeURI" || expr.text === "decodeURIComponent" || expr.text === "decodeURI") && lowerer.isStdlibGlobal(expr, expr.text)) {
1080
+ const name = `%builtin.${expr.text}`;
1081
+ const type = funcOf([DYN], STRING);
1082
+ if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
1083
+ const value = { kind: "libCall", fn: "dyn.toStringCoerce", args: [varRef("value", DYN, loc)], type: STRING, loc };
1084
+ const fn = expr.text === "encodeURIComponent" ? "str.encodeUriComponent" : expr.text === "encodeURI" ? "str.encodeUri" : expr.text === "decodeURI" ? "str.decodeUri" : "str.decodeUriComponent";
1085
+ lowerer.liftedFns.push({ name, params: [{ localId: "value", name: "value", type: DYN }],
1086
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: STRING,
1087
+ body: [{ kind: "return", value: { kind: "libCall", fn, args: [value], type: STRING, loc }, loc }], loc });
1088
+ }
1089
+ return { kind: "closure", fnName: name, captures: [], type, loc };
1090
+ }
1091
+ if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "crypto"))
1092
+ return { kind: "libCall", fn: "crypto.native", args: [], type: DYN, loc };
1093
+ const nativeAlias = stdlibGlobalNameOf(lowerer, expr);
1094
+ if (nativeAlias === "Buffer" && expr.text !== "Buffer") {
1095
+ return { kind: "strLit", value: "[builtin Buffer]", type: STRING, loc };
1096
+ }
1097
+ if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "fetch")) {
1098
+ return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
1063
1099
  }
1064
1100
  // The lib fence's IDENTIFIER chokepoint: the real standard library
1065
1101
  // resolves names the old minimal ambient world never declared
@@ -1260,8 +1296,13 @@ function lowerExprInner(lowerer, expr) {
1260
1296
  // `this` in plain nested functions is already a tsc error under
1261
1297
  // noImplicitThis, so the generic scope walk here is preflight-safe.
1262
1298
  const local = lowerer.resolveThis();
1263
- if (local)
1264
- return { kind: "varRef", localId: local.id, type: local.type, loc };
1299
+ if (local) {
1300
+ const value = { kind: "varRef", localId: local.id, type: local.type, loc };
1301
+ const initialized = lowerer.ctx.superInitialized;
1302
+ if (initialized)
1303
+ return { kind: "seqExpr", stmts: [{ kind: "if", cond: { kind: "unary", op: "!", operand: { kind: "varRef", localId: initialized.id, type: BOOL, loc }, type: BOOL, loc }, then: [{ kind: "exprStmt", expr: nodeThrowExpr(5, "", "Must call super constructor in derived class before accessing 'this' or returning from derived constructor", DYN, loc), loc }], else_: null, loc }], result: value, type: value.type, loc };
1304
+ return value;
1305
+ }
1265
1306
  // `this` in a plain JS FUNCTION (not a method): the AMBIENT
1266
1307
  // RECEIVER (libCall dyn.this — scr_dyn_this_get). Firing sites bind
1267
1308
  // the emitting handle around each listener call (Node calls
@@ -1456,6 +1497,12 @@ function lowerExprInner(lowerer, expr) {
1456
1497
  const prototypeData = lowerClassPrototypeData(lowerer, expr);
1457
1498
  if (prototypeData)
1458
1499
  return prototypeData;
1500
+ if (ts.isPropertyAccessExpression(expr.expression) && expr.expression.name.text === "prototype") {
1501
+ const prototype = lowerClassPrototypeData(lowerer, expr.expression);
1502
+ if (prototype)
1503
+ return { kind: "dynKeyGet", value: prototype,
1504
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
1505
+ }
1459
1506
  // `super.x`: the base chain's GETTER, called directly (super
1460
1507
  // dispatch is static in JS — never through the dynamic class).
1461
1508
  // super.method() calls are routed at the call site; a bare super
@@ -1501,6 +1548,14 @@ function lowerExprInner(lowerer, expr) {
1501
1548
  ["iterator", "asyncIterator", "toStringTag", "toPrimitive", "hasInstance", "isConcatSpreadable", "match", "matchAll", "replace", "search", "split", "species", "unscopables", "dispose", "asyncDispose"].includes(expr.name.text)) {
1502
1549
  return { kind: "libCall", fn: "sym.wellKnown", args: [{ kind: "strLit", value: expr.name.text, type: STRING, loc }], type: { kind: "symbol" }, loc };
1503
1550
  }
1551
+ if (!lowerer.dynamic && lowerer.stdlibGlobalMember(expr, "Object") === "freeze") {
1552
+ const name = "%builtin.Object.freeze";
1553
+ if (!lowerer.liftedFns.some((fn) => fn.name === name))
1554
+ lowerer.liftedFns.push({ name,
1555
+ params: [{ localId: "value", name: "value", type: DYN }], locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: DYN,
1556
+ body: [{ kind: "return", value: { kind: "libCall", fn: "dyn.freeze", args: [varRef("value", DYN, loc)], type: DYN, loc }, loc }], loc });
1557
+ return { kind: "closure", fnName: name, captures: [], type: funcOf([DYN], DYN), loc };
1558
+ }
1504
1559
  if (lowerer.stdlibGlobalMember(expr, "globalThis") === "console") {
1505
1560
  return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
1506
1561
  }
@@ -1586,15 +1641,6 @@ function lowerExprInner(lowerer, expr) {
1586
1641
  }
1587
1642
  return read;
1588
1643
  }
1589
- // `m.groups` on a match result whose regex is statically known:
1590
- // the compile-time record projection over the honest slice (or
1591
- // Node's undefined when the pattern has no named groups) — see
1592
- // lowerMatchGroupsRead.
1593
- {
1594
- const g = lowerMatchGroupsRead(lowerer, expr);
1595
- if (g !== null)
1596
- return g;
1597
- }
1598
1644
  // `m.index` on a for-of-over-matchAll binding reads the companion-
1599
1645
  // index array at this iteration's cursor — always a number: every
1600
1646
  // drained row matched, so the lib's `.index` is never undefined
@@ -1612,6 +1658,25 @@ function lowerExprInner(lowerer, expr) {
1612
1658
  };
1613
1659
  }
1614
1660
  }
1661
+ if ((expr.name.text === "index" || expr.name.text === "input") && !expr.questionDotToken) {
1662
+ const represented = tryLowerExpression(lowerer, expr.expression);
1663
+ if (represented && isMatchSliceType(lowerer, represented.type) &&
1664
+ (lowerer.isStdlibMember(expr) || isJsSourceFile(expr.getSourceFile()))) {
1665
+ const recv = represented;
1666
+ const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(recv, DYN),
1667
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
1668
+ return lowerer.maybeNarrow(read, expr);
1669
+ }
1670
+ }
1671
+ // `m.groups` on a match result whose regex is statically known:
1672
+ // the compile-time record projection over the honest slice (or
1673
+ // Node's undefined when the pattern has no named groups) — see
1674
+ // lowerMatchGroupsRead.
1675
+ {
1676
+ const g = lowerMatchGroupsRead(lowerer, expr);
1677
+ if (g !== null)
1678
+ return g;
1679
+ }
1615
1680
  // `arguments.length` in a TYPED function whose signature is
1616
1681
  // FIXED-ARITY (no optional/default/rest parameters): tsc enforces
1617
1682
  // exact arity at every call site and call/apply/bind indirection is
@@ -1833,6 +1898,12 @@ function lowerExprInner(lowerer, expr) {
1833
1898
  if (canonical !== null) {
1834
1899
  if (!lowerer.dynamic && canonical === "globalThis")
1835
1900
  return lowerGlobalValue(lowerer, expr);
1901
+ if (!lowerer.dynamic && canonical === "ArrayBuffer")
1902
+ return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
1903
+ if (!lowerer.dynamic && canonical === "fetch")
1904
+ return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
1905
+ if (!lowerer.dynamic && Object.values(BYTES_ELEMENT_NAME).includes(canonical))
1906
+ return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: canonical, type: STRING, loc }], type: DYN, loc };
1836
1907
  return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
1837
1908
  }
1838
1909
  }
@@ -1876,6 +1947,13 @@ function lowerExprInner(lowerer, expr) {
1876
1947
  : { kind: "strIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc };
1877
1948
  }
1878
1949
  }
1950
+ if (isJsSourceFile(expr.getSourceFile()) && expr.name.text === "constructor") {
1951
+ const receiver = tryLowerExpression(lowerer, expr.expression);
1952
+ if (receiver?.type.kind === "dyn" || receiver?.type.kind === "object" && !lowerer.classes.get(receiver.type.className)?.fields.has("constructor")) {
1953
+ return { kind: "dynKeyGet", value: lowerer.coerceToExpected(receiver, DYN),
1954
+ key: { kind: "strLit", value: "constructor", type: STRING, loc }, type: DYN, loc };
1955
+ }
1956
+ }
1879
1957
  // `f.name` / `f.length` / own properties on a FUNCTION-typed JS
1880
1958
  // value (the mustCall wrapper's function-instance members): read
1881
1959
  // through the dyn box — the closure's own-property table answers
@@ -1921,10 +1999,14 @@ function lowerExprInner(lowerer, expr) {
1921
1999
  // is an ordinary recordGet — maybeNarrow then bridges the union slot
1922
2000
  // to the checker's narrowed arm, exactly like a union field read.
1923
2001
  if (expr.name.text === "done" || expr.name.text === "value") {
1924
- const recvSym = lowerer.typeOf(expr.expression).getSymbol();
1925
- if ((recvSym?.name === "IteratorYieldResult" || recvSym?.name === "IteratorReturnResult") &&
1926
- lowerer.checker.declarationsOf(recvSym).some((d) => ts.isInterfaceDeclaration(d) && lowerer.isStdlibFile(d.getSourceFile()))) {
2002
+ const receiverType = lowerer.typeOf(expr.expression);
2003
+ const recvSym = receiverType.getAliasSymbol()?.name === "IteratorResult" ? receiverType.getAliasSymbol() : receiverType.getSymbol();
2004
+ if ((recvSym?.name === "IteratorResult" || recvSym?.name === "IteratorYieldResult" || recvSym?.name === "IteratorReturnResult") &&
2005
+ lowerer.checker.declarationsOf(recvSym).some((d) => (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d)) && lowerer.isStdlibFile(d.getSourceFile()))) {
1927
2006
  const recv = lowerer.lowerExpr(expr.expression);
2007
+ if (recv.type.kind === "dyn")
2008
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: recv,
2009
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
1928
2010
  if (recv.type.kind === "record") {
1929
2011
  const shape = lowerer.shapes.get(recv.type.shapeId);
1930
2012
  const field = shape?.fields.find((f) => f.name === expr.name.text);
@@ -2021,10 +2103,12 @@ function lowerExprInner(lowerer, expr) {
2021
2103
  // on undefined/null receivers unless an earlier `?.` guards the
2022
2104
  // chain. Scalar-narrowed occurrences bridge via maybeNarrow's
2023
2105
  // validated dynCheck as usual.
2024
- if (recvLowered.type.kind === "dyn") {
2106
+ if (recvLowered.type.kind === "dyn" || isJsSourceFile(expr.getSourceFile()) &&
2107
+ (["f64", "bool", "bigint", "string", "symbol"].includes(recvLowered.type.kind) ||
2108
+ recvLowered.type.kind === "union" && lowerer.dynConvertible(recvLowered.type))) {
2025
2109
  const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
2026
2110
  const opt = hasOptionalChainGuard(expr.expression);
2027
- return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recvLowered, type: DYN, loc }, expr);
2111
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(recvLowered, DYN), type: DYN, loc }, expr);
2028
2112
  }
2029
2113
  // The lowered receiver is a RECORD the checker spelled wider —
2030
2114
  // `s.match(re).groups.key` in a JS file: the checker says
@@ -2074,6 +2158,16 @@ function lowerExprInner(lowerer, expr) {
2074
2158
  // reports a class. Its property bag must remain reachable, while
2075
2159
  // an absent element takes the normal checked-value TypeError.
2076
2160
  const nativeReceiver = lowerer.stripUndefinedArm(recvLowered.type);
2161
+ if (ts.isPrivateIdentifier(expr.name) && recvLowered.type.kind === "object") {
2162
+ const field = lowerer.classes.get(recvLowered.type.className)?.fields.get(expr.name.text);
2163
+ if (field)
2164
+ return { kind: "fieldGet", obj: recvLowered, className: recvLowered.type.className,
2165
+ field: expr.name.text, type: field, loc };
2166
+ }
2167
+ if (nativeReceiver.kind === "object" && nativeReceiver.className === "%Error") {
2168
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
2169
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
2170
+ }
2077
2171
  if (isDynTypedRefType(nativeReceiver)) {
2078
2172
  const info = lowerer.classes.get(nativeReceiver.className);
2079
2173
  if (info && !info.def.runtime && !info.builtinError && !info.builtinEmitter && !info.builtinStream) {
@@ -2107,7 +2201,7 @@ function moduleFileName(lowerer, sf) {
2107
2201
  : sf.fileName;
2108
2202
  }
2109
2203
  function importMetaProperty(lowerer, expr) {
2110
- if (expr.questionDotToken !== undefined || !ts.isMetaProperty(expr.expression))
2204
+ if (!ts.isMetaProperty(expr.expression))
2111
2205
  return null;
2112
2206
  const meta = expr.expression;
2113
2207
  if (meta.keywordToken !== ts.SyntaxKind.ImportKeyword || meta.name.text !== "meta")
@@ -2115,6 +2209,8 @@ function importMetaProperty(lowerer, expr) {
2115
2209
  const sf = expr.getSourceFile();
2116
2210
  const fileName = moduleFileName(lowerer, sf);
2117
2211
  switch (expr.name.text) {
2212
+ case "env":
2213
+ return dynUndefinedExpr(locOf(expr));
2118
2214
  case "url":
2119
2215
  return {
2120
2216
  kind: "strLit",
@@ -2398,7 +2494,13 @@ export function lowerTernary(lowerer, expr, expected) {
2398
2494
  thenRaw.type.kind === "func" && elseRaw.type.kind === "func" &&
2399
2495
  !typeEquals(thenRaw.type, elseRaw.type) &&
2400
2496
  lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
2401
- const type = callableJoin ? DYN :
2497
+ // JavaScript inference can discard an initially-undefined field from
2498
+ // a conditional's type even after that field acquired other values.
2499
+ // A checked arm must retain its actual representation at the join.
2500
+ const checkedJsJoin = isJsSourceFile(expr.getSourceFile()) &&
2501
+ (thenRaw.type.kind === "dyn" || elseRaw.type.kind === "dyn") &&
2502
+ lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
2503
+ const type = callableJoin || checkedJsJoin ? DYN :
2402
2504
  thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
2403
2505
  ? thenRaw.type
2404
2506
  : dynJoin
@@ -2452,8 +2554,57 @@ export function maybeNarrow(lowerer, expr, node) {
2452
2554
  // instead of misreading the payload (the dyn boundary's usual stance).
2453
2555
  // Object/array narrowings stay dyn-typed and keep their fences.
2454
2556
  if (expr.type.kind === "dyn") {
2557
+ // An inferred JS member's storage may be deliberately wider than its
2558
+ // declaration (prototype-selected buffers, null-initialized scratch
2559
+ // objects). Extract at the consuming operation, not at the read.
2560
+ if (isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node)) {
2561
+ const receiver = lowerer.mapTypeOf(lowerer.typeOf(node.expression));
2562
+ if (receiver?.kind === "object") {
2563
+ const info = lowerer.classes.get(receiver.className);
2564
+ if (info?.fields.get(node.name.text)?.kind === "dyn" || lowerer.findMethodOn(info ?? null, `get:${node.name.text}`)?.sig.ret.kind === "dyn")
2565
+ return expr;
2566
+ }
2567
+ if (receiver?.kind === "classval") {
2568
+ const info = lowerer.classes.get(receiver.className);
2569
+ if (info && hasRuntimeStatics(info))
2570
+ return expr;
2571
+ }
2572
+ }
2573
+ if (isJsSourceFile(node.getSourceFile()) &&
2574
+ (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node))) {
2575
+ let receiver = node.expression;
2576
+ while (ts.isPropertyAccessExpression(receiver) || ts.isElementAccessExpression(receiver) || ts.isParenthesizedExpression(receiver)) {
2577
+ if (ts.isPropertyAccessExpression(receiver)) {
2578
+ const owner = lowerer.mapTypeOf(lowerer.typeOf(receiver.expression));
2579
+ if (owner?.kind === "object" && lowerer.classes.get(owner.className)?.fields.get(receiver.name.text)?.kind === "dyn")
2580
+ return expr;
2581
+ }
2582
+ receiver = receiver.expression;
2583
+ }
2584
+ if (receiver.kind === ts.SyntaxKind.ThisKeyword && lowerer.resolveThis()?.type.kind === "dyn")
2585
+ return expr;
2586
+ if (ts.isIdentifier(receiver)) {
2587
+ const stored = lowerer.peekLocal(receiver)?.type ?? lowerer.globalOf(receiver)?.type;
2588
+ if (stored?.kind === "dyn")
2589
+ return expr;
2590
+ const symbol = lowerer.resolveValueSymbol(receiver);
2591
+ const bound = symbol && lowerer.implicitParamTypes?.get(symbol);
2592
+ if (bound && lowerer.mapTypeOf(bound)?.kind === "dyn")
2593
+ return expr;
2594
+ }
2595
+ }
2455
2596
  if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
2456
2597
  const symbol = lowerer.resolveValueSymbol(node);
2598
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) &&
2599
+ ts.isMethodDeclaration(decl.parent) && lowerer.checkedVirtualJsMethods.has(decl.parent)))
2600
+ return expr;
2601
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && ts.isConstructorDeclaration(decl.parent)))
2602
+ return expr;
2603
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isVariableDeclaration(decl) && jsBindingHasOpenWrites(lowerer, decl)))
2604
+ return expr;
2605
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && decl.initializer && !decl.type &&
2606
+ !/@(?:param|type)\b/.test(decl.getSourceFile().text.slice(decl.parent?.pos ?? decl.pos, decl.getStart()))))
2607
+ return expr;
2457
2608
  if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
2458
2609
  if (!ts.isParameter(decl) || decl.type)
2459
2610
  return false;
@@ -2512,6 +2663,19 @@ export function maybeNarrow(lowerer, expr, node) {
2512
2663
  }
2513
2664
  if (expr.type.kind !== "union")
2514
2665
  return expr;
2666
+ // Represented fields and index values can include undefined even when
2667
+ // checker inference or lib.d.ts declares strings (RegExp captures).
2668
+ // Only a declaration that represents the optional arm can provide a
2669
+ // trustworthy flow narrowing; otherwise consumers must check it.
2670
+ if ((expr.kind === "recordGet" || expr.kind === "recordKeyGet") &&
2671
+ (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node))) {
2672
+ const receiver = lowerer.mapTypeOf(lowerer.typeOf(node.expression));
2673
+ const shape = receiver?.kind === "record" ? lowerer.shapes.get(receiver.shapeId) : undefined;
2674
+ const declared = expr.kind === "recordGet" ? shape?.fields.find((field) => field.name === expr.field)?.type : shape?.indexValue;
2675
+ if (lowerer.armTag(expr.type.unionId, UNDEFINED_T) >= 0 &&
2676
+ declared && !(declared.kind === "union" && lowerer.armTag(declared.unionId, UNDEFINED_T) >= 0))
2677
+ return expr;
2678
+ }
2515
2679
  if (ts.isIdentifier(node)) {
2516
2680
  const symbol = lowerer.resolveValueSymbol(node);
2517
2681
  if (symbol && lowerer.unstableSwitchBindings.has(symbol))
@@ -3003,6 +3167,10 @@ function lowerNullishPair(lowerer, expr, loc, left) {
3003
3167
  if (fresh)
3004
3168
  return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
3005
3169
  const type = lowerer.irTypeOf(expr);
3170
+ if (type.kind === "dyn" && lowerer.dynConvertible(left.type)) {
3171
+ return { kind: "nullish", left: lowerer.coerceToExpected(left, DYN),
3172
+ right: lowerer.lowerExprExpecting(expr.right, DYN), type: DYN, loc };
3173
+ }
3006
3174
  if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
3007
3175
  const right = lowerer.lowerExprExpecting(expr.right, type);
3008
3176
  return { kind: "nullish", left, right, type, loc };
@@ -3073,6 +3241,8 @@ export function lowerCondition(lowerer, expr) {
3073
3241
  if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
3074
3242
  const isAnd = op === ts.SyntaxKind.AmpersandAmpersandToken;
3075
3243
  const left = lowerer.lowerCondition(e.left);
3244
+ if (left.kind === "boolLit" && left.value !== isAnd)
3245
+ return left;
3076
3246
  // The right operand evaluates only when the left already answered
3077
3247
  // (true for &&, false for ||) — aliased-typeof narrows the left
3078
3248
  // PROVES under that polarity hold while it lowers (`type ===
@@ -3089,6 +3259,8 @@ export function lowerCondition(lowerer, expr) {
3089
3259
  lowerer.runtimeOptionalLocals.add(lowerer.runtimeOptionalRootOf(runtimeOptionalLocal));
3090
3260
  }
3091
3261
  });
3262
+ if (left.kind === "boolLit")
3263
+ return right;
3092
3264
  return {
3093
3265
  kind: "logical",
3094
3266
  op: isAnd ? "&&" : "||",
@@ -3366,13 +3538,56 @@ export function eqComparableUnion(lowerer, unionId) {
3366
3538
  * the receiver type AND the ambient-file provenance of the member are
3367
3539
  * verified — the name alone proves nothing. */
3368
3540
  export function lowerIntrinsicProperty(lowerer, expr) {
3541
+ for (const name of ["String", "Number", "Boolean", "BigInt"]) {
3542
+ if (!lowerer.chainBlocked(expr) && lowerer.isStdlibGlobal(expr, name)) {
3543
+ return primitiveCtorClosure(lowerer, name, locOf(expr), isJsSourceFile(expr.getSourceFile()));
3544
+ }
3545
+ }
3546
+ if (lowerer.stdlibGlobalMember(expr, "Reflect") === "ownKeys") {
3547
+ const value = lowerer.lowerNativeCallableValue({
3548
+ fn: "dyn.ownKeys", params: [DYN], result: DYN, valueParams: [{ mode: "required", type: DYN }],
3549
+ }, "Reflect.ownKeys", locOf(expr));
3550
+ return { kind: "dynFrom", value, fnName: "ownKeys", type: DYN, loc: locOf(expr) };
3551
+ }
3369
3552
  if (lowerer.chainBlocked(expr))
3370
3553
  return null;
3554
+ const builtinPrototype = builtinPrototypeMethod(lowerer, expr);
3555
+ if (builtinPrototype)
3556
+ return { kind: "libCall", fn: "dyn.builtinMethod", args: [
3557
+ { kind: "strLit", value: builtinPrototype, type: STRING, loc: locOf(expr) },
3558
+ { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
3559
+ ], type: DYN, loc: locOf(expr) };
3560
+ if (ts.isPropertyAccessExpression(expr.expression) &&
3561
+ lowerer.stdlibGlobalMember(expr.expression, "Array") === "prototype") {
3562
+ return { kind: "dynKeyGet", value: { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr.expression) },
3563
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) }, type: DYN, loc: locOf(expr) };
3564
+ }
3565
+ if (expr.name.text === "apply" && ts.isPropertyAccessExpression(expr.expression) &&
3566
+ lowerer.stdlibGlobalMember(expr.expression, "Function") === "prototype")
3567
+ return { kind: "libCall", fn: "dyn.functionApply", args: [], type: DYN, loc: locOf(expr) };
3371
3568
  if (lowerer.stdlibGlobalMember(expr, "Array") === "prototype")
3372
3569
  return { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr) };
3570
+ if (lowerer.stdlibGlobalMember(expr, "Object") === "prototype")
3571
+ return { kind: "libCall", fn: "dyn.objectPrototype", args: [], type: DYN, loc: locOf(expr) };
3373
3572
  if (lowerer.stdlibGlobalMember(expr, "Array") === "isArray")
3374
3573
  return lowerArrayIsArrayValue(lowerer, locOf(expr));
3574
+ if (lowerer.stdlibGlobalMember(expr, "Array") === "from")
3575
+ return lowerArrayFromValue(lowerer, locOf(expr));
3576
+ const stringMember = lowerer.stdlibGlobalMember(expr, "String");
3577
+ if (stringMember === "fromCharCode" || stringMember === "fromCodePoint")
3578
+ return lowerStringCodesValue(lowerer, stringMember, locOf(expr));
3579
+ const numberMember = lowerer.stdlibGlobalMember(expr, "Number");
3580
+ if (numberMember === "parseInt" || numberMember === "parseFloat")
3581
+ return lowerNumberParserValue(lowerer, numberMember, locOf(expr));
3582
+ if (lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer")
3583
+ return lowerCheckedPredicateValue(lowerer, "Buffer.isBuffer", "buffer", locOf(expr));
3584
+ if (lowerer.stdlibGlobalMember(expr, "Date") === "now")
3585
+ return lowerer.lowerNativeCallableValue({
3586
+ fn: "date.now", params: [], result: F64, valueParams: [],
3587
+ }, "Date.now", locOf(expr));
3375
3588
  if (stdlibGlobalNameOf(lowerer, expr.expression) === "Object") {
3589
+ if (expr.name.text === "assign")
3590
+ return lowerObjectAssignValue(lowerer, locOf(expr));
3376
3591
  const fn = own(OBJECT_CALLABLE_VALUES, expr.name.text);
3377
3592
  if (fn)
3378
3593
  return lowerer.lowerNativeCallableValue(fn, `Object.${expr.name.text}`, locOf(expr));
@@ -3410,10 +3625,21 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3410
3625
  if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !tupleLengthOnArray && !lowerer.isStdlibMember(expr))
3411
3626
  return null;
3412
3627
  const name = expr.name.text;
3628
+ if (kind === "array" && name === "constructor" && isJsSourceFile(expr.getSourceFile())) {
3629
+ const receiver = lowerer.lowerExpr(expr.expression);
3630
+ if (receiver.type.kind === "dyn")
3631
+ return { kind: "dynKeyGet", value: receiver,
3632
+ key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, loc: locOf(expr) };
3633
+ const result = { kind: "libCall", fn: "dyn.arrayConstructor", args: [], type: DYN, loc: locOf(expr) };
3634
+ return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc: locOf(expr) }], result, type: DYN, loc: locOf(expr) };
3635
+ }
3413
3636
  const strictReceiver = (expected) => {
3414
3637
  const value = lowerer.lowerExpr(expr.expression);
3415
- if (value.type.kind === "dyn")
3638
+ if (value.type.kind === "dyn") {
3639
+ if (isJsSourceFile(expr.getSourceFile()) && name === "length")
3640
+ return value;
3416
3641
  return lowerer.coerceInto(expr.expression, value, expected);
3642
+ }
3417
3643
  return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
3418
3644
  };
3419
3645
  if (kind === "child") {
@@ -3517,7 +3743,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3517
3743
  // (the child.stdout pattern); a checker-narrowed read extracts
3518
3744
  // through maybeNarrow like any union member.
3519
3745
  if (name === "description") {
3520
- const receiver = lowerer.lowerExpr(expr.expression);
3746
+ const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), SYMBOL_T);
3521
3747
  const type = { kind: "union", unionId: lowerer.unions.intern([STRING, UNDEFINED_T]) };
3522
3748
  return { kind: "libCall", fn: "sym.desc", args: [receiver], type, loc };
3523
3749
  }
@@ -3585,6 +3811,9 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3585
3811
  lowerer.noLowering(`URLSearchParams.${name}`, expr, "get, getAll, set, append, delete, has, sort, size, toString(), forEach, and for-of iteration are the supported URLSearchParams members", lowerer.checker.getSymbolAtLocation(expr.name));
3586
3812
  }
3587
3813
  if (kind === "regex") {
3814
+ if (name === "lastIndex") {
3815
+ return { kind: "regexIntrinsic", method: "lastIndex", receiver: strictReceiver({ kind: "regex" }), args: [], type: F64, loc: locOf(expr) };
3816
+ }
3588
3817
  if (name === "source" || name === "flags") {
3589
3818
  const receiver = strictReceiver({ kind: "regex" });
3590
3819
  return { kind: "regexIntrinsic", method: name, receiver, args: [], type: STRING, loc: locOf(expr) };
@@ -3604,11 +3833,20 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3604
3833
  }
3605
3834
  if (kind === "bytes") {
3606
3835
  const loc = locOf(expr);
3836
+ if (name === "constructor") {
3837
+ const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3838
+ const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
3839
+ return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, receiver, DYN),
3840
+ key: { kind: "strLit", value: name, type: STRING, loc }, type: DYN, loc };
3841
+ }
3607
3842
  if (name === "length" || name === "byteLength") {
3608
3843
  const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3609
3844
  let receiver = expected?.kind === "bytes"
3610
3845
  ? strictReceiver(expected)
3611
3846
  : lowerer.lowerExpr(expr.expression);
3847
+ if (receiver.type.kind === "dyn")
3848
+ return { kind: "dynCheck", value: { kind: "dynKeyGet", value: receiver,
3849
+ key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, loc: locOf(expr) }, type: F64, loc: locOf(expr) };
3612
3850
  if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
3613
3851
  const present = lowerer.stripUndefinedArm(receiver.type);
3614
3852
  const helper = present.kind === "bytes"
@@ -3674,9 +3912,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3674
3912
  const expected = kind === "string"
3675
3913
  ? STRING
3676
3914
  : lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3677
- let receiver = expected && (expected.kind === "string" || expected.kind === "array")
3678
- ? strictReceiver(expected)
3679
- : lowerer.lowerExpr(expr.expression);
3915
+ let receiver = lowerer.lowerExpr(expr.expression);
3916
+ if (receiver.type.kind !== "dyn" && expected && (expected.kind === "string" || expected.kind === "array")) {
3917
+ receiver = lowerer.runtimeOptionalPropertyReceiver(expr.expression, receiver, expected, name) ?? receiver;
3918
+ }
3680
3919
  if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0 && kind === "array") {
3681
3920
  const present = lowerer.stripUndefinedArm(receiver.type);
3682
3921
  const helper = present.kind === "array"
@@ -3742,6 +3981,19 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3742
3981
  // destination can use the ordinary tuple-to-array coercion afterward.
3743
3982
  const tsType = underConstAssertion(expr) ? lowerer.typeOf(expr) : ctxType ?? lowerer.typeOf(expr);
3744
3983
  let mapped = expected ?? lowerer.mapTypeOf(tsType);
3984
+ // Inferred JavaScript arrays can contain unrelated constructors with
3985
+ // structurally equal checker types. Their native class ABIs still differ.
3986
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "classval")
3987
+ mapped = DYN;
3988
+ // An inferred destructuring pattern supplies a contextual [any, ...]
3989
+ // tuple. Its positions do not erase the literal's inferred element
3990
+ // types; explicit destinations already arrive through expected.
3991
+ if (expected === undefined && ctxType && lowerer.checker.isTupleType(ctxType) &&
3992
+ lowerer.checker.getTypeArguments(ctxType).some((type) => (type.flags & ts.TypeFlags.Any) !== 0)) {
3993
+ const inferred = lowerer.mapTypeOf(lowerer.typeOf(expr));
3994
+ if (inferred?.kind === "array" || inferred?.kind === "record" && lowerer.shapes.get(inferred.shapeId)?.tuple)
3995
+ mapped = inferred;
3996
+ }
3745
3997
  // A JS literal whose OWN inferred type is never-tainted
3746
3998
  // (neverTaintedJsType — the evolving `const gb = []`, the mixed
3747
3999
  // command tuple `['pwd', []]`) carries no element information: route
@@ -3838,6 +4090,14 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3838
4090
  mapped = arrayOf(first.type);
3839
4091
  }
3840
4092
  }
4093
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array") {
4094
+ const elem = mapped.elem;
4095
+ const arms = elem.kind === "union" ? lowerer.unions.get(elem.unionId)?.arms : [elem];
4096
+ if (arms?.length && arms.every((arm) => arm.kind === "classval"))
4097
+ mapped = DYN;
4098
+ }
4099
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && expr.elements.some((element) => ts.isSpreadElement(element) && tryLowerExpression(lowerer, element.expression)?.type.kind === "dyn"))
4100
+ mapped = DYN;
3841
4101
  // An EMPTY literal under a CONST ASSERTION whose type queries panicked
3842
4102
  // (tsgo's readonly-[] TupleType conversion — the facade answers `any`,
3843
4103
  // which maps to nothing statically and to an island value under
@@ -3849,6 +4109,11 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3849
4109
  underConstAssertion(expr)) {
3850
4110
  mapped = arrayOf(unitOnlyUnion(lowerer.unions));
3851
4111
  }
4112
+ // Heterogeneous JavaScript object arrays must retain each literal's
4113
+ // real keys. A structural union can otherwise drop descriptor fields.
4114
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "union" &&
4115
+ expr.elements.some(ts.isObjectLiteralExpression))
4116
+ mapped = DYN;
3852
4117
  // A TUPLE-typed slot (`const t: [string, number] = ["a", 1]`): the
3853
4118
  // literal constructs the tuple's record shape — one positional field
3854
4119
  // per element, source order (which IS index order, so evaluation order
@@ -3904,7 +4169,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3904
4169
  // `unknown[]` annotation or a collapsed `(string | object)[]` maps
3905
4170
  // to DYN wholesale now (mapType's dyn-element array rule), so the
3906
4171
  // literal IS the dyn array.
3907
- if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
4172
+ if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn" || lowerer.checkerAnyArray(expr)) {
3908
4173
  if (expr.elements.some(ts.isSpreadElement)) {
3909
4174
  // Native collection drains return vectors of retained dyn
3910
4175
  // elements. Reuse the ordinary spread builder, then expose the
@@ -3998,11 +4263,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3998
4263
  // the checker inferred a typed array from its producer. Drain once
3999
4264
  // and validate the elements before the ordinary spread copy.
4000
4265
  if (src.type.kind === "dyn" && (type.elem.kind === "dyn" || type.elem.kind === "f64" || type.elem.kind === "string" || type.elem.kind === "bool")) {
4001
- src = lowerer.coerceInto(el.expression, {
4002
- kind: "libCall", fn: "dyn.iterPack", args: [src,
4003
- { kind: "strLit", value: el.expression.getText(), type: STRING, loc: locOf(el) }],
4004
- type: DYN, loc: locOf(el),
4005
- }, type);
4266
+ src = lowerer.coerceInto(el.expression, checkedIteratorPack(lowerer, src, locOf(el)), type);
4006
4267
  }
4007
4268
  // A same-family array whose ELEMENT lifts (string[] into a
4008
4269
  // (string | symbol)[] literal — per-element wrap/width copy):
@@ -4255,6 +4516,9 @@ export function lowerElementAccess(lowerer, expr) {
4255
4516
  const cachedModule = lowerRequireCacheElement(lowerer, expr);
4256
4517
  if (cachedModule)
4257
4518
  return cachedModule;
4519
+ const namespaceElement = lowerModuleNamespaceElement(lowerer, expr);
4520
+ if (namespaceElement)
4521
+ return namespaceElement;
4258
4522
  // Native Web handles use the same supported surface for bracket and dot
4259
4523
  // spellings. Fence a statically-known unsupported key before the generic
4260
4524
  // checked-dynamic element-read path can turn it into a runtime missing
@@ -4320,7 +4584,11 @@ export function lowerElementAccess(lowerer, expr) {
4320
4584
  if (target)
4321
4585
  return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
4322
4586
  let receiver = lowerer.lowerExpr(expr.expression);
4323
- if (receiver.type.kind === "func") {
4587
+ const classInfo = receiver.type.kind === "classval" ? lowerer.classes.get(receiver.type.className) : undefined;
4588
+ if (receiver.type.kind === "func" || receiver.type.kind === "generator" ||
4589
+ ((receiver.type.kind === "array" || receiver.type.kind === "string" || receiver.type.kind === "bytes") && lowerer.dynConvertible(receiver.type)) ||
4590
+ (receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) || isDynTypedRefType(receiver.type) ||
4591
+ (classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) || ((receiver.type.kind === "map" || receiver.type.kind === "set") && lowerer.dynConvertible(receiver.type))) {
4324
4592
  const fnName = jsFuncNameOf(expr.expression);
4325
4593
  receiver = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr),
4326
4594
  ...(fnName !== null ? { fnName } : {}) };
@@ -4340,13 +4608,26 @@ export function lowerElementAccess(lowerer, expr) {
4340
4608
  : lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
4341
4609
  if (isJsSourceFile(expr.getSourceFile())) {
4342
4610
  const receiver = tryLowerExpression(lowerer, expr.expression);
4611
+ if (receiver?.type.kind === "dyn") {
4612
+ const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4613
+ const optional = hasOptionalChainGuard(expr.expression);
4614
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key,
4615
+ ...(optional ? { optional: true } : {}), type: DYN, loc: locOf(expr) }, expr);
4616
+ }
4343
4617
  if (receiver?.type.kind === "func" && lowerer.dynConvertible(receiver.type)) {
4344
4618
  const loc = locOf(expr);
4345
4619
  const fnName = jsFuncNameOf(expr.expression);
4346
4620
  const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
4347
- const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
4621
+ const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4348
4622
  return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
4349
4623
  }
4624
+ if (receiver?.type.kind === "array" || receiver?.type.kind === "bytes")
4625
+ receiverIr = receiver.type;
4626
+ if (receiver?.type.kind === "union") {
4627
+ const present = lowerer.stripUndefinedArm(receiver.type);
4628
+ if (present.kind === "array" || present.kind === "bytes")
4629
+ receiverIr = present;
4630
+ }
4350
4631
  }
4351
4632
  // A readonly tuple union under Array.isArray is checker-typed as an
4352
4633
  // intersection with any[], whose structural mapping is not the tuple
@@ -4363,7 +4644,7 @@ export function lowerElementAccess(lowerer, expr) {
4363
4644
  if (target)
4364
4645
  return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
4365
4646
  }
4366
- if (isDynTypedRefType(receiverIr)) {
4647
+ if (receiverIr.className === "%Error" || isDynTypedRefType(receiverIr)) {
4367
4648
  const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN);
4368
4649
  const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4369
4650
  return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
@@ -4412,6 +4693,11 @@ export function lowerElementAccess(lowerer, expr) {
4412
4693
  if (recv.type.kind === "jsval")
4413
4694
  return islandElementRead(lowerer, expr, recv);
4414
4695
  const index = lowerer.lowerExpr(expr.argumentExpression);
4696
+ if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "bytes") {
4697
+ return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN),
4698
+ key: index.type.kind === "f64" ? { kind: "toString", operand: index, type: STRING, loc: locOf(expr) } : index,
4699
+ type: DYN, loc: locOf(expr) };
4700
+ }
4415
4701
  if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
4416
4702
  return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN), key: index, type: DYN, loc: locOf(expr) };
4417
4703
  }
@@ -4509,16 +4795,21 @@ export function lowerElementAccess(lowerer, expr) {
4509
4795
  // Dispatch from that stored shape, as the array fallback below does.
4510
4796
  if (receiverIr === null && obj.type.kind === "record") {
4511
4797
  const shape = lowerer.shapes.get(obj.type.shapeId);
4798
+ const index = tupleLiteralIndex(expr.argumentExpression);
4799
+ const field = shape?.tuple && index !== null ? shape.fields.find((field) => field.name === String(index)) : null;
4800
+ if (field)
4801
+ return lowerer.maybeNarrow({ kind: "recordGet", obj, shapeId: obj.type.shapeId, field: field.name, type: field.type, loc: locOf(expr) }, expr);
4512
4802
  if (shape && !shape.tuple) {
4513
4803
  return lowerer.lowerRecordKeyRead(expr, obj.type.shapeId, shape);
4514
4804
  }
4515
4805
  }
4516
- if (obj.type.kind === "dyn" || isDynTypedRefType(obj.type)) {
4806
+ if (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type)) {
4517
4807
  const rawKey = lowerer.lowerExpr(expr.argumentExpression);
4518
4808
  // Number, bool, and DYN keys stringify (ToPropertyKey) — the
4519
4809
  // dyn-keyed read `catchWarning[warning.name]` where the property
4520
4810
  // chain itself lowered dyn.
4521
- const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol"
4811
+ const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol" ||
4812
+ rawKey.type.kind === "union" && lowerer.dynConvertible(rawKey.type)
4522
4813
  ? lowerer.coerceToExpected(rawKey, DYN)
4523
4814
  : lowerRecordPropertyKey(lowerer, rawKey, expr.argumentExpression);
4524
4815
  if (key.type.kind === "string" || key.type.kind === "dyn") {
@@ -4571,6 +4862,11 @@ export function lowerElementAccess(lowerer, expr) {
4571
4862
  if (receiverIr?.kind !== "array") {
4572
4863
  {
4573
4864
  const receiver = tryLowerExpression(lowerer, expr.expression);
4865
+ if (receiver?.type.kind === "dyn" || receiver && isJsSourceFile(expr.getSourceFile()) &&
4866
+ (receiver.type.kind === "union" || receiver.type.kind === "bytes") && lowerer.dynConvertible(receiver.type)) {
4867
+ const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
4868
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, receiver, DYN), key, type: DYN, loc: locOf(expr) }, expr);
4869
+ }
4574
4870
  if (receiver?.type.kind === "func" &&
4575
4871
  canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
4576
4872
  const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
@@ -4692,6 +4988,11 @@ export function lowerElementAccess(lowerer, expr) {
4692
4988
  // is undefined, whose property lookup misses just like NaN; keep that
4693
4989
  // value instead of rejecting a checker-number index with optional IR.
4694
4990
  const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
4991
+ if (arr.type.kind === "array" && (index.type.kind === "dyn" ||
4992
+ isJsSourceFile(expr.getSourceFile()) && index.type.kind !== "f64" && lowerer.dynConvertible(index.type))) {
4993
+ return { kind: "dynKeyGet", value: lowerer.coerceToExpected(arr, DYN),
4994
+ key: index, type: DYN, loc: locOf(expr) };
4995
+ }
4695
4996
  if (index.type.kind !== "f64") {
4696
4997
  lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
4697
4998
  }
@@ -4802,8 +5103,11 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4802
5103
  // Number AND checked-dynamic keys ride the JS-exact formatter —
4803
5104
  // property keys ARE strings (o[k] is o[String(k)] in JS), and a dyn
4804
5105
  // key (agent.sockets[agent.getName(...)]) stringifies the same way.
4805
- dk = lowerRecordPropertyKey(lowerer, dk, keyNode);
4806
- if (dk.type.kind !== "string") {
5106
+ if (dk.type.kind === "union" && lowerer.dynConvertible(dk.type))
5107
+ dk = lowerer.coerceToExpected(dk, DYN);
5108
+ if (dk.type.kind !== "symbol" && dk.type.kind !== "dyn")
5109
+ dk = lowerRecordPropertyKey(lowerer, dk, keyNode);
5110
+ if (dk.type.kind !== "string" && dk.type.kind !== "symbol" && dk.type.kind !== "dyn") {
4807
5111
  lowerer.unsupported("SC1090", keyNode, "indexing records with non-string or non-number keys");
4808
5112
  }
4809
5113
  const read = { kind: "dynKeyGet", key: dk, value: obj, type: DYN, loc };
@@ -4850,6 +5154,19 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4850
5154
  let key = litKey !== null
4851
5155
  ? { kind: "strLit", value: litKey, type: STRING, loc: locOf(keyNode) }
4852
5156
  : lowerer.lowerExpr(keyNode);
5157
+ // Optional string/number keys need ToPropertyKey, not a boxed copy of
5158
+ // the receiver. Keep record reads on their native storage so nested
5159
+ // references retain identity across index-signature reads.
5160
+ if (key.type.kind === "union" &&
5161
+ !lowerer.unions.get(key.type.unionId)?.arms.some((arm) => arm.kind === "symbol" || arm.kind === "dyn")) {
5162
+ key = lowerRecordPropertyKey(lowerer, key, keyNode);
5163
+ }
5164
+ if ((key.type.kind === "symbol" || key.type.kind === "dyn" || key.type.kind === "union") &&
5165
+ lowerer.dynConvertible(key.type) && lowerer.dynConvertible(obj.type)) {
5166
+ const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(obj, DYN),
5167
+ key: lowerer.coerceToExpected(key, DYN), type: DYN, loc };
5168
+ return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
5169
+ }
4853
5170
  // Runtime NUMBER keys ride the JS-exact formatter (o[n] is o[String(n)]
4854
5171
  // in JS — the number-keyed-signature access path).
4855
5172
  key = lowerRecordPropertyKey(lowerer, key, keyNode);
@@ -4914,6 +5231,9 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4914
5231
  * values remain real undefined values, whose JavaScript property spelling
4915
5232
  * is the string "undefined". */
4916
5233
  function lowerRecordPropertyKey(lowerer, key, node) {
5234
+ if (key.type.kind === "union" && lowerer.dynConvertible(key.type)) {
5235
+ return { kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(node, key, DYN)], type: STRING, loc: key.loc };
5236
+ }
4917
5237
  if (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "dyn" ||
4918
5238
  lowerer.runtimeOptionalWidening(key.type, STRING) !== null) {
4919
5239
  return lowerer.ensureString(key, node);
@@ -5079,11 +5399,29 @@ export function lowerElementCompound(lowerer, expr, op) {
5079
5399
  const target = expr.left;
5080
5400
  fenceNodeModuleMutation(lowerer, target, "assignment");
5081
5401
  const loc = locOf(expr);
5082
- const receiverType = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5402
+ const checked = lowerer.lowerExpr(target.expression);
5403
+ if (checked?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
5404
+ const stmts = [];
5405
+ const save = (value, name) => {
5406
+ const local = lowerer.declareHiddenLocal(name, value.type);
5407
+ stmts.push({ kind: "varDecl", localId: local.id, init: value, loc });
5408
+ return varRef(local.id, value.type, loc);
5409
+ };
5410
+ const receiver = save(checked, "%compoundReceiver");
5411
+ const key = save(lowerer.lowerExprExpecting(target.argumentExpression, DYN), "%compoundKey");
5412
+ const old = save({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, "%compoundOld");
5413
+ const rhs = lowerer.lowerExprExpecting(expr.right, DYN);
5414
+ const numeric = ["-", "*", "/", "%", "**"].includes(op);
5415
+ const value = save({ kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
5416
+ args: op === "+" ? [old, rhs] : [old, rhs, strLit(op, loc)], type: DYN, loc }, "%compoundResult");
5417
+ stmts.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, value], type: VOID, loc }, loc });
5418
+ return { kind: "seqExpr", stmts, result: value, type: DYN, loc };
5419
+ }
5420
+ const receiverType = checked.type.kind === "array" || checked.type.kind === "bytes" ? checked.type : lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5083
5421
  if (receiverType?.kind !== "array" && receiverType?.kind !== "bytes") {
5084
5422
  lowerer.unsupported("SC1090", target, "compound assignment to non-array elements");
5085
5423
  }
5086
- let receiver = lowerer.lowerExpr(target.expression);
5424
+ let receiver = checked;
5087
5425
  if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
5088
5426
  const present = lowerer.stripUndefinedArm(receiver.type);
5089
5427
  const helper = present.kind === receiverType.kind
@@ -5108,7 +5446,9 @@ export function lowerElementCompound(lowerer, expr, op) {
5108
5446
  const oldLocal = lowerer.declareHiddenLocal("%compoundOld", oldValue.type);
5109
5447
  const oldRef = () => varRef(oldLocal.id, oldValue.type, loc);
5110
5448
  const rhs = lowerer.lowerExpr(expr.right);
5111
- const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
5449
+ const numericRhs = rhs.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile()) && receiver.type.kind === "bytes"
5450
+ ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [rhs], type: F64, loc }
5451
+ : lowerOptionalNumber(lowerer, rhs, loc);
5112
5452
  const elementType = receiver.type.kind === "array" ? receiver.type.elem : F64;
5113
5453
  const valueType = elementType.kind === "union" && lowerer.armTag(elementType.unionId, UNDEFINED_T) >= 0
5114
5454
  ? lowerer.stripUndefinedArm(elementType)
@@ -5120,6 +5460,12 @@ export function lowerElementCompound(lowerer, expr, op) {
5120
5460
  else if (valueType.kind === "f64" && numericRhs.type.kind === "f64") {
5121
5461
  computed = { kind: "bin", op, left: lowerOptionalNumber(lowerer, oldRef(), loc), right: numericRhs, type: F64, loc };
5122
5462
  }
5463
+ else if (valueType.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
5464
+ const left = lowerer.coerceToExpected(oldRef(), DYN), right = lowerer.coerceToExpected(rhs, DYN);
5465
+ const numeric = ["-", "*", "/", "%", "**"].includes(op);
5466
+ computed = { kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
5467
+ args: op === "+" ? [left, right] : [left, right, strLit(op, loc)], type: DYN, loc };
5468
+ }
5123
5469
  else {
5124
5470
  lowerer.unsupported("SC1043", expr);
5125
5471
  }
@@ -5181,6 +5527,18 @@ export function lowerNativeFunctionAssignment(lowerer, expr) {
5181
5527
  (!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) ||
5182
5528
  target.questionDotToken)
5183
5529
  return null;
5530
+ // Checked constructors share the callable's live property descriptors.
5531
+ const classType = storedClassValueType(lowerer, target.expression);
5532
+ const staticClass = classType?.kind === "classval" ? lowerer.classes.get(classType.className) : undefined;
5533
+ if (staticClass && hasRuntimeStatics(staticClass)) {
5534
+ return lowerDynMemberAssignment(lowerer, expr, lowerer.lowerExprExpecting(target.expression, DYN));
5535
+ }
5536
+ // Other class statics and prototype tables have their own paths.
5537
+ // Probing them as ordinary functions could materialize unused methods.
5538
+ if (lowerer.exactClassOfReceiver(target.expression) ||
5539
+ ts.isPropertyAccessExpression(target.expression) && target.expression.name.text === "prototype" &&
5540
+ lowerer.exactClassOfReceiver(target.expression.expression))
5541
+ return null;
5184
5542
  const receiver = tryLowerExpression(lowerer, target.expression);
5185
5543
  if (receiver?.type.kind !== "func" ||
5186
5544
  !canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
@@ -5194,6 +5552,9 @@ export function lowerNativeFunctionAssignment(lowerer, expr) {
5194
5552
  return lowerDynMemberAssignment(lowerer, expr, boxed);
5195
5553
  }
5196
5554
  export function lowerElementWrite(lowerer, expr) {
5555
+ const prototype = lowerClassPrototypeComputedAssignment(lowerer, expr);
5556
+ if (prototype)
5557
+ return { kind: "exprStmt", expr: prototype, loc: locOf(expr) };
5197
5558
  const target = expr.left;
5198
5559
  fenceNodeModuleMutation(lowerer, target, "assignment");
5199
5560
  const globalKey = globalSymbolKey(lowerer, target.expression, target.argumentExpression);
@@ -5233,13 +5594,30 @@ export function lowerElementWrite(lowerer, expr) {
5233
5594
  const receiver = lowerer.lowerExpr(target.expression);
5234
5595
  if (receiver.type.kind === "dyn")
5235
5596
  return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
5597
+ if (isJsSourceFile(expr.getSourceFile()) && receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) {
5598
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceToExpected(receiver, DYN)), loc: locOf(expr) };
5599
+ }
5236
5600
  lowerer.unsupported("SC1090", target, "symbol-keyed property writes outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
5237
5601
  }
5238
5602
  const checkedReceiver = tryLowerExpression(lowerer, target.expression);
5239
5603
  if (checkedReceiver?.type.kind === "dyn") {
5240
5604
  return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, checkedReceiver), loc: locOf(expr) };
5241
5605
  }
5606
+ if (checkedReceiver?.type.kind === "object" && isJsSourceFile(expr.getSourceFile())) {
5607
+ const info = lowerer.classes.get(checkedReceiver.type.className);
5608
+ const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
5609
+ if (info && key !== null && isClassCallback(lowerer, info, key)) {
5610
+ const receiver = lowerer.coerceInto(target.expression, checkedReceiver, DYN);
5611
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
5612
+ }
5613
+ }
5242
5614
  let receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5615
+ if (isJsSourceFile(expr.getSourceFile()) && checkedReceiver) {
5616
+ const represented = checkedReceiver.type.kind === "union"
5617
+ ? lowerer.stripUndefinedArm(checkedReceiver.type) : checkedReceiver.type;
5618
+ if (represented.kind === "array" || represented.kind === "bytes")
5619
+ receiverIr = represented;
5620
+ }
5243
5621
  if (receiverIr === null || receiverIr.kind === "dyn") {
5244
5622
  const receiver = tryLowerExpression(lowerer, target.expression);
5245
5623
  if (receiver?.type.kind === "array")
@@ -5307,13 +5685,15 @@ export function lowerElementWrite(lowerer, expr) {
5307
5685
  return lowerer.fieldSetStmt(field, value, locOf(expr), target);
5308
5686
  }
5309
5687
  if (hasClassPayload(lowerer, receiver.type)) {
5688
+ if (receiver.type.kind === "object" && receiver.type.className === "%Error") {
5689
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, receiver, DYN)), loc: locOf(expr) };
5690
+ }
5310
5691
  lowerer.unsupported("SC1090", target, "writing undeclared properties through structural views of class instances");
5311
5692
  }
5312
5693
  }
5313
5694
  }
5314
5695
  // Typed-array element write `b[i] = v` — bytesSet: the value is an f64
5315
- // the runtime coerces JS-exactly; invalid indices trap (no appends —
5316
- // typed arrays are fixed-length; JS would ignore the write).
5696
+ // the runtime coerces JS-exactly; invalid indices are ignored.
5317
5697
  if (receiverIr?.kind === "bytes") {
5318
5698
  const recv = lowerer.lowerExpr(target.expression);
5319
5699
  // The write twin: a typed-array .d.ts surface whose VALUE is a
@@ -5499,7 +5879,7 @@ export function lowerElementWrite(lowerer, expr) {
5499
5879
  // write, anything else falls through to the fences.
5500
5880
  if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
5501
5881
  const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
5502
- if (obj !== null && (obj.type.kind === "dyn" || isDynTypedRefType(obj.type))) {
5882
+ if (obj !== null && (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type))) {
5503
5883
  const loc = locOf(expr);
5504
5884
  const litKey = recordKeyLiteralText(target.argumentExpression);
5505
5885
  let key = litKey !== null
@@ -5544,6 +5924,9 @@ export function lowerElementWrite(lowerer, expr) {
5544
5924
  lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
5545
5925
  }
5546
5926
  const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
5927
+ if (index.type.kind === "dyn" && lowerer.dynConvertible(arr.type)) {
5928
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, arr, DYN)), loc: locOf(expr) };
5929
+ }
5547
5930
  if (index.type.kind !== "f64") {
5548
5931
  lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
5549
5932
  }
@@ -5878,7 +6261,7 @@ export function lowerAsExpression(lowerer, expr) {
5878
6261
  return { kind: "jsExit", value: inner, type: target, loc: locOf(expr) };
5879
6262
  }
5880
6263
  const targetTs = lowerer.checker.getTypeFromTypeNode(expr.type);
5881
- const target = lowerer.mapTypeOf(targetTs);
6264
+ const target = lowerer.mapTypeOf(targetTs) ?? dynFallbackType(lowerer, expr.type, targetTs);
5882
6265
  if (!target)
5883
6266
  lowerer.badType(expr.type, targetTs);
5884
6267
  if (target.kind === "dyn")
@@ -6011,59 +6394,86 @@ export function lowerPrefixUnary(lowerer, expr) {
6011
6394
  }
6012
6395
  lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
6013
6396
  }
6014
- /** `x++`/`x--`/`++x`/`--x` in EXPRESSION position — the incDec node:
6015
- * read, write ±1, yield old (postfix) or new (prefix) value. Receivers
6016
- * are f64 LOCALS or module globals (captured/boxed included — the
6017
- * backend reads/writes through the box); record fields and array
6018
- * elements keep the fence in value position (statement-position `obj.f++`
6019
- * still desugars through the compound-field path). */
6020
- function lowerIncDec(lowerer, expr, prefix) {
6397
+ /** Member updates evaluate the reference once, convert its old value with
6398
+ * ToNumeric, store the new value, then yield the old or new numeric value. */
6399
+ export function lowerIncDec(lowerer, expr, prefix) {
6021
6400
  const loc = locOf(expr);
6022
6401
  const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
6023
6402
  if (!ts.isIdentifier(expr.operand)) {
6024
- if (ts.isPropertyAccessExpression(expr.operand)) {
6025
- const target = staticFieldWriteTarget(lowerer, expr.operand);
6403
+ const access = expr.operand;
6404
+ if (!ts.isPropertyAccessExpression(access) && !ts.isElementAccessExpression(access) || access.questionDotToken) {
6405
+ lowerer.unsupported("SC1045", expr, "increment/decrement of this target");
6406
+ }
6407
+ fenceNodeModuleMutation(lowerer, access, "assignment");
6408
+ if (ts.isPropertyAccessExpression(access)) {
6409
+ const target = staticFieldWriteTarget(lowerer, access);
6026
6410
  if (target) {
6027
6411
  if (target.type.kind !== "f64")
6028
6412
  lowerer.unsupported("SC1043", expr);
6029
6413
  return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
6030
6414
  }
6031
6415
  }
6032
- // CLASS-FIELD receivers (`if (--this.limit === 0)` — countdown.js's
6033
- // dec(); `--this[kLimit]` — its symbol-keyed spelling): a single-
6034
- // evaluation read-modify-write over the instance, yielding old/new
6035
- // like the local form. f64 fields are JS-exact; CHECKED-DYNAMIC
6036
- // fields (implicit-any ctor assignments in JS) take the dyn
6037
- // arithmetic stance — the number validates out (dynCheck's
6038
- // catchable TypeError, never a silent ToNumber; SEMANTICS.md),
6039
- // computes, and boxes back. Array elements, record fields, and
6040
- // accessor-backed names keep the fence.
6041
- if ((ts.isPropertyAccessExpression(expr.operand) || ts.isElementAccessExpression(expr.operand)) &&
6042
- !expr.operand.questionDotToken) {
6043
- const target = ts.isPropertyAccessExpression(expr.operand)
6044
- ? lowerer.fieldTarget(expr.operand)
6045
- : symbolFieldTarget(lowerer, expr.operand);
6046
- if (target?.container === "class" &&
6047
- !RUNTIME_ERROR_CLASSES.has(target.className) &&
6048
- (target.fieldType.kind === "f64" ||
6049
- (target.fieldType.kind === "dyn" && isJsSourceFile(expr.getSourceFile())))) {
6050
- return {
6051
- kind: "fieldIncDec",
6052
- op,
6053
- prefix,
6054
- obj: target.obj,
6055
- className: target.className,
6056
- field: target.field,
6057
- fieldDyn: target.fieldType.kind === "dyn",
6058
- type: F64,
6059
- loc,
6060
- };
6061
- }
6062
- }
6063
- // Other field/element receivers need a read-modify-write over a
6064
- // computed receiver — statement position has that desugar; value
6065
- // position does not yet. The message names the working forms.
6066
- lowerer.unsupported("SC1045", expr, "increment/decrement of record fields or elements in expression position");
6416
+ const stmts = [];
6417
+ const save = (value, name) => {
6418
+ const local = lowerer.declareHiddenLocal(name, value.type);
6419
+ stmts.push({ kind: "varDecl", localId: local.id, init: value, loc });
6420
+ return varRef(local.id, value.type, loc);
6421
+ };
6422
+ const finish = (read, write) => {
6423
+ const old = save(read.type.kind === "f64" ? read : {
6424
+ kind: "libCall", fn: "dyn.toNumeric", args: [lowerer.coerceInto(access, read, DYN)], type: DYN, loc,
6425
+ }, "%incrementOld");
6426
+ const next = save(old.type.kind === "f64" ? {
6427
+ kind: "bin", op, left: old, right: { kind: "numLit", value: 1, type: F64, loc }, type: F64, loc,
6428
+ } : {
6429
+ kind: "libCall", fn: "dyn.increment", args: [old, { kind: "boolLit", value: op === "+", type: BOOL, loc }], type: DYN, loc,
6430
+ }, "%incrementNext");
6431
+ stmts.push(write(next));
6432
+ const result = prefix ? next : old;
6433
+ return { kind: "seqExpr", stmts, result, type: result.type, loc };
6434
+ };
6435
+ const raw = lowerer.lowerExpr(access.expression);
6436
+ // Keep native array/byte storage: boxing a native array can create a
6437
+ // checked view, which is not a substitute for updating its slot.
6438
+ if (ts.isElementAccessExpression(access) && (raw.type.kind === "array" || raw.type.kind === "bytes")) {
6439
+ const receiver = save(raw, "%incrementArray");
6440
+ const rawIndex = lowerer.lowerExpr(access.argumentExpression);
6441
+ const index = save(rawIndex.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())
6442
+ ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [rawIndex], type: F64, loc } : rawIndex, "%incrementIndex");
6443
+ if (index.type.kind !== "f64")
6444
+ lowerer.unsupported("SC1090", access, "incrementing non-number array keys");
6445
+ const type = raw.type;
6446
+ const read = type.kind === "array"
6447
+ ? arrayValueRead(lowerer, receiver, index, type.elem, loc)
6448
+ : { kind: "bytesIntrinsic", method: "get", receiver, args: [index], type: F64, loc };
6449
+ return finish(read, (value) => type.kind === "array"
6450
+ ? arrayValueStore(lowerer, receiver, index, lowerer.coerceInto(access, value, type.elem), type.elem, loc)
6451
+ : { kind: "bytesSet", arr: receiver, index, value: lowerer.coerceInto(access, value, F64), loc });
6452
+ }
6453
+ if (isJsSourceFile(expr.getSourceFile()) && raw.type.kind === "dyn") {
6454
+ const receiver = save(raw, "%incrementReceiver");
6455
+ const key = save(ts.isElementAccessExpression(access)
6456
+ ? lowerer.lowerExprExpecting(access.argumentExpression, DYN)
6457
+ : lowerer.coerceInto(access, { kind: "strLit", value: access.name.text, type: STRING, loc }, DYN), "%incrementKey");
6458
+ return finish({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, (value) => ({
6459
+ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, lowerer.coerceInto(access, value, DYN)], type: VOID, loc }, loc,
6460
+ }));
6461
+ }
6462
+ const target = ts.isPropertyAccessExpression(access) ? lowerer.fieldTarget(access) : symbolFieldTarget(lowerer, access);
6463
+ if (target && lowerer.dynConvertible(target.fieldType)) {
6464
+ target.obj = save(target.obj, "%incrementReceiver");
6465
+ return finish(lowerer.fieldGetExpr(target, loc, access), (value) => lowerer.fieldSetStmt(target, lowerer.coerceInto(access, value, target.fieldType), loc, access));
6466
+ }
6467
+ if (isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(raw.type)) {
6468
+ const receiver = save(lowerer.coerceInto(access.expression, raw, DYN), "%incrementReceiver");
6469
+ const key = save(ts.isElementAccessExpression(access)
6470
+ ? lowerer.lowerExprExpecting(access.argumentExpression, DYN)
6471
+ : lowerer.coerceInto(access, { kind: "strLit", value: access.name.text, type: STRING, loc }, DYN), "%incrementKey");
6472
+ return finish({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, (value) => ({
6473
+ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, lowerer.coerceInto(access, value, DYN)], type: VOID, loc }, loc,
6474
+ }));
6475
+ }
6476
+ lowerer.unsupported("SC1045", expr, "increment/decrement of this member target");
6067
6477
  }
6068
6478
  const target = lowerer.resolveWritable(expr.operand);
6069
6479
  if (!target) {
@@ -6143,6 +6553,7 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
6143
6553
  const loc = locOf(expr);
6144
6554
  const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
6145
6555
  const rhs = lowerer.lowerExpr(expr.right);
6556
+ const numericRead = lowerOptionalNumber(lowerer, read, loc);
6146
6557
  const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
6147
6558
  if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
6148
6559
  const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
@@ -6159,16 +6570,17 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
6159
6570
  if (compound === "+" && target.type.kind === "string") {
6160
6571
  return { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
6161
6572
  }
6162
- if (target.type.kind === "f64" && numericRhs.type.kind === "f64") {
6163
- return { kind: "bin", op: compound, left: read, right: numericRhs, type: F64, loc };
6573
+ if (numericRead.type.kind === "f64" && numericRhs.type.kind === "f64") {
6574
+ return lowerer.coerceInto(expr, { kind: "bin", op: compound, left: numericRead, right: numericRhs, type: F64, loc }, target.type);
6164
6575
  }
6165
6576
  if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
6166
- (target.type.kind === "dyn" || target.type.kind === "f64") &&
6167
- (rhs.type.kind === "dyn" || rhs.type.kind === "f64") &&
6168
6577
  isJsSourceFile(expr.getSourceFile())) {
6169
- const checkNum = (value) => value.type.kind === "dyn" ? { kind: "dynCheck", value, type: F64, loc: value.loc } : value;
6170
- const computed = { kind: "bin", op: compound, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
6171
- return target.type.kind === "dyn" ? { kind: "dynFrom", value: computed, type: DYN, loc } : computed;
6578
+ const left = lowerer.coerceInto(expr.left, read, DYN);
6579
+ const right = lowerer.coerceInto(expr.right, rhs, DYN);
6580
+ const numeric = ["-", "*", "/", "%", "**"].includes(compound);
6581
+ const computed = { kind: "libCall", fn: compound === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
6582
+ args: compound === "+" ? [left, right] : [left, right, { kind: "strLit", value: compound, type: STRING, loc }], type: DYN, loc };
6583
+ return lowerer.coerceInto(expr, computed, target.type);
6172
6584
  }
6173
6585
  lowerer.unsupported("SC1043", expr);
6174
6586
  }
@@ -6180,6 +6592,9 @@ export function lowerBinary(lowerer, expr) {
6180
6592
  return cacheHas;
6181
6593
  if (op === ts.SyntaxKind.EqualsToken || (op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
6182
6594
  if (op === ts.SyntaxKind.EqualsToken) {
6595
+ const prototypeWrite = lowerClassPrototypeComputedAssignment(lowerer, expr);
6596
+ if (prototypeWrite)
6597
+ return prototypeWrite;
6183
6598
  const callableWrite = lowerNativeFunctionAssignment(lowerer, expr);
6184
6599
  if (callableWrite)
6185
6600
  return callableWrite;
@@ -6293,8 +6708,30 @@ export function lowerBinary(lowerer, expr) {
6293
6708
  // key afterward, then yields the RHS without rereading the property.
6294
6709
  if (ts.isElementAccessExpression(expr.left) && !expr.left.questionDotToken) {
6295
6710
  const recv = tryLowerExpression(lowerer, expr.left.expression);
6296
- if (recv?.type.kind === "dyn") {
6297
- return lowerDynMemberAssignment(lowerer, expr, recv);
6711
+ if (recv && (recv.type.kind === "dyn" ||
6712
+ recv.type.kind === "array" && isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(recv.type))) {
6713
+ return lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(expr.left.expression, recv, DYN));
6714
+ }
6715
+ if (recv?.type.kind === "array" || recv?.type.kind === "bytes") {
6716
+ const receiver = lowerer.declareHiddenLocal("%setArray", recv.type);
6717
+ const index = lowerer.lowerExpr(expr.left.argumentExpression);
6718
+ if (index.type.kind !== "f64")
6719
+ lowerer.unsupported("SC1090", expr.left.argumentExpression, "indexing with non-number keys");
6720
+ const key = lowerer.declareHiddenLocal("%setIndex", F64);
6721
+ const raw = lowerer.lowerExpr(expr.right);
6722
+ const value = lowerer.declareHiddenLocal("%setValue", raw.type);
6723
+ const result = varRef(value.id, raw.type, loc);
6724
+ const arr = varRef(receiver.id, recv.type, loc);
6725
+ const idx = varRef(key.id, F64, loc);
6726
+ const write = recv.type.kind === "array"
6727
+ ? arrayValueStore(lowerer, arr, idx, result, recv.type.elem, loc)
6728
+ : { kind: "bytesSet", arr, index: idx, value: lowerer.coerceInto(expr.right, result, F64), loc };
6729
+ return { kind: "seqExpr", stmts: [
6730
+ { kind: "varDecl", localId: receiver.id, init: recv, loc },
6731
+ { kind: "varDecl", localId: key.id, init: index, loc },
6732
+ { kind: "varDecl", localId: value.id, init: raw, loc },
6733
+ write,
6734
+ ], result, type: result.type, loc };
6298
6735
  }
6299
6736
  }
6300
6737
  // `h.k = v` on an ISLAND receiver in VALUE position: the engine
@@ -6337,32 +6774,8 @@ export function lowerBinary(lowerer, expr) {
6337
6774
  // consumer sees exactly what the checker typed.
6338
6775
  if (ts.isPropertyAccessExpression(expr.left) && !expr.left.questionDotToken) {
6339
6776
  const recv = tryLowerExpression(lowerer, expr.left.expression);
6340
- if (recv && recv.type.kind === "dyn") {
6341
- const recvTmp = lowerer.declareHiddenLocal("%setRecv", DYN);
6342
- const rhsVal = lowerer.lowerExpr(expr.right);
6343
- const valTmp = lowerer.declareHiddenLocal("%setVal", rhsVal.type);
6344
- const valRef = () => ({ kind: "varRef", localId: valTmp.id, type: rhsVal.type, loc });
6345
- const stored = lowerer.coerceToExpected(valRef(), DYN);
6346
- if (stored.type.kind !== "dyn") {
6347
- lowerer.unsupported("SC1101", expr.right, `assigning '${lowerer.fmt(rhsVal.type)}' values into a checked-dynamic member`);
6348
- }
6349
- const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
6350
- return {
6351
- kind: "seqExpr",
6352
- stmts: [
6353
- { kind: "varDecl", localId: recvTmp.id, init: recv, loc },
6354
- { kind: "varDecl", localId: valTmp.id, init: rhsVal, loc },
6355
- {
6356
- kind: "exprStmt",
6357
- expr: { kind: "libCall", fn: "dyn.keySet", args: [{ kind: "varRef", localId: recvTmp.id, type: DYN, loc }, key, stored], type: VOID, loc },
6358
- loc,
6359
- },
6360
- ],
6361
- result: valRef(),
6362
- type: rhsVal.type,
6363
- loc,
6364
- };
6365
- }
6777
+ if (recv && recv.type.kind === "dyn")
6778
+ return lowerDynMemberAssignment(lowerer, expr, recv);
6366
6779
  const field = lowerer.fieldTarget(expr.left);
6367
6780
  if (field) {
6368
6781
  const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
@@ -6446,8 +6859,14 @@ export function lowerBinary(lowerer, expr) {
6446
6859
  }
6447
6860
  const strictEquality = op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
6448
6861
  op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6449
- const left = strictEquality ? lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left) : lowerer.lowerExpr(expr.left);
6450
- const right = strictEquality ? lowerAbsenceProbe(lowerer, expr.right) ?? lowerer.lowerExpr(expr.right) : lowerer.lowerExpr(expr.right);
6862
+ let left = strictEquality ? lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left) : lowerer.lowerExpr(expr.left);
6863
+ let right = strictEquality ? lowerAbsenceProbe(lowerer, expr.right) ?? lowerer.lowerExpr(expr.right) : lowerer.lowerExpr(expr.right);
6864
+ if (strictEquality && (left.type.kind === "caught" || right.type.kind === "caught")) {
6865
+ if (left.type.kind === "caught")
6866
+ left = { kind: "caughtToDyn", value: left, type: DYN, loc: left.loc };
6867
+ if (right.type.kind === "caught")
6868
+ right = { kind: "caughtToDyn", value: right, type: DYN, loc: right.loc };
6869
+ }
6451
6870
  if (left.type.kind === "dyn" || right.type.kind === "dyn") {
6452
6871
  // The narrowing unit comparisons ARE answerable on unknown: `v ===
6453
6872
  // undefined` / `v !== null` test the dyn node's kind directly, and
@@ -6475,9 +6894,9 @@ export function lowerBinary(lowerer, expr) {
6475
6894
  const dynSide = left.type.kind === "dyn" ? left : right;
6476
6895
  const scalarSide = dynSide === left ? right : left;
6477
6896
  const reference = scalarSide.type;
6478
- if ((isDynTypedRefType(reference) || reference.kind === "record" || reference.kind === "array" ||
6479
- reference.kind === "bytes" || reference.kind === "func" || reference.kind === "regex" || reference.kind === "url" ||
6480
- reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
6897
+ if ((reference.kind === "object" || reference.kind === "record" || reference.kind === "array" ||
6898
+ reference.kind === "bytes" || reference.kind === "func" || reference.kind === "classval" || reference.kind === "regex" || reference.kind === "url" ||
6899
+ reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "map" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
6481
6900
  lowerer.dynConvertible(reference)) {
6482
6901
  const boxed = {
6483
6902
  kind: "dynFrom", value: scalarSide, type: DYN, loc: scalarSide.loc,
@@ -6524,8 +6943,11 @@ export function lowerBinary(lowerer, expr) {
6524
6943
  }
6525
6944
  // Numeric operators dispatch on ToNumeric's actual Number/BigInt
6526
6945
  // result, including values flowing through native callable properties.
6527
- if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken) {
6528
- const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
6946
+ if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken ||
6947
+ [ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
6948
+ ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken].includes(op) ||
6949
+ [ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
6950
+ ts.SyntaxKind.PercentToken, ts.SyntaxKind.AsteriskAsteriskToken].includes(op)) {
6529
6951
  const other = left.type.kind === "dyn" ? right : left;
6530
6952
  const NUM_BIN = {
6531
6953
  [ts.SyntaxKind.MinusToken]: "-",
@@ -6553,8 +6975,9 @@ export function lowerBinary(lowerer, expr) {
6553
6975
  return numeric ? { kind: "dynCheck", value, type: numeric, loc } : value;
6554
6976
  }
6555
6977
  }
6556
- if (cmp && (other.type.kind === "f64" || other.type.kind === "dyn")) {
6557
- return { kind: "bin", op: cmp, left: checkNum(left), right: checkNum(right), type: BOOL, loc };
6978
+ if (cmp && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
6979
+ return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
6980
+ lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmp, type: STRING, loc }], type: BOOL, loc };
6558
6981
  }
6559
6982
  // Untyped addition chooses concatenation or numeric addition only
6560
6983
  // after both operands have undergone ToPrimitive at runtime.
@@ -6986,6 +7409,10 @@ export function lowerBinary(lowerer, expr) {
6986
7409
  if (DYN_HANDLE_KINDS.has(idLeft.type.kind) && typeEquals(idLeft.type, idRight.type)) {
6987
7410
  return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
6988
7411
  }
7412
+ if (lowerer.dynConvertible(idLeft.type) && lowerer.dynConvertible(idRight.type)) {
7413
+ return { kind: "dynScalarEq", left: lowerer.coerceToExpected(idLeft, DYN),
7414
+ right: lowerer.coerceToExpected(idRight, DYN), ...(negated ? { negated: true } : {}), type: BOOL, loc };
7415
+ }
6989
7416
  lowerer.unsupported("SC1043", expr);
6990
7417
  break;
6991
7418
  }
@@ -7000,6 +7427,10 @@ export function lowerBinary(lowerer, expr) {
7000
7427
  return { kind: "bin", op: cmpOp, left: arithmeticLeft, right: arithmeticRight, type: BOOL, loc };
7001
7428
  if (bothStr)
7002
7429
  return { kind: "strCmp", op: cmpOp, left, right, type: BOOL, loc };
7430
+ if (lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
7431
+ return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
7432
+ lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmpOp, type: STRING, loc }], type: BOOL, loc };
7433
+ }
7003
7434
  lowerer.unsupported("SC1043", expr);
7004
7435
  break;
7005
7436
  }
@@ -7042,6 +7473,16 @@ function lowerLogicalChain(lowerer, expr) {
7042
7473
  function lowerLogicalPair(lowerer, expr, left) {
7043
7474
  const op = expr.operatorToken.kind;
7044
7475
  const loc = locOf(expr);
7476
+ // Preserve short-circuiting during lowering too: an unreachable member
7477
+ // on a specialized value must not poison the surrounding JS statement.
7478
+ if (left.kind === "boolLit") {
7479
+ const takeRight = op === ts.SyntaxKind.AmpersandAmpersandToken ? left.value : !left.value;
7480
+ return takeRight ? lowerer.lowerExpr(expr.right) : left;
7481
+ }
7482
+ if (left.kind === "unitLit" && op === ts.SyntaxKind.AmpersandAmpersandToken)
7483
+ return left;
7484
+ if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind))
7485
+ return left;
7045
7486
  const right = lowerer.lowerExpr(expr.right);
7046
7487
  // A LITERAL-unit left operand (a compile-time undefined/null — the
7047
7488
  // capability-probe members: `process.features.inspector ||
@@ -7091,6 +7532,11 @@ function lowerLogicalPair(lowerer, expr, left) {
7091
7532
  type: JSVAL, loc,
7092
7533
  };
7093
7534
  }
7535
+ if (isJsSourceFile(expr.getSourceFile()) && op === ts.SyntaxKind.AmpersandAmpersandToken &&
7536
+ left.type.kind === "union" && (lowerer.unions.get(left.type.unionId)?.arms.some((arm) => arm.kind === "bool")) &&
7537
+ !typeEquals(left.type, right.type) && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
7538
+ return { kind: "logical", op: "&&", left: lowerer.coerceToExpected(left, DYN), right: lowerer.coerceToExpected(right, DYN), type: DYN, loc };
7539
+ }
7094
7540
  if (left.type.kind === "union" || right.type.kind === "union") {
7095
7541
  // Test the deciding operand in its own representation. The result
7096
7542
  // contains only the values that can survive short-circuit evaluation.
@@ -7664,6 +8110,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
7664
8110
  for (const [a, b] of operandPairs(expr)) {
7665
8111
  if (!ts.isTypeOfExpression(a))
7666
8112
  continue;
8113
+ if (lowerer.isStdlibGlobal(a.expression, "fetch"))
8114
+ continue;
7667
8115
  // Non-literal comparisons use the ordinary typeof string value,
7668
8116
  // preserving both operands' evaluation order without a narrowing test.
7669
8117
  if (!ts.isStringLiteral(b))
@@ -7970,6 +8418,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7970
8418
  if (value.type.kind === collection)
7971
8419
  return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
7972
8420
  result: { kind: "boolLit", value: true, type: BOOL, loc }, type: BOOL, loc };
8421
+ return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs",
8422
+ args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
7973
8423
  }
7974
8424
  if (lowerer.isStdlibGlobal(expr.right, "WeakMap") || lowerer.isStdlibGlobal(expr.right, "WeakSet")) {
7975
8425
  if (lowerer.dynamic) {
@@ -7986,6 +8436,10 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7986
8436
  const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7987
8437
  return { kind: "libCall", fn: "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
7988
8438
  }
8439
+ if (lowerer.isStdlibGlobal(expr.right, "DataView")) {
8440
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
8441
+ return { kind: "libCall", fn: "dyn.dataViewIs", args: [value], type: BOOL, loc };
8442
+ }
7989
8443
  if (lowerer.isStdlibGlobal(expr.right, "ArrayBuffer")) {
7990
8444
  const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7991
8445
  return { kind: "libCall", fn: "arrayBuffer.is", args: [value], type: BOOL, loc };
@@ -8040,24 +8494,29 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8040
8494
  if (value.type.kind === "dyn")
8041
8495
  return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
8042
8496
  if (value.type.kind === "union")
8043
- lowerer.unsupported("SC1090", expr, "Date or URL instanceof over a union");
8497
+ return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs",
8498
+ args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
8044
8499
  return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
8045
8500
  result: { kind: "boolLit", value: value.type.kind === (date ? "date" : "url"), type: BOOL, loc }, type: BOOL, loc };
8046
8501
  }
8502
+ const webBrand = ["Request", "Response", "Headers"].find((name) => lowerer.isStdlibGlobal(expr.right, name));
8503
+ if (webBrand)
8504
+ return { kind: "libCall", fn: "fetch.webIs", args: [lowerer.lowerExprExpecting(expr.left, DYN),
8505
+ { kind: "strLit", value: webBrand, type: STRING, loc }], type: BOOL, loc };
8506
+ if (lowerer.isStdlibGlobal(expr.right, "ReadableStream")) {
8507
+ const value = lowerer.lowerExprExpecting(expr.left, DYN);
8508
+ return { kind: "libCall", fn: "fetch.streamIs", args: [value], type: BOOL, loc };
8509
+ }
8047
8510
  // Unknown storage retains each numeric typed array's exact native brand.
8048
- if (ts.isIdentifier(expr.right) && !lowerer.caughtLocalOf(expr.left)) {
8511
+ if (!lowerer.caughtLocalOf(expr.left)) {
8049
8512
  const elem = Object.keys(BYTES_ELEMENT_NAME).find((kind) => lowerer.isStdlibGlobal(expr.right, BYTES_ELEMENT_NAME[kind]));
8050
8513
  if (elem !== undefined) {
8051
8514
  const left = lowerer.lowerExpr(expr.left);
8052
8515
  if (left.type.kind === "dyn") {
8053
8516
  return { kind: "dynTest", test: "bytes", bytesElem: elem, value: left, type: BOOL, loc };
8054
8517
  }
8055
- if (left.type.kind === "bytes") {
8056
- return {
8057
- kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
8058
- result: { kind: "boolLit", value: left.type.elem === elem, type: BOOL, loc }, type: BOOL, loc,
8059
- };
8060
- }
8518
+ return { kind: "dynTest", test: "bytes", bytesElem: elem,
8519
+ value: lowerer.coerceInto(expr.left, left, DYN), type: BOOL, loc };
8061
8520
  }
8062
8521
  }
8063
8522
  // `x instanceof RegExp` over a union with a regex arm (the
@@ -8079,9 +8538,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8079
8538
  return { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: left, type: BOOL, loc };
8080
8539
  }
8081
8540
  }
8082
- if (left.type.kind === "regex" && left.kind === "varRef" && ts.isIdentifier(expr.left)) {
8083
- return { kind: "boolLit", value: true, type: BOOL, loc };
8084
- }
8541
+ return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [lowerer.coerceInto(expr.left, left, DYN)], type: BOOL, loc };
8085
8542
  }
8086
8543
  // `x instanceof X` where X is a class VALUE (a classval-typed
8087
8544
  // binding): the target is DYNAMIC — a hierarchy target reads its
@@ -8100,6 +8557,10 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8100
8557
  lowerer.unsupported("SC1090", expr, "'instanceof' against a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic)");
8101
8558
  }
8102
8559
  const left = lowerer.lowerExpr(expr.left);
8560
+ if (left.type.kind === "dyn") {
8561
+ const classValue = lowerer.lowerExpr(expr.right);
8562
+ return classInstanceOf(lowerer, left, targetInfo, loc, classValue);
8563
+ }
8103
8564
  if (left.type.kind !== "object") {
8104
8565
  lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
8105
8566
  }
@@ -8124,6 +8585,11 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8124
8585
  }
8125
8586
  lowerer.unsupported("SC1090", expr, "statically-decided 'instanceof' on computed operands (bind the values to variables first)");
8126
8587
  }
8588
+ const constructor = tryLowerExpression(lowerer, expr.right);
8589
+ if (constructor?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
8590
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
8591
+ return checkedClassInstanceOf(lowerer, value, constructor, loc);
8592
+ }
8127
8593
  lowerer.unsupported("SC1090", expr.right, "'instanceof' right-hand sides other than classes declared in the program");
8128
8594
  }
8129
8595
  // A catch binding on the left: the runtime test against the class's
@@ -8218,7 +8684,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8218
8684
  }
8219
8685
  }
8220
8686
  if (left.type.kind !== "object") {
8221
- lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
8687
+ return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
8688
+ result: { kind: "boolLit", value: false, type: BOOL, loc }, type: BOOL, loc };
8222
8689
  }
8223
8690
  const lhsInfo = lowerer.classes.get(left.type.className);
8224
8691
  if (!lhsInfo)
@@ -8446,9 +8913,9 @@ function lowerInExpression(lowerer, expr, loc) {
8446
8913
  // `in` shares the coercion).
8447
8914
  {
8448
8915
  const probed = tryLowerExpression(lowerer, expr.right);
8449
- if (probed && (probed.type.kind === "dyn" || isDynTypedRefType(probed.type))) {
8916
+ if (probed && lowerer.dynConvertible(probed.type)) {
8450
8917
  const k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
8451
- if (k.type.kind === "symbol" || k.type.kind === "dyn" || k.type.kind === "string" || k.type.kind === "f64") {
8918
+ if (lowerer.dynConvertible(k.type)) {
8452
8919
  const boxed = lowerer.coerceToExpected(k, DYN);
8453
8920
  const keyLocal = lowerer.declareHiddenLocal("%inKey", DYN);
8454
8921
  return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: keyLocal.id, init: boxed, loc }],
@@ -8479,7 +8946,9 @@ function lowerInExpression(lowerer, expr, loc) {
8479
8946
  return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
8480
8947
  }
8481
8948
  let recv = lowerer.lowerExpr(expr.right);
8482
- if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type)) {
8949
+ const classInfo = recv.type.kind === "classval" ? lowerer.classes.get(recv.type.className) : undefined;
8950
+ if ((classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) ||
8951
+ (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type))) {
8483
8952
  recv = lowerer.coerceToExpected(recv, DYN);
8484
8953
  }
8485
8954
  const siteType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
@@ -8728,8 +9197,8 @@ function lowerRuntimeKeyIn(lowerer, expr, loc) {
8728
9197
  }
8729
9198
  return { kind: "call", callee: helper, args: [keyIr, recv], type: BOOL, loc };
8730
9199
  }
8731
- /** A regex literal `/ab+c/gi` → regexLit (interned per (pattern, flags)
8732
- * by the backend). The TS parser has already syntax-checked the literal;
9200
+ /** A regex literal `/ab+c/gi` → regexLit (fresh state per evaluation,
9201
+ * shared bytecode per (pattern, flags) pair). The TS parser has already syntax-checked the literal;
8733
9202
  * what remains here is the flag-alphabet fence (d and v are
8734
9203
  * declared-valid TS flags outside this slice). Named capture groups
8735
9204
  * `(?<name>...)` and `\k<name>` backreferences compile — libregexp
@@ -8870,14 +9339,52 @@ function constInitializerOf(lowerer, ident) {
8870
9339
  * caller keeps its fence), or the groups list (possibly empty — a
8871
9340
  * traced regex WITHOUT named groups is the Node-undefined case). */
8872
9341
  export function matchResultNamedGroupsOf(lowerer, e) {
9342
+ let producer = e;
9343
+ while (ts.isParenthesizedExpression(producer) || ts.isNonNullExpression(producer))
9344
+ producer = producer.expression;
9345
+ if (ts.isIdentifier(producer)) {
9346
+ const init = constInitializerOf(lowerer, producer);
9347
+ if (init !== null)
9348
+ return matchResultNamedGroupsOf(lowerer, init);
9349
+ }
8873
9350
  const reExpr = matchProducerRegexOf(lowerer, e);
8874
9351
  if (reExpr === null)
8875
9352
  return null;
9353
+ if (ts.isCallExpression(producer) && ts.isPropertyAccessExpression(producer.expression) &&
9354
+ producer.expression.name.text === "match") {
9355
+ const flags = staticRegexFlagsOf(lowerer, reExpr);
9356
+ if (flags === null)
9357
+ return null;
9358
+ // Global String.match returns whole matches without a groups
9359
+ // property, even when the pattern contains named captures.
9360
+ if (flags.includes("g"))
9361
+ return [];
9362
+ }
8876
9363
  const pattern = staticRegexPatternOf(lowerer, reExpr);
8877
9364
  if (pattern === null)
8878
9365
  return null;
8879
9366
  return namedCaptureGroupsOfPattern(pattern);
8880
9367
  }
9368
+ function staticRegexFlagsOf(lowerer, node) {
9369
+ let expr = node;
9370
+ while (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr))
9371
+ expr = expr.expression;
9372
+ if (ts.isRegularExpressionLiteral(expr))
9373
+ return expr.text.slice(expr.text.lastIndexOf("/") + 1);
9374
+ if (ts.isIdentifier(expr)) {
9375
+ const init = constInitializerOf(lowerer, expr);
9376
+ return init !== null ? staticRegexFlagsOf(lowerer, init) : null;
9377
+ }
9378
+ if (ts.isNewExpression(expr) && ts.isIdentifier(expr.expression) &&
9379
+ lowerer.isStdlibGlobal(expr.expression, "RegExp")) {
9380
+ const flags = expr.arguments?.[1];
9381
+ if (!flags)
9382
+ return "";
9383
+ if (ts.isStringLiteral(flags) || ts.isNoSubstitutionTemplateLiteral(flags))
9384
+ return flags.text;
9385
+ }
9386
+ return null;
9387
+ }
8881
9388
  /** The regex EXPRESSION whose match produced `e` (see
8882
9389
  * matchResultNamedGroupsOf for the traced shapes). */
8883
9390
  function matchProducerRegexOf(lowerer, e) {
@@ -8967,11 +9474,10 @@ function matchAllRegexOf(lowerer, e) {
8967
9474
  * member fence);
8968
9475
  * - a nullable receiver (the JS `s.match(re).groups` idiom) throws
8969
9476
  * Node's exact TypeError on the unit arms;
8970
- * - every declared name is a key (nonparticipating groups hold "" —
8971
- * divergence 51's rule, exactly the slice elements they project);
9477
+ * - every declared name is a key (nonparticipating groups hold undefined,
9478
+ * exactly the slice elements they project);
8972
9479
  * - ES2025 duplicate names (distinct alternatives) project the first
8973
- * participating occurrence: at most one participates, and a
8974
- * participating-empty "" falls through to slots that are "" anyway;
9480
+ * participating occurrence, including a participating empty string;
8975
9481
  * - key order is group declaration order (declaredOrder), Node's own.
8976
9482
  * The record has no prototype — Node's groups object is null-prototype,
8977
9483
  * observable only through util.inspect's "[Object: null prototype]"
@@ -9048,12 +9554,8 @@ function matchSliceUnionArms(lowerer, unionId) {
9048
9554
  }
9049
9555
  return { arrTag, units };
9050
9556
  }
9051
- /** The groups-record projection call: the result is the CHECKER'S OWN
9052
- * shape — `{ [key: string]: string }`, the hybrid index-signature
9053
- * record with no declared fields — holding one OVERFLOW entry per
9054
- * group name in declaration order, so every downstream consumer
9055
- * (recordKeyGet reads, Object.keys, JSON, the `| undefined` union
9056
- * wrap) meets exactly the type tsc reported. One lifted helper per
9557
+ /** The groups-record projection holds string | undefined values, including
9558
+ * an own overflow entry for every nonparticipating capture. One lifted helper per
9057
9559
  * (index list, receiver representation) builds it; see
9058
9560
  * lowerMatchGroupsRead for the semantics the body encodes. */
9059
9561
  export function lowerGroupsProjection(lowerer, recv, groups, loc) {
@@ -9071,7 +9573,8 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
9071
9573
  order.push(g.name);
9072
9574
  }
9073
9575
  }
9074
- const shapeId = lowerer.shapes.intern([], false, STRING);
9576
+ const valueType = arrayValueType(lowerer, STRING);
9577
+ const shapeId = lowerer.shapes.intern([], false, valueType);
9075
9578
  const recordT = { kind: "record", shapeId };
9076
9579
  const recvKey = recv.type.kind === "union" ? recv.type.unionId : "arr";
9077
9580
  const key = `regexgroups:${shapeId}:${groups.map((g) => `${g.name}=${g.index}`).join(",")}:${recvKey}`;
@@ -9113,7 +9616,7 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
9113
9616
  body.push({ kind: "varDecl", localId: "arr.0", init: narrowed, loc });
9114
9617
  mRef = { kind: "varRef", localId: "arr.0", type: arrayOf(STRING), loc };
9115
9618
  }
9116
- const elem = (index) => ({ kind: "arrayGet", arr: mRef, index: numLit(index, loc), type: STRING, loc });
9619
+ const elem = (index) => arrayValueRead(lowerer, mRef, numLit(index, loc), STRING, loc);
9117
9620
  const values = new Map();
9118
9621
  order.forEach((name, i) => {
9119
9622
  const idxs = indicesByName.get(name);
@@ -9121,16 +9624,16 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
9121
9624
  values.set(name, elem(idxs[0]));
9122
9625
  return;
9123
9626
  }
9124
- // Duplicates: the first non-"" occurrence (at most one
9125
- // participates; all-"" answers "" like any nonparticipating slot).
9627
+ // Duplicates: the first participating occurrence. Empty strings
9628
+ // participate; undefined means this alternative did not match.
9126
9629
  const id = `g${i}.0`;
9127
- locals.push({ id, name: `g${i}`, type: STRING, mutable: true });
9630
+ locals.push({ id, name: `g${i}`, type: valueType, mutable: true });
9128
9631
  body.push({ kind: "varDecl", localId: id, init: elem(idxs[0]), loc });
9129
- const gRef = { kind: "varRef", localId: id, type: STRING, loc };
9632
+ const gRef = { kind: "varRef", localId: id, type: valueType, loc };
9130
9633
  for (const idx of idxs.slice(1)) {
9131
9634
  body.push({
9132
9635
  kind: "if",
9133
- cond: { kind: "strEq", negated: false, left: gRef, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc },
9636
+ cond: { kind: "unionIsTag", unionId: valueType.kind === "union" ? valueType.unionId : "", tag: valueType.kind === "union" ? lowerer.armTag(valueType.unionId, UNDEFINED_T) : -1, negated: false, value: gRef, type: BOOL, loc },
9134
9637
  then: [{ kind: "assign", localId: id, value: elem(idx), loc }],
9135
9638
  else_: null,
9136
9639
  loc,
@@ -9224,6 +9727,15 @@ export function lowerFieldRead(lowerer, expr) {
9224
9727
  if (receiverIr?.kind === "record") {
9225
9728
  const shape = lowerer.shapes.get(receiverIr.shapeId);
9226
9729
  if (shape?.indexValue && !shape.fields.some((f) => f.name === expr.name.text)) {
9730
+ const actual = tryLowerExpression(lowerer, expr.expression);
9731
+ if (actual?.type.kind === "dyn")
9732
+ return { kind: "dynKeyGet", value: actual,
9733
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) }, type: DYN, loc: locOf(expr) };
9734
+ if (actual?.type.kind === "record") {
9735
+ const field = lowerer.shapes.get(actual.type.shapeId)?.fields.find((field) => field.name === expr.name.text);
9736
+ if (field)
9737
+ return { kind: "recordGet", obj: actual, shapeId: actual.type.shapeId, field: field.name, type: field.type, loc: locOf(expr) };
9738
+ }
9227
9739
  lowerer.unsupported("SC1090", expr, `dot access to index-signature keys (spell it r["${expr.name.text}"] — brackets are the index-signature form)`);
9228
9740
  }
9229
9741
  }
@@ -9370,7 +9882,7 @@ export function lowerUnionProperty(lowerer, expr) {
9370
9882
  // instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
9371
9883
  // missing-property semantics and dispatch class data through its native
9372
9884
  // capsule instead of demanding a field shared by every union arm.
9373
- if (isJsSourceFile(expr.getSourceFile()) && def.arms.some(isDynTypedRefType) && lowerer.dynConvertible(value.type)) {
9885
+ if (isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(value.type)) {
9374
9886
  return { kind: "dynKeyGet", value: lowerer.coerceToExpected(value, DYN), key,
9375
9887
  ...(hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr) };
9376
9888
  }
@@ -9504,6 +10016,10 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
9504
10016
  * or accessor dispatch. Resolve the property from the represented class;
9505
10017
  * the optional checker view must not turn its getter into a record read. */
9506
10018
  function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved) {
10019
+ const represented = saved ?? lowerer.lowerExpr(receiverNode);
10020
+ if (represented.type.kind === "dyn" && isJsSourceFile(receiverNode.getSourceFile()) && !fieldName.startsWith("#")) {
10021
+ return { container: "dynamic", obj: represented, field: fieldName, fieldType: DYN };
10022
+ }
9507
10023
  const info = lowerer.classes.get(receiverType.className);
9508
10024
  if (!info) {
9509
10025
  // A receiver typed as a class whose collection deferred: the
@@ -9513,14 +10029,14 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
9513
10029
  }
9514
10030
  const fieldType = info.fields.get(fieldName);
9515
10031
  const lowerObjectReceiver = () => {
9516
- let obj = saved ?? lowerer.lowerExpr(receiverNode);
10032
+ let obj = represented;
9517
10033
  if (obj.type.kind === "union") {
9518
10034
  const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
9519
10035
  obj = helper
9520
10036
  ? { kind: "call", callee: helper, args: [obj], type: receiverType, loc: locOf(receiverNode) }
9521
10037
  : lowerer.maybeNarrow(obj, receiverNode);
9522
10038
  }
9523
- return obj.type.kind === "dyn" ? lowerer.coerceInto(receiverNode, obj, receiverType) : obj;
10039
+ return lowerer.coerceInto(receiverNode, obj, receiverType);
9524
10040
  };
9525
10041
  if (fieldName === "cause" && receiverType.className !== "%DOMException") {
9526
10042
  let root = info;
@@ -9532,6 +10048,11 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
9532
10048
  }
9533
10049
  if (fieldType) {
9534
10050
  const obj = lowerObjectReceiver();
10051
+ if (isJsSourceFile(receiverNode.getSourceFile()) && !fieldName.startsWith("#") &&
10052
+ [...lowerer.classes.values()].some((candidate) => lowerer.isSubclassOf(candidate.def.name, receiverType.className) &&
10053
+ (candidate.methods.has(`get:${fieldName}`) || candidate.methods.has(`set:${fieldName}`)))) {
10054
+ return { container: "dynamic", obj: lowerer.coerceToExpected(obj, DYN), field: fieldName, fieldType: DYN };
10055
+ }
9535
10056
  return { container: "class", obj, className: receiverType.className, field: fieldName, fieldType };
9536
10057
  }
9537
10058
  // Accessor property: either half declared anywhere on the chain
@@ -9549,6 +10070,12 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
9549
10070
  fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
9550
10071
  };
9551
10072
  }
10073
+ if (info.decl && isJsSourceFile(info.decl.getSourceFile())) {
10074
+ const property = lowerer.checker.getPropertyOfType(lowerer.checker.getTypeAtLocation(receiverNode), fieldName);
10075
+ if (property && lowerer.checker.declarationsOf(property).some((declaration) => ts.isPropertyAccessExpression(declaration) || ts.isBinaryExpression(declaration) || ts.isElementAccessExpression(declaration))) {
10076
+ return { container: "dynamic", obj: lowerer.coerceToExpected(lowerObjectReceiver(), DYN), field: fieldName, fieldType: DYN };
10077
+ }
10078
+ }
9552
10079
  return null;
9553
10080
  }
9554
10081
  /** A structural assertion can leave the value in its original class
@@ -9574,6 +10101,8 @@ function representedClassFieldTarget(lowerer, receiverNode, fieldName, value) {
9574
10101
  * receivers) a declared accessor property. Returns the pieces of a
9575
10102
  * fieldSet/recordSet/accessor-call (minus value/kind) or null. */
9576
10103
  export function fieldTarget(lowerer, access) {
10104
+ if (lowerer.cjsLocalModuleBindingOf(access.expression))
10105
+ return null;
9577
10106
  if (lowerer.chainBlocked(access))
9578
10107
  return null;
9579
10108
  if (access.name.text === "stackTraceLimit" && lowerer.isStdlibGlobal(access.expression, "Error")) {
@@ -9581,7 +10110,7 @@ export function fieldTarget(lowerer, access) {
9581
10110
  }
9582
10111
  const stored = ts.isIdentifier(access.expression)
9583
10112
  ? lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type : undefined;
9584
- const receiverIr = stored?.kind === "record" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
10113
+ const receiverIr = stored?.kind === "record" || stored?.kind === "object" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
9585
10114
  if (receiverIr?.kind === "object") {
9586
10115
  return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
9587
10116
  }
@@ -9688,8 +10217,17 @@ export function fieldTarget(lowerer, access) {
9688
10217
  return declaredArms.every((a) => ivArms.some((b) => typeEquals(a, b)));
9689
10218
  };
9690
10219
  if (!nameSym || nameSym.name === ts.InternalSymbolName.Index || canonicalized()) {
9691
- const obj = lowerer.lowerExpr(access.expression);
9692
- return { container: "recordOvf", obj, shapeId: receiverIr.shapeId, field: access.name.text, fieldType: shape.indexValue };
10220
+ let obj = lowerer.lowerExpr(access.expression);
10221
+ if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
10222
+ const present = lowerer.stripUndefinedArm(obj.type);
10223
+ obj = present.kind === "record" ? lowerer.runtimeOptionalPropertyReceiver(access.expression, obj, present, access.name.text) ?? obj : lowerer.maybeNarrow(obj, access.expression);
10224
+ }
10225
+ if (obj.type.kind !== "record")
10226
+ return null;
10227
+ const actualShape = lowerer.shapes.get(obj.type.shapeId);
10228
+ if (!actualShape?.indexValue)
10229
+ return null;
10230
+ return { container: "recordOvf", obj, shapeId: obj.type.shapeId, field: access.name.text, fieldType: actualShape.indexValue };
9693
10231
  }
9694
10232
  }
9695
10233
  return null;
@@ -9787,6 +10325,13 @@ function symbolFieldTarget(lowerer, expr) {
9787
10325
  * tsc-clean (the property types as the setter's param), but Node yields
9788
10326
  * undefined, which these property types cannot represent. */
9789
10327
  export function fieldGetExpr(lowerer, target, loc, blame) {
10328
+ if (target.container === "dynamic")
10329
+ return {
10330
+ kind: "dynKeyGet", value: target.obj,
10331
+ key: { kind: "strLit", value: target.field, type: STRING, loc }, type: DYN, loc,
10332
+ };
10333
+ if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
10334
+ return errorPropertyRead(lowerer, target.obj, target.field);
9790
10335
  if (target.container === "errorStackLimit") {
9791
10336
  return { kind: "libCall", fn: "error.stackLimitGet", args: [], type: F64, loc };
9792
10337
  }
@@ -9812,6 +10357,11 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
9812
10357
  if (!getF) {
9813
10358
  lowerer.unsupported("SC1090", blame, `reading a property that has only a setter ('${target.field}' — Node would yield undefined)`);
9814
10359
  }
10360
+ if (!target.field.startsWith("#") && isJsSourceFile(blame.getSourceFile()) && lowerer.dynConvertible(getF.sig.ret)) {
10361
+ const value = { kind: "dynKeyGet", value: lowerer.coerceToExpected(target.obj, DYN),
10362
+ key: { kind: "strLit", value: target.field, type: STRING, loc }, type: DYN, loc };
10363
+ return lowerer.coerceToExpected(value, getF.sig.ret);
10364
+ }
9815
10365
  return lowerer.accessorCall(target.className, `get:${target.field}`, target.obj, [], getF.sig.ret, loc);
9816
10366
  }
9817
10367
  // Record accessor properties: the read IS a call of the %get: closure
@@ -9889,6 +10439,13 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
9889
10439
  * call). A write to a getter-only property never gets here in a clean
9890
10440
  * program (tsc's TS2540 is the fence); the rejection is the backstop. */
9891
10441
  export function fieldSetStmt(lowerer, target, value, loc, blame) {
10442
+ if (target.container === "dynamic")
10443
+ return {
10444
+ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet",
10445
+ args: [target.obj, { kind: "strLit", value: target.field, type: STRING, loc }, lowerer.coerceToExpected(value, DYN)], type: VOID, loc }, loc,
10446
+ };
10447
+ if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
10448
+ return errorPropertyWrite(lowerer, target.obj, value, target.field);
9892
10449
  if (target.container === "errorStackLimit") {
9893
10450
  return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.stackLimitSet", args: [value], type: VOID, loc }, loc };
9894
10451
  }
@@ -9996,13 +10553,13 @@ export function lowerPromiseRejectCall(lowerer, call, access) {
9996
10553
  // identity survives to catch/unhandledRejection observers), and the
9997
10554
  // result is promise<dyn> when no context names a concrete one.
9998
10555
  const reason = lowerer.lowerExpr(reasonNode);
9999
- if (reason.type.kind === "dyn") {
10556
+ if (reason.type.kind === "dyn" || lowerer.dynConvertible(reason.type) && reason.type.kind !== "object") {
10000
10557
  // The result only ever REJECTS, so the inner type is unobservable:
10001
10558
  // adopt the context's promise type when one names it, else the
10002
10559
  // checker's own Promise<never> reading (promise<void> — exactly the
10003
10560
  // Error-reason form below).
10004
10561
  const resultT = type?.kind === "promise" ? type : { kind: "promise", inner: VOID };
10005
- return { kind: "intrinsic", name: "promise.reject", args: [reason], type: resultT, loc };
10562
+ return { kind: "intrinsic", name: "promise.reject", args: [lowerer.coerceInto(reasonNode, reason, DYN)], type: resultT, loc };
10006
10563
  }
10007
10564
  // An Error reason with NO context-named promise type (a bare
10008
10565
  // top-level `Promise.reject(new Error())` — the unhandled-rejection
@@ -10054,7 +10611,7 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
10054
10611
  const loc = locOf(call);
10055
10612
  const inner = first.inner;
10056
10613
  const entryT = { kind: "promise", inner };
10057
- const elems = argNode.elements.map((el) => lowerer.lowerExpr(el));
10614
+ const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, entryT));
10058
10615
  const entries = { kind: "arrayLit", elems, type: arrayOf(entryT), loc };
10059
10616
  const type = inner.kind === "void"
10060
10617
  ? { kind: "promise", inner: VOID }
@@ -10086,7 +10643,10 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
10086
10643
  // NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
10087
10644
  // receiver and member read are saved before evaluating the RHS.
10088
10645
  if (ts.isPropertyAccessExpression(access)) {
10089
- const probed = tryLowerExpression(lowerer, access.expression);
10646
+ let probed = tryLowerExpression(lowerer, access.expression);
10647
+ const staticClass = probed?.type.kind === "classval" ? lowerer.classes.get(probed.type.className) : undefined;
10648
+ if (probed && staticClass && hasRuntimeStatics(staticClass))
10649
+ probed = lowerer.coerceToExpected(probed, DYN);
10090
10650
  if (probed?.type.kind === "dyn") {
10091
10651
  const receiver = save(probed, "%compoundReceiver");
10092
10652
  const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
@@ -10095,6 +10655,14 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
10095
10655
  const rhs = save(rhsNode
10096
10656
  ? lowerer.lowerExpr(rhsNode)
10097
10657
  : { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
10658
+ if (isJsSourceFile(access.getSourceFile()) && lowerer.dynConvertible(rhs.type)) {
10659
+ const numeric = ["-", "*", "/", "%", "**"].includes(op);
10660
+ const right = lowerer.coerceToExpected(rhs, DYN);
10661
+ const value = save({ kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
10662
+ args: op === "+" ? [read, right] : [read, right, { kind: "strLit", value: op, type: STRING, loc }], type: DYN, loc }, "%compoundResult");
10663
+ body.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [receiver, key, value], type: VOID, loc }, loc });
10664
+ return { kind: "seqExpr", stmts: body, result: value, type: DYN, loc };
10665
+ }
10098
10666
  const numericRhs = rhs.type.kind === "dyn"
10099
10667
  ? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
10100
10668
  : lowerOptionalNumber(lowerer, rhs, loc);
@@ -10132,35 +10700,12 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
10132
10700
  value = { kind: "bin", op, left: read, right: numericRhs, type: F64, loc };
10133
10701
  }
10134
10702
  else if (target.fieldType.kind === "dyn" &&
10135
- isJsSourceFile(access.getSourceFile()) &&
10136
- (op === "+" || op === "-" || op === "*" || op === "/" || op === "%" || op === "**")) {
10137
- // CHECKED-DYNAMIC fields (implicit-any ctor assignments —
10138
- // countdown.js's `this[kLimit]` shape with a plain name) follow the
10139
- // JS dyn-operand binary stance: arithmetic CHECKS the dyn side to
10140
- // number (dynCheck — the catchable TypeError, never a silent
10141
- // ToNumber; SEMANTICS.md) and computes natively; the result boxes
10142
- // back into the field's dyn slot. `+=` with a string RHS is the
10143
- // string-context read: check to string, concat, box back.
10144
- const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
10145
- if (op === "+" && rhs.type.kind === "string") {
10146
- const concat = {
10147
- kind: "strConcat",
10148
- // String-context read: JS's String(unknown) over the field —
10149
- // the JS-exact dyn walker, never a checked cast.
10150
- left: { kind: "toString", operand: read, type: STRING, loc },
10151
- right: rhs,
10152
- type: STRING,
10153
- loc,
10154
- };
10155
- value = { kind: "dynFrom", value: concat, type: DYN, loc };
10156
- }
10157
- else if (rhs.type.kind === "f64" || rhs.type.kind === "dyn") {
10158
- const bin = { kind: "bin", op, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
10159
- value = { kind: "dynFrom", value: bin, type: DYN, loc };
10160
- }
10161
- else {
10162
- lowerer.unsupported("SC1043", access);
10163
- }
10703
+ isJsSourceFile(access.getSourceFile())) {
10704
+ const numeric = ["-", "*", "/", "%", "**"].includes(op);
10705
+ const left = lowerer.coerceToExpected(read, DYN);
10706
+ const right = lowerer.coerceToExpected(rhs, DYN);
10707
+ value = { kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
10708
+ args: op === "+" ? [left, right] : [left, right, { kind: "strLit", value: op, type: STRING, loc }], type: DYN, loc };
10164
10709
  }
10165
10710
  else {
10166
10711
  lowerer.unsupported("SC1043", access);
@@ -10193,8 +10738,8 @@ function lowerStreamObjectProperty(lowerer, expr) {
10193
10738
  * Interning makes every reference under one ABI the SAME immortal closure,
10194
10739
  * so `opt.type === String` compares like JS function identity. */
10195
10740
  function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
10196
- const ret = name === "String" ? STRING : name === "Number" ? F64 : BOOL;
10197
- const input = dynamicInput ? DYN : STRING;
10741
+ const ret = name === "String" ? STRING : name === "Number" ? F64 : name === "BigInt" ? BIGINT_T : BOOL;
10742
+ const input = dynamicInput || name === "BigInt" ? DYN : STRING;
10198
10743
  const fnT = funcOf([input], ret);
10199
10744
  const key = `${name}:${dynamicInput ? "dyn" : "string"}`;
10200
10745
  let fnName = lowerer.primitiveCtorFns.get(key);
@@ -10203,16 +10748,20 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
10203
10748
  lowerer.primitiveCtorFns.set(key, fnName);
10204
10749
  const v = { kind: "varRef", localId: "v.0", type: input, loc };
10205
10750
  const value = dynamicInput
10206
- ? name === "String"
10207
- ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
10208
- : name === "Number"
10209
- ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [v], type: F64, loc }
10210
- : { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
10211
- : name === "String"
10212
- ? v
10213
- : name === "Number"
10214
- ? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
10215
- : { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
10751
+ ? name === "BigInt"
10752
+ ? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
10753
+ : name === "String"
10754
+ ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
10755
+ : name === "Number"
10756
+ ? { kind: "libCall", fn: "dyn.numberConstructor", args: [v], type: F64, loc }
10757
+ : { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
10758
+ : name === "BigInt"
10759
+ ? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
10760
+ : name === "String"
10761
+ ? v
10762
+ : name === "Number"
10763
+ ? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
10764
+ : { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
10216
10765
  const fn = {
10217
10766
  name: fnName,
10218
10767
  params: [{ localId: "v.0", name: "value", type: input }],
@@ -10225,4 +10774,25 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
10225
10774
  }
10226
10775
  return { kind: "closure", fnName, captures: [], type: fnT, loc };
10227
10776
  }
10777
+ /** Drain using checked dispatch so native class and generator iterators keep their methods. */
10778
+ function checkedIteratorPack(lowerer, source, loc) {
10779
+ const name = "%iterator.pack.checked";
10780
+ if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
10781
+ const ref = (id) => ({ kind: "varRef", localId: id, type: DYN, loc });
10782
+ const property = (key) => ({ kind: "dynKeyGet", value: ref("step"), key: { kind: "strLit", value: key, type: STRING, loc }, type: DYN, loc });
10783
+ lowerer.liftedFns.push({ name, params: [{ localId: "source", name: "source", type: DYN }], returnType: DYN,
10784
+ locals: ["source", "iterator", "step", "result"].map((id) => ({ id, name: id, type: DYN, mutable: id !== "source" })),
10785
+ body: [
10786
+ { kind: "varDecl", localId: "iterator", init: { kind: "libCall", fn: "dyn.iterator", args: [ref("source"), { kind: "strLit", value: "value", type: STRING, loc }], type: DYN, loc }, loc },
10787
+ { kind: "varDecl", localId: "result", init: { kind: "dynArrLit", elems: [], type: DYN, loc }, loc },
10788
+ { kind: "while", cond: { kind: "boolLit", value: true, type: BOOL, loc }, body: [
10789
+ { kind: "varDecl", localId: "step", init: { kind: "dynInvoke", recv: ref("iterator"), method: "next", calleeName: "iterator.next", args: [], type: DYN, loc }, loc },
10790
+ { kind: "if", cond: { kind: "dynTest", test: "truthy", value: property("done"), type: BOOL, loc }, then: [{ kind: "return", value: ref("result"), loc }], else_: null, loc },
10791
+ { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.packPush", args: [ref("result"), property("value")], type: VOID, loc }, loc },
10792
+ ], loc },
10793
+ { kind: "return", value: ref("result"), loc },
10794
+ ], loc });
10795
+ }
10796
+ return { kind: "call", callee: name, args: [source], type: DYN, loc };
10797
+ }
10228
10798
  //# sourceMappingURL=lower-exprs.js.map