@scriptc/compiler 0.2.1 → 0.2.3

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 (315) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +158 -31
  2. package/dist/backend/cycle-analysis.js +63 -50
  3. package/dist/backend/cycle-analysis.js.map +1 -1
  4. package/dist/backend/cycle-analysis.test.js +51 -0
  5. package/dist/backend/cycle-analysis.test.js.map +1 -1
  6. package/dist/backend/llvm/borrowed-receivers.d.ts +9 -0
  7. package/dist/backend/llvm/borrowed-receivers.js +58 -0
  8. package/dist/backend/llvm/borrowed-receivers.js.map +1 -0
  9. package/dist/backend/llvm/call-lifetimes.d.ts +20 -0
  10. package/dist/backend/llvm/call-lifetimes.js +179 -0
  11. package/dist/backend/llvm/call-lifetimes.js.map +1 -0
  12. package/dist/backend/llvm/call-lifetimes.test.d.ts +1 -0
  13. package/dist/backend/llvm/call-lifetimes.test.js +205 -0
  14. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -0
  15. package/dist/backend/llvm/dyn.d.ts +5 -2
  16. package/dist/backend/llvm/dyn.js +163 -47
  17. package/dist/backend/llvm/dyn.js.map +1 -1
  18. package/dist/backend/llvm/emitter.d.ts +37 -1
  19. package/dist/backend/llvm/emitter.js +209 -21
  20. package/dist/backend/llvm/emitter.js.map +1 -1
  21. package/dist/backend/llvm/emitter.test.js +29 -2
  22. package/dist/backend/llvm/emitter.test.js.map +1 -1
  23. package/dist/backend/llvm/expr-async.js +1 -1
  24. package/dist/backend/llvm/expr-async.js.map +1 -1
  25. package/dist/backend/llvm/expr-calls.js +42 -5
  26. package/dist/backend/llvm/expr-calls.js.map +1 -1
  27. package/dist/backend/llvm/expr-containers.js +75 -13
  28. package/dist/backend/llvm/expr-containers.js.map +1 -1
  29. package/dist/backend/llvm/expr-dynamic.js +31 -6
  30. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  31. package/dist/backend/llvm/expr-primitives.js +6 -5
  32. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  33. package/dist/backend/llvm/expr-records.js +1 -1
  34. package/dist/backend/llvm/expr-records.js.map +1 -1
  35. package/dist/backend/llvm/expr-stream-bridges.js +75 -19
  36. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  37. package/dist/backend/llvm/expr-stream-callbacks.d.ts +2 -2
  38. package/dist/backend/llvm/expr-stream-callbacks.js +7 -57
  39. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  40. package/dist/backend/llvm/lib-dispatch.js +41 -3
  41. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  42. package/dist/backend/llvm/lib-filesystem.js +12 -9
  43. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  44. package/dist/backend/llvm/lib-shared.js +70 -2
  45. package/dist/backend/llvm/lib-shared.js.map +1 -1
  46. package/dist/backend/llvm/local-array-reads.d.ts +44 -0
  47. package/dist/backend/llvm/local-array-reads.js +313 -0
  48. package/dist/backend/llvm/local-array-reads.js.map +1 -0
  49. package/dist/backend/llvm/local-array-reads.test.d.ts +1 -0
  50. package/dist/backend/llvm/local-array-reads.test.js +446 -0
  51. package/dist/backend/llvm/local-array-reads.test.js.map +1 -0
  52. package/dist/backend/llvm/map-read-lifetimes.d.ts +36 -0
  53. package/dist/backend/llvm/map-read-lifetimes.js +128 -0
  54. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -0
  55. package/dist/backend/llvm/map-read-lifetimes.test.d.ts +1 -0
  56. package/dist/backend/llvm/map-read-lifetimes.test.js +123 -0
  57. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -0
  58. package/dist/backend/llvm/shapes.js +1 -0
  59. package/dist/backend/llvm/shapes.js.map +1 -1
  60. package/dist/backend/llvm/split.js +15 -5
  61. package/dist/backend/llvm/split.js.map +1 -1
  62. package/dist/backend/llvm/split.test.js +17 -0
  63. package/dist/backend/llvm/split.test.js.map +1 -1
  64. package/dist/backend/llvm/stack-unions.d.ts +20 -0
  65. package/dist/backend/llvm/stack-unions.js +61 -0
  66. package/dist/backend/llvm/stack-unions.js.map +1 -0
  67. package/dist/backend/llvm/stack-unions.test.d.ts +1 -0
  68. package/dist/backend/llvm/stack-unions.test.js +241 -0
  69. package/dist/backend/llvm/stack-unions.test.js.map +1 -0
  70. package/dist/backend/llvm/string-lifetimes.d.ts +12 -0
  71. package/dist/backend/llvm/string-lifetimes.js +70 -0
  72. package/dist/backend/llvm/string-lifetimes.js.map +1 -0
  73. package/dist/backend/llvm/string-lifetimes.test.d.ts +1 -0
  74. package/dist/backend/llvm/string-lifetimes.test.js +230 -0
  75. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -0
  76. package/dist/backend/mangle.d.ts +2 -0
  77. package/dist/backend/mangle.js +4 -0
  78. package/dist/backend/mangle.js.map +1 -1
  79. package/dist/backend/may-throw.js +71 -29
  80. package/dist/backend/may-throw.js.map +1 -1
  81. package/dist/backend/may-throw.test.js +104 -1
  82. package/dist/backend/may-throw.test.js.map +1 -1
  83. package/dist/backend/native-codegen.d.ts +1 -1
  84. package/dist/backend/native-codegen.js +4 -3
  85. package/dist/backend/native-codegen.js.map +1 -1
  86. package/dist/backend/native-codegen.test.js +11 -0
  87. package/dist/backend/native-codegen.test.js.map +1 -1
  88. package/dist/backend/native-toolchain.d.ts +2 -2
  89. package/dist/backend/native-toolchain.js +2 -0
  90. package/dist/backend/native-toolchain.js.map +1 -1
  91. package/dist/backend/target-diagnostics.d.ts +5 -0
  92. package/dist/backend/target-diagnostics.js +72 -1
  93. package/dist/backend/target-diagnostics.js.map +1 -1
  94. package/dist/backend/target-diagnostics.test.d.ts +1 -0
  95. package/dist/backend/target-diagnostics.test.js +68 -0
  96. package/dist/backend/target-diagnostics.test.js.map +1 -0
  97. package/dist/coverage/surface-manifest.js +5 -3
  98. package/dist/coverage/surface-manifest.js.map +1 -1
  99. package/dist/executable/executable-cache.d.ts +7 -3
  100. package/dist/executable/executable-cache.js +47 -11
  101. package/dist/executable/executable-cache.js.map +1 -1
  102. package/dist/executable/executable-cache.test.js +109 -2
  103. package/dist/executable/executable-cache.test.js.map +1 -1
  104. package/dist/executable/prepare.js +2 -1
  105. package/dist/executable/prepare.js.map +1 -1
  106. package/dist/frontend/builtin-modules.d.ts +1 -1
  107. package/dist/frontend/builtin-modules.js +1 -1
  108. package/dist/frontend/builtin-modules.js.map +1 -1
  109. package/dist/frontend/function-completion.d.ts +6 -0
  110. package/dist/frontend/function-completion.js +50 -0
  111. package/dist/frontend/function-completion.js.map +1 -0
  112. package/dist/frontend/input-tracker.js +3 -3
  113. package/dist/frontend/input-tracker.js.map +1 -1
  114. package/dist/frontend/input-tracker.test.js +38 -2
  115. package/dist/frontend/input-tracker.test.js.map +1 -1
  116. package/dist/frontend/lowering/abstract-equality.js +5 -1
  117. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  118. package/dist/frontend/lowering/checked-iterable-spread.d.ts +4 -0
  119. package/dist/frontend/lowering/checked-iterable-spread.js +30 -0
  120. package/dist/frontend/lowering/checked-iterable-spread.js.map +1 -0
  121. package/dist/frontend/lowering/checked-promise-all.d.ts +5 -0
  122. package/dist/frontend/lowering/checked-promise-all.js +31 -0
  123. package/dist/frontend/lowering/checked-promise-all.js.map +1 -0
  124. package/dist/frontend/lowering/class-callbacks.d.ts +1 -1
  125. package/dist/frontend/lowering/class-callbacks.js +7 -6
  126. package/dist/frontend/lowering/class-callbacks.js.map +1 -1
  127. package/dist/frontend/lowering/class-construction.d.ts +2 -0
  128. package/dist/frontend/lowering/class-construction.js +34 -5
  129. package/dist/frontend/lowering/class-construction.js.map +1 -1
  130. package/dist/frontend/lowering/class-descriptors.d.ts +1 -1
  131. package/dist/frontend/lowering/class-descriptors.js +12 -5
  132. package/dist/frontend/lowering/class-descriptors.js.map +1 -1
  133. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +25 -0
  134. package/dist/frontend/lowering/class-dynamic-dispatch.js +597 -78
  135. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  136. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +64 -2
  137. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
  138. package/dist/frontend/lowering/class-instance-constructor.d.ts +1 -1
  139. package/dist/frontend/lowering/class-instance-constructor.js +11 -41
  140. package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
  141. package/dist/frontend/lowering/class-method-values.d.ts +16 -0
  142. package/dist/frontend/lowering/class-method-values.js +60 -29
  143. package/dist/frontend/lowering/class-method-values.js.map +1 -1
  144. package/dist/frontend/lowering/class-prototypes.d.ts +5 -1
  145. package/dist/frontend/lowering/class-prototypes.js +69 -10
  146. package/dist/frontend/lowering/class-prototypes.js.map +1 -1
  147. package/dist/frontend/lowering/class-runtime-statics.d.ts +4 -4
  148. package/dist/frontend/lowering/class-runtime-statics.js +26 -8
  149. package/dist/frontend/lowering/class-runtime-statics.js.map +1 -1
  150. package/dist/frontend/lowering/class-static-data.d.ts +1 -0
  151. package/dist/frontend/lowering/class-static-data.js +4 -2
  152. package/dist/frontend/lowering/class-static-data.js.map +1 -1
  153. package/dist/frontend/lowering/collection-ingestion.d.ts +31 -0
  154. package/dist/frontend/lowering/collection-ingestion.js +215 -0
  155. package/dist/frontend/lowering/collection-ingestion.js.map +1 -0
  156. package/dist/frontend/lowering/containers/bytes.js +19 -4
  157. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  158. package/dist/frontend/lowering/containers/string-and-regexp.js +6 -1
  159. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  160. package/dist/frontend/lowering/deferred-module-initializers.d.ts +13 -0
  161. package/dist/frontend/lowering/deferred-module-initializers.js +410 -0
  162. package/dist/frontend/lowering/deferred-module-initializers.js.map +1 -0
  163. package/dist/frontend/lowering/deferred-module-initializers.test.d.ts +1 -0
  164. package/dist/frontend/lowering/deferred-module-initializers.test.js +92 -0
  165. package/dist/frontend/lowering/deferred-module-initializers.test.js.map +1 -0
  166. package/dist/frontend/lowering/expressions/object-literals.js +31 -1
  167. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  168. package/dist/frontend/lowering/expressions/optional-chains.js +3 -3
  169. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  170. package/dist/frontend/lowering/import-meta.d.ts +10 -0
  171. package/dist/frontend/lowering/import-meta.js +52 -0
  172. package/dist/frontend/lowering/import-meta.js.map +1 -0
  173. package/dist/frontend/lowering/lower-assert.js +105 -41
  174. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  175. package/dist/frontend/lowering/lower-builtin-values.d.ts +2 -0
  176. package/dist/frontend/lowering/lower-builtin-values.js +22 -1
  177. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  178. package/dist/frontend/lowering/lower-builtins.d.ts +7 -22
  179. package/dist/frontend/lowering/lower-builtins.js +439 -95
  180. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  181. package/dist/frontend/lowering/lower-calls.js +289 -76
  182. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  183. package/dist/frontend/lowering/lower-classes.d.ts +5 -0
  184. package/dist/frontend/lowering/lower-classes.js +443 -29
  185. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  186. package/dist/frontend/lowering/lower-containers.d.ts +6 -2
  187. package/dist/frontend/lowering/lower-containers.js +79 -20
  188. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  189. package/dist/frontend/lowering/lower-exprs.d.ts +10 -6
  190. package/dist/frontend/lowering/lower-exprs.js +329 -170
  191. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  192. package/dist/frontend/lowering/lower-generators.js +5 -1
  193. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  194. package/dist/frontend/lowering/lower-inspect.js +58 -1
  195. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  196. package/dist/frontend/lowering/lower-island.js +4 -0
  197. package/dist/frontend/lowering/lower-island.js.map +1 -1
  198. package/dist/frontend/lowering/lower-island.test.d.ts +1 -0
  199. package/dist/frontend/lowering/lower-island.test.js +51 -0
  200. package/dist/frontend/lowering/lower-island.test.js.map +1 -0
  201. package/dist/frontend/lowering/lower-modules.d.ts +1 -3
  202. package/dist/frontend/lowering/lower-modules.js +186 -82
  203. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  204. package/dist/frontend/lowering/lower-modules.test.js +69 -1
  205. package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
  206. package/dist/frontend/lowering/lower-stmts.js +172 -103
  207. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  208. package/dist/frontend/lowering/lower-stmts.test.d.ts +1 -0
  209. package/dist/frontend/lowering/lower-stmts.test.js +77 -0
  210. package/dist/frontend/lowering/lower-stmts.test.js.map +1 -0
  211. package/dist/frontend/lowering/lower-stream.js +2 -0
  212. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  213. package/dist/frontend/lowering/lower-system-errors.d.ts +6 -0
  214. package/dist/frontend/lowering/lower-system-errors.js +29 -0
  215. package/dist/frontend/lowering/lower-system-errors.js.map +1 -0
  216. package/dist/frontend/lowering/lower-url.d.ts +2 -1
  217. package/dist/frontend/lowering/lower-url.js +32 -9
  218. package/dist/frontend/lowering/lower-url.js.map +1 -1
  219. package/dist/frontend/lowering/lowerer.d.ts +30 -12
  220. package/dist/frontend/lowering/lowerer.js +268 -49
  221. package/dist/frontend/lowering/lowerer.js.map +1 -1
  222. package/dist/frontend/lowering/lowerer.test.js +38 -0
  223. package/dist/frontend/lowering/lowerer.test.js.map +1 -1
  224. package/dist/frontend/lowering/module-namespace-elements.d.ts +7 -0
  225. package/dist/frontend/lowering/module-namespace-elements.js +54 -0
  226. package/dist/frontend/lowering/module-namespace-elements.js.map +1 -0
  227. package/dist/frontend/lowering/object-factory-new.d.ts +4 -3
  228. package/dist/frontend/lowering/object-factory-new.js +27 -14
  229. package/dist/frontend/lowering/object-factory-new.js.map +1 -1
  230. package/dist/frontend/lowering/surfaces.d.ts +3 -2
  231. package/dist/frontend/lowering/surfaces.js +93 -33
  232. package/dist/frontend/lowering/surfaces.js.map +1 -1
  233. package/dist/frontend/lowering/text-decoder-encoding.js +3 -0
  234. package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
  235. package/dist/frontend/lowering/text-decoder-encoding.test.js +2 -1
  236. package/dist/frontend/lowering/text-decoder-encoding.test.js.map +1 -1
  237. package/dist/frontend/module-syntax.d.ts +1 -1
  238. package/dist/frontend/module-syntax.js +8 -1
  239. package/dist/frontend/module-syntax.js.map +1 -1
  240. package/dist/frontend/module-syntax.test.js +10 -0
  241. package/dist/frontend/module-syntax.test.js.map +1 -1
  242. package/dist/frontend/npm.d.ts +4 -0
  243. package/dist/frontend/npm.js +86 -33
  244. package/dist/frontend/npm.js.map +1 -1
  245. package/dist/frontend/npm.test.js +103 -0
  246. package/dist/frontend/npm.test.js.map +1 -1
  247. package/dist/frontend/pipeline-native.js +2 -1
  248. package/dist/frontend/pipeline-native.js.map +1 -1
  249. package/dist/frontend/program.js +4 -2
  250. package/dist/frontend/program.js.map +1 -1
  251. package/dist/frontend/services-node.js +3 -2
  252. package/dist/frontend/services-node.js.map +1 -1
  253. package/dist/frontend/services.d.ts +5 -1
  254. package/dist/frontend/services.js +9 -1
  255. package/dist/frontend/services.js.map +1 -1
  256. package/dist/frontend/services.test.js +21 -0
  257. package/dist/frontend/services.test.js.map +1 -1
  258. package/dist/frontend/ts7/ast-node.d.ts +2 -0
  259. package/dist/frontend/ts7/ast-node.js +30 -4
  260. package/dist/frontend/ts7/ast-node.js.map +1 -1
  261. package/dist/frontend/ts7/ast-node.test.js +60 -1
  262. package/dist/frontend/ts7/ast-node.test.js.map +1 -1
  263. package/dist/frontend/type-mapper.js +89 -14
  264. package/dist/frontend/type-mapper.js.map +1 -1
  265. package/dist/frontend/union-discriminants.js +22 -7
  266. package/dist/frontend/union-discriminants.js.map +1 -1
  267. package/dist/index.js +19 -12
  268. package/dist/index.js.map +1 -1
  269. package/dist/ir/analysis.js +1 -0
  270. package/dist/ir/analysis.js.map +1 -1
  271. package/dist/ir/integer-loops.d.ts +7 -0
  272. package/dist/ir/integer-loops.js +23 -0
  273. package/dist/ir/integer-loops.js.map +1 -1
  274. package/dist/ir/integer-loops.test.d.ts +1 -0
  275. package/dist/ir/integer-loops.test.js +41 -0
  276. package/dist/ir/integer-loops.test.js.map +1 -0
  277. package/dist/ir/ir.d.ts +44 -26
  278. package/dist/ir/ir.js +69 -11
  279. package/dist/ir/ir.js.map +1 -1
  280. package/dist/ir/ir.test.js +18 -0
  281. package/dist/ir/ir.test.js.map +1 -1
  282. package/dist/ir/serialize.js +21 -1
  283. package/dist/ir/serialize.js.map +1 -1
  284. package/dist/ir/serialize.test.js +21 -0
  285. package/dist/ir/serialize.test.js.map +1 -1
  286. package/dist/ir/traverse.d.ts +0 -3
  287. package/dist/ir/traverse.js +33 -20
  288. package/dist/ir/traverse.js.map +1 -1
  289. package/dist/ir/validate.js +216 -80
  290. package/dist/ir/validate.js.map +1 -1
  291. package/dist/ir/validate.test.js +244 -1
  292. package/dist/ir/validate.test.js.map +1 -1
  293. package/dist/library/compiler-self-identity.js +1 -1
  294. package/dist/library/compiler-self-identity.test.js +2 -2
  295. package/dist/library/compiler-self-identity.test.js.map +1 -1
  296. package/dist/library/fence-eval.js +9 -2
  297. package/dist/library/fence-eval.js.map +1 -1
  298. package/dist/library/fence-eval.test.d.ts +1 -0
  299. package/dist/library/fence-eval.test.js +21 -0
  300. package/dist/library/fence-eval.test.js.map +1 -0
  301. package/dist/library/int-infer.d.ts +2 -2
  302. package/dist/library/int-infer.js +10 -7
  303. package/dist/library/int-infer.js.map +1 -1
  304. package/dist/library/int-infer.test.js +44 -0
  305. package/dist/library/int-infer.test.js.map +1 -1
  306. package/dist/library/prepare.js +2 -1
  307. package/dist/library/prepare.js.map +1 -1
  308. package/dist/native/runtime-typescript.d.ts +4 -0
  309. package/dist/native/runtime-typescript.js +61 -0
  310. package/dist/native/runtime-typescript.js.map +1 -0
  311. package/dist/native/runtime-typescript.test.d.ts +1 -0
  312. package/dist/native/runtime-typescript.test.js +41 -0
  313. package/dist/native/runtime-typescript.test.js.map +1 -0
  314. package/package.json +20 -20
  315. package/surface-manifest.json +385 -29
@@ -4,20 +4,48 @@ import { varRef } from "../../ir/build.js";
4
4
  import { everyStmtList, transformExpr, transformStmtList } from "../../ir/traverse.js";
5
5
  import { dynUndefinedExpr, PoisonError } from "./lowerer.js";
6
6
  import { implicitDefaultInstance } from "./lower-calls.js";
7
- import { accessorCall, classValueRef, findGenericMethodOn, findMethodOn, findStaticOn, genericOverrideBelow, upcastTo } from "./lower-classes.js";
7
+ import { accessorCall, classValueRef, findGenericMethodOn, findGenericStaticOn, findMethodOn, findStaticOn, genericOverrideBelow, upcastTo } from "./lower-classes.js";
8
8
  import { classPrototypeData, hasClassPrototypeData, reflectClassPrototype } from "./class-prototypes.js";
9
9
  import { errorPropertyRead, errorPropertyWrite, errorToStringCall, errorToStringMethod, refreshErrorPropertyDispatch, refreshErrorMethodDispatch } from "./error-methods.js";
10
10
  import { GeneratorDynamicDispatch } from "./generator-dynamic-dispatch.js";
11
- import { classMethodValue } from "./class-method-values.js";
11
+ import { classMethodValue, reflectedClassStaticMethodValue, refreshClassMethodValueSelections } from "./class-method-values.js";
12
+ import { classStaticData } from "./class-static-data.js";
13
+ import { isJsSourceFile } from "../program.js";
12
14
  import * as ts from "../ts7/adapter.js";
13
15
  import { SYMBOL_T } from "../../ir/ir.js";
14
16
  import { isClassCallback } from "./class-callbacks.js";
15
17
  import { refreshDescriptorGuards } from "./class-descriptors.js";
16
18
  import { ClassConstructionDispatch } from "./class-construction.js";
19
+ import { emitterRooted } from "./lower-event-emitter.js";
17
20
  const STREAM_METHODS = new Set(["on", "addListener", "once", "prependListener", "prependOnceListener", "off", "removeListener", "read", "pause", "resume", "isPaused", "destroy"]);
21
+ const EMITTER_METHODS = new Set(["emit", "on", "addListener", "once", "prependListener", "prependOnceListener", "off", "removeListener"]);
18
22
  const STREAM_BOOL_PROPERTIES = new Set(["readable", "readableEnded", "writable", "writableEnded", "writableFinished", "writableNeedDrain", "destroyed", "closed", "readableObjectMode", "writableObjectMode", "allowHalfOpen"]);
19
23
  const STREAM_NUM_PROPERTIES = new Set(["readableLength", "readableHighWaterMark", "writableLength", "writableHighWaterMark", "writableCorked"]);
20
24
  const streamProperty = (name) => STREAM_BOOL_PROPERTIES.has(name) || STREAM_NUM_PROPERTIES.has(name);
25
+ function symbolMemberKey(lowerer, info, name, loc) {
26
+ if (name.startsWith("sym:"))
27
+ return lowerer.coerceToExpected({ kind: "libCall", fn: "sym.wellKnown",
28
+ args: [{ kind: "strLit", value: name.slice(4), type: STRING, loc }], type: SYMBOL_T, loc }, DYN);
29
+ if (info.symbolFields !== undefined) {
30
+ for (const [symbol, member] of info.symbolFields) {
31
+ if (member !== name)
32
+ continue;
33
+ const global = lowerer.globalsBySymbol.get(symbol);
34
+ if (global)
35
+ return lowerer.coerceToExpected(varRef(global.id, global.type, loc), DYN);
36
+ }
37
+ }
38
+ if (info.symbolMethods !== undefined) {
39
+ for (const [symbol, member] of info.symbolMethods) {
40
+ if (member !== name)
41
+ continue;
42
+ const global = lowerer.globalsBySymbol.get(symbol);
43
+ if (global)
44
+ return lowerer.coerceToExpected(varRef(global.id, global.type, loc), DYN);
45
+ }
46
+ }
47
+ return null;
48
+ }
21
49
  export function classInstanceOf(lowerer, value, info, loc, classValue) {
22
50
  const name = `%dyn.class.${classValue ? "valueInstanceof" : "instanceof"}:${info.def.name}`;
23
51
  const params = [{ localId: "value", name: "value", type: DYN },
@@ -29,7 +57,7 @@ export function classInstanceOf(lowerer, value, info, loc, classValue) {
29
57
  { kind: "varDecl", localId: "candidate", init: varRef("value", DYN, loc), loc },
30
58
  { kind: "while", cond: { kind: "ternary", cond: { kind: "dynTest", test: "nullish", value: candidate, type: BOOL, loc },
31
59
  then: { kind: "boolLit", value: false, type: BOOL, loc }, else_: { kind: "dynTest", test: "object", value: candidate, type: BOOL, loc }, type: BOOL, loc }, body: [
32
- { kind: "assign", localId: "candidate", value: { kind: "libCall", fn: "dyn.getPrototype", args: [candidate], type: DYN, loc }, loc },
60
+ { kind: "assign", localId: "candidate", value: { kind: "libCall", fn: "dyn.getPrototype", args: [candidate], type: DYN, loc, prototypeIdentityOnly: true }, loc },
33
61
  { kind: "if", cond: { kind: "dynScalarEq", left: candidate, right: prototype, type: BOOL, loc }, then: [
34
62
  { kind: "return", value: { kind: "boolLit", value: true, type: BOOL, loc }, loc },
35
63
  ], else_: null, loc },
@@ -57,6 +85,82 @@ export function classPropertiesHelper(lowerer, loc) {
57
85
  lowerer.liftedFns.push(helper);
58
86
  return helper;
59
87
  }
88
+ /** A for-in presence recheck needs only the live property table, avoiding
89
+ * value conversion or a dispatch branch for every possible member name. */
90
+ export function classForInHasKey(lowerer, value, key, loc) {
91
+ const name = "%dyn.class.forInHasKey";
92
+ if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
93
+ lowerer.liftedFns.push({ name, params: [{ localId: "value", name: "value", type: DYN }, { localId: "key", name: "key", type: STRING }], returnType: BOOL,
94
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }, { id: "key", name: "key", type: STRING, mutable: false }],
95
+ body: [{ kind: "return", value: { kind: "libCall", fn: "dyn.hasKey", args: [
96
+ { kind: "call", callee: classPropertiesHelper(lowerer, loc).name, args: [varRef("value", DYN, loc)], type: DYN, loc },
97
+ varRef("key", STRING, loc),
98
+ ], type: BOOL, loc }, loc }], loc,
99
+ });
100
+ }
101
+ return { kind: "call", callee: name, args: [value, key], type: BOOL, loc };
102
+ }
103
+ /** Record field creation after the dispatch worklist closes, including writes
104
+ * in generated member tables. Native values stay in their slots; the bag
105
+ * owns only their property presence, attributes, and insertion position. */
106
+ export function trackClassFieldCreation(lowerer, functions) {
107
+ if (![...lowerer.classes.values()].some((info) => info.def.tracksOwnFields))
108
+ return;
109
+ const loc = functions[0].loc;
110
+ const name = "%dyn.class.fieldCreated";
111
+ const value = varRef("value", DYN, loc), key = varRef("key", DYN, loc);
112
+ const bag = varRef("bag", DYN, loc);
113
+ const created = (receiver, className, field, loc) => ({ kind: "exprStmt", expr: {
114
+ kind: "call", callee: name, args: [lowerer.coerceToExpected(receiver, DYN), symbolMemberKey(lowerer, lowerer.classes.get(className), field, loc) ??
115
+ lowerer.coerceToExpected({ kind: "strLit", value: field, type: STRING, loc }, DYN)], type: VOID, loc,
116
+ }, loc });
117
+ const tracked = (className, field) => !!lowerer.classes.get(className)?.def.tracksOwnFields && (isClassOwnEnumerableFieldName(field) ||
118
+ symbolMemberKey(lowerer, lowerer.classes.get(className), field, loc) !== null);
119
+ for (const fn of functions) {
120
+ const instrumented = new WeakSet();
121
+ const local = (type, loc) => {
122
+ const id = `%fieldCreated.${fn.locals.length}`;
123
+ fn.locals.push({ id, name: id, type, mutable: false });
124
+ return { kind: "varRef", localId: id, type, loc };
125
+ };
126
+ fn.body = transformStmtList(fn.body, {
127
+ stmt: (statement) => {
128
+ if (statement.kind !== "fieldSet" || instrumented.has(statement) || !tracked(statement.className, statement.field))
129
+ return statement;
130
+ const receiver = local(statement.obj.type, statement.loc);
131
+ const store = { ...statement, obj: receiver };
132
+ instrumented.add(store);
133
+ return { kind: "block", body: [
134
+ { kind: "varDecl", localId: receiver.localId, init: statement.obj, loc: statement.loc },
135
+ store, created(receiver, statement.className, statement.field, statement.loc),
136
+ ], loc: statement.loc };
137
+ },
138
+ expr: (expression) => {
139
+ if (expression.kind !== "fieldIncDec" || instrumented.has(expression) || !tracked(expression.className, expression.field))
140
+ return expression;
141
+ const receiver = local(expression.obj.type, expression.loc), result = local(expression.type, expression.loc);
142
+ const update = { ...expression, obj: receiver };
143
+ instrumented.add(update);
144
+ return { kind: "seqExpr", stmts: [
145
+ { kind: "varDecl", localId: receiver.localId, init: expression.obj, loc: expression.loc },
146
+ { kind: "varDecl", localId: result.localId, init: update, loc: expression.loc },
147
+ created(receiver, expression.className, expression.field, expression.loc),
148
+ ], result, type: expression.type, loc: expression.loc };
149
+ },
150
+ });
151
+ }
152
+ functions.push({ name, params: [{ localId: "value", name: "value", type: DYN }, { localId: "key", name: "key", type: DYN }], returnType: VOID,
153
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }, { id: "key", name: "key", type: DYN, mutable: false }, { id: "bag", name: "bag", type: DYN, mutable: false }],
154
+ body: [
155
+ { kind: "varDecl", localId: "bag", init: { kind: "call", callee: classPropertiesHelper(lowerer, loc).name, args: [value], type: DYN, loc }, loc },
156
+ { kind: "if", cond: { kind: "unary", op: "!", operand: { kind: "libCall", fn: "dyn.hasOwnComputed", args: [bag, key], type: BOOL, loc }, type: BOOL, loc }, then: [
157
+ { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.defineProperty", args: [bag, lowerer.coerceToExpected(key, DYN), { kind: "dynObjLit", fields: [
158
+ { key: { kind: "strLit", value: "value", type: STRING, loc }, value: dynUndefinedExpr(loc) },
159
+ ...["writable", "enumerable", "configurable"].map((key) => ({ key: { kind: "strLit", value: key, type: STRING, loc }, value: lowerer.coerceToExpected({ kind: "boolLit", value: true, type: BOOL, loc }, DYN) })),
160
+ ], type: DYN, loc }], type: DYN, loc }, loc },
161
+ ], else_: null, loc },
162
+ ], loc, });
163
+ }
60
164
  /** Calls on native class capsules keep the instance's compiled methods. The
61
165
  * reachable-body fixed point discovers both the boxed classes and method
62
166
  * names before generating checked native dispatch; ordinary dyn receivers
@@ -73,6 +177,11 @@ export class ClassDynamicDispatch {
73
177
  dispatches = new Map();
74
178
  properties = new Map();
75
179
  computed = new Map();
180
+ computedClassHelpers = new Map();
181
+ normalization = null;
182
+ normalizationKey = "";
183
+ staticPropertyBag = null;
184
+ staticBagClasses = new Set();
76
185
  propertyBag = null;
77
186
  errorStrings = new Map();
78
187
  bagClasses = new Set();
@@ -84,12 +193,26 @@ export class ClassDynamicDispatch {
84
193
  constructed = new Set();
85
194
  instanceTests = new Map();
86
195
  jsonPrototypes = new Set();
196
+ serializesClasses = false;
87
197
  descriptorHelper = null;
88
198
  prototypeHelper = null;
89
199
  prototypeClasses = new Set();
200
+ prototypeIdentityHelper = null;
201
+ prototypeIdentityClasses = new Set();
90
202
  instancePrototypes = new Map();
203
+ checkedCasts = new Map();
204
+ enumeration = null;
205
+ enumerationClasses = new Set();
91
206
  process(lowerer, functions) {
207
+ // Dispatch tables grow between worklist waves. Keep their normalization
208
+ // entry ahead of every newly inserted branch after rebuilding the tables.
209
+ for (const fn of this.normalizedDispatches()) {
210
+ const first = fn.body[0];
211
+ if (first?.kind === "assign" && first.value.kind === "call" && first.value.callee === this.normalization?.name)
212
+ fn.body.shift();
213
+ }
92
214
  let changed = refreshErrorMethodDispatch(lowerer, functions);
215
+ changed = refreshClassMethodValueSelections(lowerer) || changed;
93
216
  changed = refreshErrorPropertyDispatch(lowerer, functions) || changed;
94
217
  changed = this.generatorDispatch.process(lowerer, functions, this.generated) || changed;
95
218
  changed = this.constructionDispatch.process(lowerer, functions) || changed;
@@ -123,12 +246,27 @@ export class ClassDynamicDispatch {
123
246
  };
124
247
  for (const fn of functions)
125
248
  everyStmtList(fn.body, {
126
- stmt: () => true,
249
+ stmt: (statement) => {
250
+ // Field-creation instrumentation can box a base-constructor receiver
251
+ // even when only its derived instances otherwise cross the boundary.
252
+ if (statement.kind === "fieldSet" && lowerer.classes.get(statement.className)?.def.tracksOwnFields &&
253
+ isClassOwnEnumerableFieldName(statement.field))
254
+ discover(statement.obj.type);
255
+ return true;
256
+ },
127
257
  expr: (expr) => {
258
+ if (expr.kind === "jsonStringify" || expr.kind === "libCall" &&
259
+ (expr.fn === "json.stringifyValue" || expr.fn === "json.stringifyReplacer")) {
260
+ this.serializesClasses = true;
261
+ }
128
262
  switch (expr.kind) {
129
263
  case "dynFrom":
130
264
  discover(expr.value.type);
131
265
  break;
266
+ case "fieldIncDec":
267
+ if (lowerer.classes.get(expr.className)?.def.tracksOwnFields && isClassOwnEnumerableFieldName(expr.field))
268
+ discover(expr.obj.type);
269
+ break;
132
270
  case "call":
133
271
  if (expr.callee === "%dyn.class.properties" && expr.args[0]?.kind === "dynFrom" && isDynTypedRefType(expr.args[0].value.type))
134
272
  rewrite.add(fn);
@@ -137,9 +275,13 @@ export class ClassDynamicDispatch {
137
275
  case "dynInvoke":
138
276
  rewrite.add(fn);
139
277
  break;
278
+ case "dynCheck":
279
+ if (isDynTypedRefType(expr.type))
280
+ rewrite.add(fn);
281
+ break;
140
282
  case "libCall":
141
283
  if (expr.fn === "bytes.construct" || expr.fn === "dyn.keySet" || expr.fn === "dyn.keySetComputed" || (expr.fn === "dyn.iterator" || expr.fn === "dyn.arrayFromIterator") || expr.fn === "dyn.toString" || expr.fn === "dyn.stringConstructor" ||
142
- expr.fn === "dyn.hasKeyComputed" || expr.fn === "dyn.hasOwnComputed" || expr.fn === "dyn.propertyIsEnumerableComputed" || expr.fn === "dyn.defineProperty" || expr.fn === "dyn.getPrototype")
284
+ expr.fn === "dyn.forInKeys" || expr.fn === "dyn.hasKeyComputed" || expr.fn === "dyn.hasOwnComputed" || expr.fn === "dyn.propertyIsEnumerableComputed" || expr.fn === "dyn.defineProperty" || expr.fn === "dyn.getPrototype" || expr.fn === "dyn.reflectGet" || expr.fn === "dyn.reflectSet")
143
285
  rewrite.add(fn);
144
286
  break;
145
287
  }
@@ -203,7 +345,19 @@ export class ClassDynamicDispatch {
203
345
  then: [{ kind: "return", value: prototype, loc }], else_: null, loc });
204
346
  changed = true;
205
347
  }
206
- if (!this.jsonPrototypes.has(className) && info.decl && findMethodOn(lowerer, info, "toJSON")) {
348
+ if (this.prototypeIdentityHelper && !this.prototypeIdentityClasses.has(className)) {
349
+ this.prototypeIdentityClasses.add(className);
350
+ const loc = this.prototypeIdentityHelper.loc;
351
+ const type = { kind: "object", className };
352
+ const value = varRef("value", DYN, loc);
353
+ const prototype = classPrototypeData(lowerer, info, loc, { kind: "dynCheck", value, type, loc });
354
+ if (prototype)
355
+ this.prototypeIdentityHelper.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.typedRefIs", args: [value,
356
+ { kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc },
357
+ then: [{ kind: "return", value: prototype, loc }], else_: null, loc });
358
+ changed = true;
359
+ }
360
+ if (this.serializesClasses && !this.jsonPrototypes.has(className) && info.decl && findMethodOn(lowerer, info, "toJSON")) {
207
361
  this.jsonPrototypes.add(className);
208
362
  const loc = this.propertyBag.loc;
209
363
  const method = classMethodValue(lowerer, info.decl, info, "toJSON", loc);
@@ -225,7 +379,7 @@ export class ClassDynamicDispatch {
225
379
  }
226
380
  }
227
381
  }
228
- for (const plan of this.propertyReceivers(lowerer)) {
382
+ for (const plan of this.propertyReceivers(lowerer, false, false)) {
229
383
  const info = plan.info;
230
384
  const className = info.def.name;
231
385
  let instancePrototype = this.instancePrototypes.get(plan.capsule.def.name);
@@ -258,13 +412,15 @@ export class ClassDynamicDispatch {
258
412
  cond: { kind: "instanceOf", value: receiver, className, type: BOOL, loc }, then: [returned], else_: null, loc });
259
413
  changed = true;
260
414
  }
261
- const needsPrototype = (current) => hasClassPrototypeData(current) || current.subclasses.some(needsPrototype);
415
+ const needsPrototype = (current) => hasClassPrototypeData(current) || current.subclasses.some((child) => lowerer.classCanBeConstructed(child) && needsPrototype(child));
262
416
  const prototypeFor = (current, receiver, loc) => {
263
417
  const currentReceiver = receiver.type.kind === "object" && receiver.type.className !== current.def.name
264
418
  ? { kind: "downcast", value: receiver, type: { kind: "object", className: current.def.name }, loc }
265
419
  : receiver;
266
420
  let result = classPrototypeData(lowerer, current, loc, currentReceiver);
267
421
  for (const child of current.subclasses) {
422
+ if (!lowerer.classCanBeConstructed(child))
423
+ continue;
268
424
  const selected = prototypeFor(child, receiver, loc);
269
425
  if (selected && result)
270
426
  result = { kind: "ternary", cond: { kind: "instanceOf", value: receiver, className: child.def.name, type: BOOL, loc },
@@ -285,6 +441,18 @@ export class ClassDynamicDispatch {
285
441
  continue;
286
442
  this.bagClasses.add(plan.key);
287
443
  this.ensurePropertyBag(info);
444
+ if (info.decl && isJsSourceFile(info.decl.getSourceFile())) {
445
+ let root = info;
446
+ while (root.base && !root.base.def.runtime)
447
+ root = root.base;
448
+ const track = (current) => {
449
+ current.def.tracksOwnFields = true;
450
+ current.subclasses.forEach(track);
451
+ };
452
+ // Runtime-owned roots keep their dedicated property contracts.
453
+ if (!root.base?.def.runtime)
454
+ track(root);
455
+ }
288
456
  const loc = this.propertyBag.loc;
289
457
  const { receiver, condition } = this.propertyReceiver(plan, loc);
290
458
  const bag = { kind: "fieldGet", obj: receiver, className, field: PROPERTY_BAG, type: DYN, loc };
@@ -314,10 +482,11 @@ export class ClassDynamicDispatch {
314
482
  const found = byMethod.get(method);
315
483
  if (found)
316
484
  return found;
317
- const matching = [...this.boxed].flatMap((name) => {
318
- const info = lowerer.classes.get(name);
319
- if (!info || info.fields.has(method) || info.builtinEmitter || info.builtinError)
485
+ const matching = this.propertyReceivers(lowerer, true).flatMap(({ info }) => {
486
+ if (!info || info.fields.has(method) || info.builtinError)
320
487
  return [];
488
+ if (emitterRooted(lowerer, info) && EMITTER_METHODS.has(method))
489
+ return [info];
321
490
  if (info.builtinStream)
322
491
  return STREAM_METHODS.has(method) ? [info] : [];
323
492
  return findMethodOn(lowerer, info, method) || findGenericMethodOn(lowerer, info, method) ? [info] : [];
@@ -331,10 +500,71 @@ export class ClassDynamicDispatch {
331
500
  // every reachability pass. Recompute this set as new bodies appear.
332
501
  if (this.generated.has(fn) || !rewrite.has(fn))
333
502
  continue;
503
+ const keyInitializers = new Map();
504
+ const written = new Set();
505
+ everyStmtList(fn.body, { expr: (expression) => {
506
+ if (expression.kind === "incDec" || expression.kind === "assignExpr")
507
+ written.add(expression.localId);
508
+ return true;
509
+ }, stmt: (statement) => {
510
+ if (statement.kind === "varDecl" && statement.init)
511
+ keyInitializers.set(statement.localId, statement.init);
512
+ if (statement.kind === "assign")
513
+ written.add(statement.localId);
514
+ return true;
515
+ } });
516
+ const keyValue = (value) => {
517
+ const visited = new Set();
518
+ while (true) {
519
+ if (value.kind === "dynFrom") {
520
+ value = value.value;
521
+ continue;
522
+ }
523
+ if (value.kind === "libCall" && value.fn === "dyn.propertyKey") {
524
+ value = value.args[0];
525
+ continue;
526
+ }
527
+ if (value.kind === "varRef" && !written.has(value.localId) && !visited.has(value.localId) && keyInitializers.has(value.localId)) {
528
+ visited.add(value.localId);
529
+ value = keyInitializers.get(value.localId);
530
+ continue;
531
+ }
532
+ return value;
533
+ }
534
+ };
334
535
  fn.body = transformStmtList(fn.body, {
335
536
  stmt: (stmt) => stmt,
336
537
  expr: (expr) => {
337
- if (expr.kind === "libCall" && expr.fn === "dyn.getPrototype") {
538
+ if (expr.kind === "dynCheck" && isDynTypedRefType(expr.type)) {
539
+ const type = expr.type;
540
+ let cast = this.checkedCasts.get(type.className);
541
+ if (!cast) {
542
+ const loc = expr.loc;
543
+ const fn = { name: `%dyn.class.cast:${type.className}`,
544
+ params: [{ localId: "value", name: "value", type: DYN }],
545
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: type,
546
+ body: [{ kind: "return", value: { ...expr, value: varRef("value", DYN, loc) }, loc }], loc };
547
+ cast = { fn, classes: new Set() };
548
+ this.checkedCasts.set(type.className, cast);
549
+ this.generated.add(fn);
550
+ lowerer.liftedFns.push(fn);
551
+ changed = true;
552
+ }
553
+ return { kind: "call", callee: cast.fn.name, args: [expr.value], type, loc: expr.loc };
554
+ }
555
+ if (expr.kind === "libCall" && expr.fn === "dyn.getPrototype" && expr.args[0]?.kind !== "dynObjLit") {
556
+ if (expr.prototypeIdentityOnly) {
557
+ if (!this.prototypeIdentityHelper) {
558
+ const loc = expr.loc;
559
+ this.prototypeIdentityHelper = { name: "%dyn.class.prototypeIdentity", params: [{ localId: "value", name: "value", type: DYN }],
560
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: DYN, loc,
561
+ body: [{ kind: "return", value: { ...expr, args: [varRef("value", DYN, loc)] }, loc }] };
562
+ lowerer.liftedFns.push(this.prototypeIdentityHelper);
563
+ this.generated.add(this.prototypeIdentityHelper);
564
+ changed = true;
565
+ }
566
+ return { kind: "call", callee: this.prototypeIdentityHelper.name, args: expr.args, type: DYN, loc: expr.loc };
567
+ }
338
568
  if (!this.prototypeHelper) {
339
569
  const loc = expr.loc;
340
570
  this.prototypeHelper = { name: "%dyn.class.getPrototype", params: [{ localId: "value", name: "value", type: DYN }],
@@ -394,6 +624,19 @@ export class ClassDynamicDispatch {
394
624
  }
395
625
  return { kind: "call", callee: this.constructDispatch.name, args: expr.args, type: DYN, loc: expr.loc };
396
626
  }
627
+ if (expr.kind === "libCall" && expr.fn === "dyn.forInKeys") {
628
+ if (!this.enumeration) {
629
+ const loc = expr.loc;
630
+ const value = varRef("p.0", DYN, loc);
631
+ this.enumeration = { name: "%dyn.class.forInKeys", params: [{ localId: "p.0", name: "value", type: DYN }], returnType: DYN,
632
+ locals: [{ id: "p.0", name: "value", type: DYN, mutable: false }],
633
+ body: [{ kind: "return", value: { ...expr, args: [value] }, loc }], loc };
634
+ this.generated.add(this.enumeration);
635
+ lowerer.liftedFns.push(this.enumeration);
636
+ changed = true;
637
+ }
638
+ return { kind: "call", callee: this.enumeration.name, args: expr.args, type: DYN, loc: expr.loc };
639
+ }
397
640
  if (expr.kind === "libCall" && expr.fn === "dyn.defineProperty") {
398
641
  const loc = expr.loc;
399
642
  if (!this.descriptorHelper) {
@@ -418,13 +661,24 @@ export class ClassDynamicDispatch {
418
661
  }
419
662
  return { kind: "call", callee: this.descriptorHelper.name, args: expr.args, type: DYN, loc };
420
663
  }
421
- const computedRead = expr.kind === "dynKeyGet" && expr.key.kind !== "strLit" ? expr : null;
422
- const computedWrite = expr.kind === "libCall" && (expr.fn === "dyn.keySetComputed" || expr.fn === "dyn.keySet" && expr.args[1]?.kind !== "strLit") ? expr : null;
664
+ if (expr.kind === "dynKeyGet" && expr.value.kind === "dynObjLit")
665
+ return expr;
666
+ const reflectRead = expr.kind === "libCall" && expr.fn === "dyn.reflectGet" ? expr : null;
667
+ const reflectWrite = expr.kind === "libCall" && expr.fn === "dyn.reflectSet" ? expr : null;
668
+ const reflect = !!reflectRead || !!reflectWrite;
669
+ const computedRead = reflectRead ? { kind: "dynKeyGet", value: reflectRead.args[0], key: reflectRead.args[1], type: DYN, loc: expr.loc }
670
+ : expr.kind === "dynKeyGet" && expr.key.kind !== "strLit" ? expr : null;
671
+ const computedWrite = reflectWrite ? { ...reflectWrite, fn: "dyn.keySetComputed", args: reflectWrite.args.slice(0, 3), type: VOID }
672
+ : expr.kind === "libCall" && (expr.fn === "dyn.keySetComputed" || expr.fn === "dyn.keySet" && expr.args[1]?.kind !== "strLit") ? expr : null;
423
673
  const computedProbe = expr.kind === "libCall" && ["dyn.hasKeyComputed", "dyn.hasOwnComputed", "dyn.propertyIsEnumerableComputed"].includes(expr.fn) ? expr : null;
424
674
  if (computedRead || computedWrite || computedProbe) {
425
675
  const probe = computedProbe ? computedProbe.fn === "dyn.hasKeyComputed" ? "in" : computedProbe.fn === "dyn.hasOwnComputed" ? "own" : "enumerable" : undefined;
426
- const dynamicKey = !!computedProbe || computedWrite?.fn === "dyn.keySetComputed" || computedRead?.key.type.kind === "dyn";
427
- const key = JSON.stringify([!!computedWrite, computedRead?.optional ?? false, dynamicKey, probe]);
676
+ const dynamicKey = reflect || !!computedProbe || computedWrite?.fn === "dyn.keySetComputed" || computedRead?.key.type.kind === "dyn";
677
+ const sourceKey = keyValue(computedRead?.key ?? (computedWrite ?? computedProbe).args[1]);
678
+ const numericKey = sourceKey.type.kind === "f64" ||
679
+ sourceKey.kind === "dynFrom" && sourceKey.value.type.kind === "f64" ||
680
+ sourceKey.kind === "toString" && sourceKey.operand.type.kind === "f64";
681
+ const key = JSON.stringify([!!computedWrite, computedRead?.optional ?? false, dynamicKey, probe, numericKey, reflect]);
428
682
  let dispatch = this.computed.get(key);
429
683
  if (!dispatch) {
430
684
  const loc = expr.loc;
@@ -432,23 +686,28 @@ export class ClassDynamicDispatch {
432
686
  { localId: "p.0", name: "value", type: DYN },
433
687
  { localId: "p.key", name: "key", type: dynamicKey ? DYN : STRING },
434
688
  ...(computedWrite ? [{ localId: "p.1", name: "stored", type: DYN }] : []),
689
+ ...(reflect ? [{ localId: "p.reflectReceiver", name: "receiver", type: DYN }] : []),
435
690
  ];
436
691
  const keyLocal = dynamicKey ? "key.string" : "p.key";
437
692
  const bag = { kind: "call", callee: this.propertyBag.name, args: [varRef("p.0", DYN, loc)], type: DYN, loc };
438
- const fallback = computedProbe
693
+ const reflectionFallback = (target, property) => ({ kind: "libCall", fn: computedWrite ? "dyn.reflectSet" : "dyn.reflectGet",
694
+ args: [target, lowerer.coerceToExpected(property, DYN), ...(computedWrite ? [varRef("p.1", DYN, loc)] : []), varRef("p.reflectReceiver", DYN, loc)], type: computedWrite ? BOOL : DYN, loc });
695
+ const fallback = reflect ? reflectionFallback(bag, varRef(keyLocal, STRING, loc)) : computedProbe
439
696
  ? { ...computedProbe, fn: probe === "in" ? "dyn.hasKey" : probe === "own" ? "dyn.hasOwn" : "dyn.propertyIsEnumerable", args: [bag, varRef(keyLocal, STRING, loc)] }
440
697
  : computedRead
441
698
  ? { ...computedRead, value: bag, key: varRef(keyLocal, STRING, loc) }
442
699
  : { ...computedWrite, fn: "dyn.keySet", args: [bag, varRef(keyLocal, STRING, loc), varRef("p.1", DYN, loc)] };
443
700
  const helper = {
444
- name: `%dyn.class.computed.${this.computed.size}`, params, returnType: computedProbe ? BOOL : computedWrite ? VOID : DYN,
701
+ name: `%dyn.class.computed.${this.computed.size}`, params, returnType: reflect && computedWrite || computedProbe ? BOOL : computedWrite ? VOID : DYN,
445
702
  locals: [...params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })), ...(dynamicKey ? [{ id: keyLocal, name: "key", type: STRING, mutable: false }] : [])],
446
- body: computedWrite
703
+ body: reflect ? [{ kind: "return", value: fallback, loc }] : computedWrite
447
704
  ? [{ kind: "exprStmt", expr: fallback, loc }, { kind: "return", value: null, loc }]
448
705
  : [{ kind: "return", value: fallback, loc }], loc,
449
706
  };
450
707
  if (dynamicKey)
451
- helper.body.unshift({ kind: "if", cond: { kind: "dynTest", test: "symbol", value: varRef("p.key", DYN, loc), type: BOOL, loc }, then: computedProbe ? [
708
+ helper.body.unshift({ kind: "if", cond: { kind: "dynTest", test: "symbol", value: varRef("p.key", DYN, loc), type: BOOL, loc }, then: reflect ? [
709
+ { kind: "return", value: reflectionFallback(bag, varRef("p.key", DYN, loc)), loc },
710
+ ] : computedProbe ? [
452
711
  { kind: "return", value: { ...computedProbe, args: [bag, varRef("p.key", DYN, loc)] }, loc },
453
712
  ] : computedRead ? [
454
713
  { kind: "return", value: { ...computedRead, value: bag, key: varRef("p.key", DYN, loc) }, loc },
@@ -456,18 +715,18 @@ export class ClassDynamicDispatch {
456
715
  { kind: "exprStmt", expr: { ...computedWrite, args: [bag, varRef("p.key", DYN, loc), varRef("p.1", DYN, loc)] }, loc },
457
716
  { kind: "return", value: null, loc },
458
717
  ], else_: null, loc }, { kind: "if", cond: { kind: "dynTest", test: "nullish", value: varRef("p.0", DYN, loc), type: BOOL, loc }, then: [
459
- ...(computedProbe ? [{ kind: "return", value: { ...computedProbe, args: [varRef("p.0", DYN, loc), varRef("p.key", DYN, loc)] }, loc }] : computedRead ? [{ kind: "return", value: { ...computedRead, value: varRef("p.0", DYN, loc), key: varRef("p.key", DYN, loc) }, loc }] : [
718
+ ...(reflect ? [{ kind: "return", value: reflectionFallback(varRef("p.0", DYN, loc), varRef("p.key", DYN, loc)), loc }] : computedProbe ? [{ kind: "return", value: { ...computedProbe, args: [varRef("p.0", DYN, loc), varRef("p.key", DYN, loc)] }, loc }] : computedRead ? [{ kind: "return", value: { ...computedRead, value: varRef("p.0", DYN, loc), key: varRef("p.key", DYN, loc) }, loc }] : [
460
719
  { kind: "exprStmt", expr: { ...computedWrite, args: [varRef("p.0", DYN, loc), varRef("p.key", DYN, loc), varRef("p.1", DYN, loc)] }, loc },
461
720
  { kind: "return", value: null, loc },
462
721
  ]),
463
722
  ], else_: null, loc }, { kind: "varDecl", localId: keyLocal, init: { kind: "libCall", fn: "dyn.toStringCoerce", args: [varRef("p.key", DYN, loc)], type: STRING, loc }, loc });
464
- dispatch = { write: !!computedWrite, fn: helper, classes: new Set(), keyLocal, branchIndex: dynamicKey ? 3 : 0, dynamicKey, probe };
723
+ dispatch = { write: !!computedWrite, reflect, fn: helper, classes: new Set(), keyLocal, branchIndex: dynamicKey ? 3 : 0, dynamicKey, numericKey, probe };
465
724
  this.computed.set(key, dispatch);
466
725
  this.generated.add(helper);
467
726
  lowerer.liftedFns.push(helper);
468
727
  changed = true;
469
728
  }
470
- return { kind: "call", callee: dispatch.fn.name, args: computedRead ? [computedRead.value, computedRead.key] : (computedWrite ?? computedProbe).args, type: dispatch.fn.returnType, loc: expr.loc };
729
+ return { kind: "call", callee: dispatch.fn.name, args: reflect ? (reflectRead ?? reflectWrite).args : computedRead ? [computedRead.value, computedRead.key] : (computedWrite ?? computedProbe).args, type: dispatch.fn.returnType, loc: expr.loc };
471
730
  }
472
731
  const read = expr.kind === "dynKeyGet" && expr.key.kind === "strLit" ? expr : null;
473
732
  const write = expr.kind === "libCall" && expr.fn === "dyn.keySet" && expr.args[1]?.kind === "strLit" ? expr : null;
@@ -478,9 +737,14 @@ export class ClassDynamicDispatch {
478
737
  if (!dispatch) {
479
738
  const loc = expr.loc;
480
739
  const params = (write ? [0, 1] : [0]).map((i) => ({ localId: `p.${i}`, name: `p${i}`, type: DYN }));
481
- const bag = { kind: "call", callee: this.propertyBag.name, args: [varRef("p.0", DYN, loc)], type: DYN, loc };
740
+ const instanceBag = { kind: "call", callee: this.propertyBag.name, args: [varRef("p.0", DYN, loc)], type: DYN, loc };
741
+ const bag = read && !["name", "length", "apply"].includes(name)
742
+ ? { kind: "call", callee: this.constructorPropertyBag(lowerer, loc).name, args: [instanceBag], type: DYN, loc }
743
+ : instanceBag;
482
744
  const fallback = read
483
- ? { ...read, value: bag }
745
+ ? { kind: "ternary", cond: { kind: "dynScalarEq", left: bag, right: varRef("p.0", DYN, loc), type: BOOL, loc },
746
+ then: { ...read, value: bag }, else_: { kind: "libCall", fn: "dyn.bagGet",
747
+ args: [bag, read.key, varRef("p.0", DYN, loc)], type: DYN, loc }, type: DYN, loc }
484
748
  : { ...write, args: [bag, write.args[1], varRef("p.1", DYN, loc)] };
485
749
  const helper = {
486
750
  name: `%dyn.class.property.${this.properties.size}`, params, returnType: write ? VOID : DYN,
@@ -507,13 +771,19 @@ export class ClassDynamicDispatch {
507
771
  const invoke = iterator
508
772
  ? { kind: "dynInvoke", recv: iterator.args[0], method: "sym:iterator", calleeName: "value[Symbol.iterator]", args: [], type: DYN, loc: iterator.loc }
509
773
  : expr.kind === "dynInvoke" ? expr : null;
510
- if (!invoke || candidates(invoke.method).length === 0 && !(invoke.method === "toString" && lowerer.classes.has("%Error")))
774
+ const staticCandidates = invoke && [...this.boxedConstructors].some((name) => {
775
+ const info = lowerer.classes.get(name);
776
+ return info && findStaticOn(lowerer, info, invoke.method)?.method;
777
+ });
778
+ if (!invoke || candidates(invoke.method).length === 0 && !staticCandidates && !(invoke.method === "toString" && lowerer.classes.has("%Error")))
511
779
  return expr;
512
- const key = JSON.stringify([invoke.method, invoke.calleeName, invoke.args.length, iterator?.fn, iterator?.args[1]]);
780
+ const key = JSON.stringify([invoke.method, invoke.args.length, iterator?.fn, iterator?.args[1]]);
513
781
  let dispatch = this.dispatches.get(key);
514
782
  if (!dispatch) {
515
783
  const params = [invoke.recv, ...invoke.args].map((_, i) => ({ localId: `p.${i}`, name: `p${i}`, type: DYN }));
516
784
  params.splice(1, 0, { localId: "p.callback", name: "callback", type: DYN });
785
+ iterator?.args.slice(1).forEach((argument, i) => params.push({ localId: `p.iterator.${i}`, name: `iterator${i}`, type: argument.type }));
786
+ params.push({ localId: "p.calleeName", name: "calleeName", type: STRING });
517
787
  const callback = {
518
788
  name: `%dyn.class.callback.${this.dispatches.size}`, params: [params[0]], returnType: DYN,
519
789
  locals: [{ id: "p.0", name: "receiver", type: DYN, mutable: false }],
@@ -521,10 +791,10 @@ export class ClassDynamicDispatch {
521
791
  };
522
792
  const helper = {
523
793
  name: `%dyn.class.call.${this.dispatches.size}`, params, returnType: DYN,
524
- locals: params.map((p) => ({ id: p.localId, name: p.name, type: DYN, mutable: false })),
794
+ locals: params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })),
525
795
  body: [{ kind: "return", value: iterator
526
- ? { ...iterator, args: [varRef("p.0", DYN, expr.loc), ...iterator.args.slice(1)] }
527
- : { ...invoke, recv: varRef("p.0", DYN, expr.loc), args: invoke.args.map((_, i) => varRef(`p.${i + 1}`, DYN, expr.loc)) }, loc: expr.loc }],
796
+ ? { ...iterator, args: [varRef("p.0", DYN, expr.loc), ...iterator.args.slice(1).map((argument, i) => varRef(`p.iterator.${i}`, argument.type, expr.loc))] }
797
+ : { ...invoke, calleeNameValue: varRef("p.calleeName", STRING, expr.loc), recv: varRef("p.0", DYN, expr.loc), args: invoke.args.map((_, i) => varRef(`p.${i + 1}`, DYN, expr.loc)) }, loc: expr.loc }],
528
798
  loc: expr.loc,
529
799
  };
530
800
  if (invoke.method === "toString" && lowerer.classes.has("%Error")) {
@@ -533,7 +803,7 @@ export class ClassDynamicDispatch {
533
803
  helper.body.unshift({ kind: "if", cond: { kind: "dynTest", test: "error", value, type: BOOL, loc }, then: [
534
804
  { kind: "return", value: {
535
805
  kind: "dynCall", callee: errorToStringMethod(lowerer, { kind: "dynCheck", value, type: { kind: "object", className: "%Error" }, loc }),
536
- receiver: value, calleeName: invoke.calleeName, args: invoke.args.map((_, i) => varRef(`p.${i + 1}`, DYN, loc)), type: DYN, loc,
806
+ receiver: value, calleeName: invoke.calleeName, calleeNameValue: varRef("p.calleeName", STRING, loc), args: invoke.args.map((_, i) => varRef(`p.${i + 1}`, DYN, loc)), type: DYN, loc,
537
807
  }, loc },
538
808
  ], else_: null, loc });
539
809
  }
@@ -550,27 +820,67 @@ export class ClassDynamicDispatch {
550
820
  const receiver = varRef(local.id, DYN, expr.loc);
551
821
  return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: local.id, init: invoke.recv, loc: expr.loc }], result: {
552
822
  kind: "call", callee: dispatch.fn.name, args: [receiver,
553
- { kind: "call", callee: dispatch.callback.name, args: [receiver], type: DYN, loc: expr.loc }, ...invoke.args], type: DYN, loc: expr.loc,
823
+ { kind: "call", callee: dispatch.callback.name, args: [receiver], type: DYN, loc: expr.loc }, ...invoke.args, ...(iterator?.args.slice(1) ?? []), invoke.calleeNameValue ?? { kind: "strLit", value: invoke.calleeName, type: STRING, loc: expr.loc }], type: DYN, loc: expr.loc,
554
824
  }, type: DYN, loc: expr.loc };
555
825
  },
556
826
  });
557
827
  }
828
+ for (const [target, cast] of this.checkedCasts) {
829
+ for (const source of this.boxed) {
830
+ if (source === target || !lowerer.isSubclassOf(target, source) || cast.classes.has(source))
831
+ continue;
832
+ cast.classes.add(source);
833
+ const loc = cast.fn.loc;
834
+ const value = varRef("value", DYN, loc);
835
+ const sourceType = { kind: "object", className: source };
836
+ const object = { kind: "dynCheck", value, type: sourceType, loc };
837
+ cast.fn.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.typedRefIs", args: [value,
838
+ { kind: "strLit", value: typeKey(sourceType), type: STRING, loc }], type: BOOL, loc }, then: [
839
+ { kind: "if", cond: { kind: "instanceOf", value: object, className: target, type: BOOL, loc }, then: [
840
+ { kind: "return", value: { kind: "downcast", value: object, type: cast.fn.returnType, loc }, loc },
841
+ ], else_: null, loc },
842
+ ], else_: null, loc });
843
+ changed = true;
844
+ }
845
+ }
558
846
  for (const dispatch of this.dispatches.values()) {
559
- for (const info of candidates(dispatch.source.method)) {
560
- if (dispatch.classes.has(info.def.name))
847
+ for (const name of this.boxedConstructors) {
848
+ const info = lowerer.classes.get(name);
849
+ const found = info && findStaticOn(lowerer, info, dispatch.source.method);
850
+ const key = `constructor:${name}`;
851
+ if (!found?.method || dispatch.classes.has(key))
852
+ continue;
853
+ const method = reflectedClassStaticMethodValue(lowerer, found.declarer, dispatch.source.method);
854
+ if (!method)
561
855
  continue;
562
- dispatch.classes.add(info.def.name);
856
+ dispatch.classes.add(key);
563
857
  const loc = dispatch.source.loc;
564
- const type = { kind: "object", className: info.def.name };
565
858
  const receiver = varRef("p.0", DYN, loc);
859
+ const condition = { kind: "libCall", fn: "dyn.classIs", args: [receiver,
860
+ { kind: "strLit", value: typeKey({ kind: "classval", className: name }), type: STRING, loc }], type: BOOL, loc };
861
+ dispatch.callback.body.unshift({ kind: "if", cond: condition, then: [{ kind: "return", value: {
862
+ kind: "dynObjLit", fields: [{ key: { kind: "strLit", value: "value", type: STRING, loc }, value: lowerer.coerceToExpected(method, DYN) }], type: DYN, loc,
863
+ }, loc }], else_: null, loc });
864
+ dispatch.fn.body.unshift({ kind: "if", cond: condition, then: [{ kind: "return", value: {
865
+ kind: "dynCall", callee: { kind: "dynKeyGet", value: varRef("p.callback", DYN, loc), key: { kind: "strLit", value: "value", type: STRING, loc }, type: DYN, loc },
866
+ receiver, calleeName: dispatch.source.calleeName, calleeNameValue: varRef("p.calleeName", STRING, loc), args: dispatch.source.args.map((_, i) => varRef(`p.${i + 1}`, DYN, loc)), type: DYN, loc,
867
+ }, loc }], else_: null, loc });
868
+ changed = true;
869
+ }
870
+ for (const plan of this.propertyReceivers(lowerer, true)) {
871
+ const { info } = plan;
872
+ if (!candidates(dispatch.source.method).includes(info) || dispatch.classes.has(plan.key))
873
+ continue;
874
+ dispatch.classes.add(plan.key);
875
+ const loc = dispatch.source.loc;
876
+ const value = varRef("p.0", DYN, loc);
877
+ const { receiver, condition } = this.propertyReceiver(plan, loc);
566
878
  if (isClassCallback(lowerer, info, dispatch.source.method)) {
567
879
  const prototypeMethod = lowerer.prototypeMethodAccesses.has(dispatch.source.method);
568
880
  if (prototypeMethod)
569
881
  classPrototypeData(lowerer, info, loc);
570
- const bag = { kind: "call", callee: this.propertyBag.name, args: [receiver], type: DYN, loc };
571
- dispatch.callback.body.unshift({ kind: "if", cond: {
572
- kind: "libCall", fn: "dyn.typedRefIs", args: [receiver, { kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc,
573
- }, then: [{ kind: "return", value: { kind: "ternary", cond: prototypeMethod ? { kind: "boolLit", value: true, type: BOOL, loc } : { kind: "libCall", fn: "dyn.hasKey", args: [bag,
882
+ const bag = { kind: "call", callee: this.propertyBag.name, args: [value], type: DYN, loc };
883
+ dispatch.callback.body.unshift({ kind: "if", cond: condition, then: [{ kind: "return", value: { kind: "ternary", cond: prototypeMethod ? { kind: "boolLit", value: true, type: BOOL, loc } : { kind: "libCall", fn: "dyn.hasKey", args: [bag,
574
884
  { kind: "strLit", value: dispatch.source.method, type: STRING, loc }], type: BOOL, loc }, then: { kind: "dynObjLit", fields: [
575
885
  { key: { kind: "strLit", value: "value", type: STRING, loc }, value: { kind: "dynKeyGet", value: bag, key: { kind: "strLit", value: dispatch.source.method, type: STRING, loc }, type: DYN, loc } },
576
886
  ], type: DYN, loc }, else_: dynUndefinedExpr(loc), type: DYN, loc }, loc }], else_: null, loc });
@@ -578,7 +888,7 @@ export class ClassDynamicDispatch {
578
888
  const before = lowerer.diags.length;
579
889
  let branch;
580
890
  try {
581
- branch = this.methodBody(lowerer, dispatch, info, { kind: "dynCheck", value: receiver, type, loc });
891
+ branch = this.methodBody(lowerer, dispatch, info, receiver);
582
892
  }
583
893
  catch (error) {
584
894
  if (!(error instanceof PoisonError) || !info.decl)
@@ -589,9 +899,7 @@ export class ClassDynamicDispatch {
589
899
  branch = [fence];
590
900
  }
591
901
  dispatch.fn.body.unshift({
592
- kind: "if", cond: {
593
- kind: "libCall", fn: "dyn.typedRefIs", args: [receiver, { kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc,
594
- }, then: branch, else_: null, loc,
902
+ kind: "if", cond: condition, then: branch, else_: null, loc,
595
903
  });
596
904
  changed = true;
597
905
  }
@@ -600,17 +908,26 @@ export class ClassDynamicDispatch {
600
908
  if (!dispatch.write)
601
909
  for (const name of this.boxedConstructors) {
602
910
  const info = lowerer.classes.get(name);
603
- const field = info && findStaticOn(lowerer, info, dispatch.name)?.field;
911
+ const found = info && findStaticOn(lowerer, info, dispatch.name);
912
+ const field = found?.field;
604
913
  const key = `constructor:${name}`;
605
- if (!field || dispatch.name.startsWith("#") || dispatch.classes.has(key))
914
+ if (!info || dispatch.name.startsWith("#") || dispatch.classes.has(key) || !found && dispatch.name !== "prototype")
915
+ continue;
916
+ const method = found?.method && reflectedClassStaticMethodValue(lowerer, found.declarer, dispatch.name);
917
+ if (found && !field && !method || findGenericStaticOn(lowerer, info, dispatch.name) && !method)
606
918
  continue;
607
- dispatch.classes.add(key);
608
919
  const loc = dispatch.fn.loc;
920
+ const result = field ? varRef(field.globalId, field.type, loc) : method ??
921
+ (dispatch.name === "prototype" ? classPrototypeData(lowerer, info, loc) : null) ??
922
+ dynUndefinedExpr(loc);
923
+ if (dispatch.name === "name" || dispatch.name === "length" || dispatch.name === "apply")
924
+ continue;
925
+ dispatch.classes.add(key);
609
926
  const value = varRef("p.0", DYN, loc);
610
927
  const type = { kind: "classval", className: name };
611
928
  dispatch.fn.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.classIs", args: [value,
612
929
  { kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc }, then: [
613
- { kind: "return", value: lowerer.coerceToExpected(varRef(field.globalId, field.type, loc), DYN), loc },
930
+ { kind: "return", value: lowerer.coerceToExpected(result, DYN), loc },
614
931
  ], else_: null, loc });
615
932
  changed = true;
616
933
  }
@@ -650,7 +967,23 @@ export class ClassDynamicDispatch {
650
967
  const { info } = plan;
651
968
  dispatch.classes.add(plan.key);
652
969
  const loc = dispatch.fn.loc;
653
- const { receiver, condition } = this.propertyReceiver(plan, loc);
970
+ const selected = this.propertyReceiver(plan, loc);
971
+ const receiverType = { kind: "object", className: info.def.name };
972
+ const receiver = varRef("p.receiver", receiverType, loc);
973
+ const helperKey = JSON.stringify([dispatch.fn.name, info.def.name]);
974
+ const existingHelper = this.computedClassHelpers.get(helperKey);
975
+ const callHelper = (helper) => {
976
+ const call = { kind: "call", callee: helper.name,
977
+ args: helper.params.map((param) => param.localId === "p.receiver" ? selected.receiver : varRef(param.localId, param.type, loc)), type: helper.returnType, loc };
978
+ return { kind: "if", cond: selected.condition, then: dispatch.write && !dispatch.reflect
979
+ ? [{ kind: "exprStmt", expr: call, loc }, { kind: "return", value: null, loc }]
980
+ : [{ kind: "return", value: call, loc }], else_: null, loc };
981
+ };
982
+ if (existingHelper && !dispatch.dynamicKey) {
983
+ dispatch.fn.body.splice(dispatch.branchIndex, 0, callHelper(existingHelper));
984
+ changed = true;
985
+ continue;
986
+ }
654
987
  const memberBody = (name) => {
655
988
  if (dispatch.probe && (name === "message" || name === "name") && lowerer.isSubclassOf(info.def.name, "%Error") && dispatch.probe !== "in")
656
989
  return [{
@@ -661,9 +994,16 @@ export class ClassDynamicDispatch {
661
994
  if (info.builtinStream !== undefined && streamProperty(name) && dispatch.probe)
662
995
  return [{ kind: "return", value: { kind: "boolLit", value: dispatch.probe === "in", type: BOOL, loc }, loc }];
663
996
  if (!dispatch.probe)
664
- return this.propertyBody(lowerer, { ...dispatch, name }, info, receiver);
997
+ return dispatch.reflect ? this.reflectPropertyBody(lowerer, { ...dispatch, name }, info, receiver) : this.propertyBody(lowerer, { ...dispatch, name }, info, receiver);
665
998
  const prototypeMethod = lowerer.prototypeMethodAccesses.has(name);
666
999
  if (info.fields.has(name) || dispatch.probe === "in" && !prototypeMethod) {
1000
+ if (info.fields.has(name) && info.def.tracksOwnFields) {
1001
+ const bag = { kind: "call", callee: this.propertyBag.name, args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
1002
+ const symbol = symbolMemberKey(lowerer, info, name, loc);
1003
+ const fn = dispatch.probe === "in" ? "dyn.hasKey" : dispatch.probe === "own" ? "dyn.hasOwn" : "dyn.propertyIsEnumerable";
1004
+ return [{ kind: "return", value: { kind: "libCall", fn: symbol ? `${fn}Computed` : fn,
1005
+ args: [bag, symbol ?? { kind: "strLit", value: name, type: STRING, loc }], type: BOOL, loc }, loc }];
1006
+ }
667
1007
  return [{ kind: "return", value: { kind: "boolLit", value: true, type: BOOL, loc }, loc }];
668
1008
  }
669
1009
  if (prototypeMethod)
@@ -701,7 +1041,7 @@ export class ClassDynamicDispatch {
701
1041
  }
702
1042
  }
703
1043
  const branches = [];
704
- if (!dispatch.write) {
1044
+ if (!dispatch.write && !dispatch.reflect && !info.def.tracksOwnFields) {
705
1045
  const value = varRef("p.0", DYN, loc);
706
1046
  const key = varRef("p.key", DYN, loc);
707
1047
  const bag = { kind: "call", callee: this.propertyBag.name, args: [value], type: DYN, loc };
@@ -728,12 +1068,17 @@ export class ClassDynamicDispatch {
728
1068
  branches.push({ kind: "if", cond: { kind: "dynScalarEq", left: varRef("p.key", DYN, loc), right: symbol, type: BOOL, loc }, then, else_: null, loc });
729
1069
  }
730
1070
  if (branches.length) {
731
- dispatch.fn.body.unshift({ kind: "if", cond: condition, then: [
732
- { kind: "if", cond: { kind: "dynTest", test: "symbol", value: varRef("p.key", DYN, loc), type: BOOL, loc }, then: branches, else_: null, loc },
1071
+ dispatch.fn.body.unshift({ kind: "if", cond: selected.condition, then: [
1072
+ { kind: "if", cond: { kind: "dynTest", test: "symbol", value: varRef("p.key", DYN, loc), type: BOOL, loc }, then: transformStmtList(branches, { stmt: (statement) => statement, expr: (expression) => expression.kind === "varRef" && expression.localId === "p.receiver" ? selected.receiver : expression }), else_: null, loc },
733
1073
  ], else_: null, loc });
734
1074
  dispatch.branchIndex++;
735
1075
  }
736
1076
  }
1077
+ if (existingHelper) {
1078
+ dispatch.fn.body.splice(dispatch.branchIndex, 0, callHelper(existingHelper));
1079
+ changed = true;
1080
+ continue;
1081
+ }
737
1082
  const names = new Set([...info.fields.keys()].filter(isClassOwnEnumerableFieldName));
738
1083
  if (info.builtinStream)
739
1084
  for (const name of [...STREAM_BOOL_PROPERTIES, ...STREAM_NUM_PROPERTIES])
@@ -750,6 +1095,8 @@ export class ClassDynamicDispatch {
750
1095
  }
751
1096
  const branch = [];
752
1097
  for (const name of names) {
1098
+ if (dispatch.numericKey && String(Number(name)) !== name)
1099
+ continue;
753
1100
  const before = lowerer.diags.length;
754
1101
  let body;
755
1102
  try {
@@ -770,25 +1117,40 @@ export class ClassDynamicDispatch {
770
1117
  // Keep each class's key table in a separate function. A single
771
1118
  // function containing every boxed class grows quadratically during
772
1119
  // LLVM's control-flow optimization for large library workloads.
773
- const params = [...dispatch.fn.params];
1120
+ const params = [...dispatch.fn.params, { localId: "p.receiver", name: "receiver", type: receiverType }];
774
1121
  if (dispatch.keyLocal !== "p.key")
775
1122
  params.push({ localId: dispatch.keyLocal, name: "key", type: STRING });
776
1123
  const helper = {
777
1124
  name: `%dyn.class.keys.${lowerer.liftedFns.length}`, params, returnType: dispatch.fn.returnType,
778
1125
  locals: params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })),
779
- body: [...branch, ...dispatch.fn.body.slice(dispatch.write ? -2 : -1)], loc,
1126
+ body: [...branch, ...transformStmtList(dispatch.fn.body.slice(dispatch.write ? -2 : -1), { stmt: (statement) => statement,
1127
+ expr: (expression) => expression.kind === "dynKeyGet" && expression.key.type.kind === "string"
1128
+ ? { kind: "libCall", fn: "dyn.bagGet", args: [expression.value, expression.key, lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc }
1129
+ : expression.kind === "libCall" && expression.fn === "dyn.keySet"
1130
+ ? { ...expression, fn: "dyn.bagSet", args: [...expression.args, lowerer.coerceToExpected(receiver, DYN)] }
1131
+ : expression,
1132
+ })], loc,
780
1133
  };
781
1134
  this.generated.add(helper);
782
1135
  lowerer.liftedFns.push(helper);
783
- const call = { kind: "call", callee: helper.name,
784
- args: params.map((p) => varRef(p.localId, p.type, loc)), type: helper.returnType, loc };
785
- const then = dispatch.write
786
- ? [{ kind: "exprStmt", expr: call, loc }, { kind: "return", value: null, loc }]
787
- : [{ kind: "return", value: call, loc }];
788
- dispatch.fn.body.splice(dispatch.branchIndex, 0, { kind: "if", cond: condition, then, else_: null, loc });
1136
+ this.computedClassHelpers.set(helperKey, helper);
1137
+ dispatch.fn.body.splice(dispatch.branchIndex, 0, callHelper(helper));
789
1138
  changed = true;
790
1139
  }
791
1140
  }
1141
+ if (this.enumeration)
1142
+ for (const plan of this.propertyReceivers(lowerer, false, false)) {
1143
+ if (!plan.info.def.tracksOwnFields || this.enumerationClasses.has(plan.key))
1144
+ continue;
1145
+ this.enumerationClasses.add(plan.key);
1146
+ const loc = this.enumeration.loc;
1147
+ const { condition } = this.propertyReceiver(plan, loc);
1148
+ const bag = { kind: "call", callee: this.propertyBag.name, args: [varRef("p.0", DYN, loc)], type: DYN, loc };
1149
+ this.enumeration.body.unshift({ kind: "if", cond: condition, then: [
1150
+ { kind: "return", value: { kind: "libCall", fn: "dyn.forInKeys", args: [bag], type: DYN, loc }, loc },
1151
+ ], else_: null, loc });
1152
+ changed = true;
1153
+ }
792
1154
  if (this.constructDispatch)
793
1155
  for (const [name, info] of this.constructors) {
794
1156
  if (this.constructed.has(name))
@@ -822,6 +1184,26 @@ export class ClassDynamicDispatch {
822
1184
  ], else_: null, loc });
823
1185
  changed = true;
824
1186
  }
1187
+ if (this.staticPropertyBag)
1188
+ for (const name of this.boxedConstructors) {
1189
+ if (this.staticBagClasses.has(name))
1190
+ continue;
1191
+ const info = lowerer.classes.get(name);
1192
+ if (!info?.decl || !isJsSourceFile(info.decl.getSourceFile()))
1193
+ continue;
1194
+ const loc = this.staticPropertyBag.loc;
1195
+ const bag = classStaticData(lowerer, info, loc);
1196
+ if (!bag)
1197
+ continue;
1198
+ this.staticBagClasses.add(name);
1199
+ const value = varRef("value", DYN, loc);
1200
+ this.staticPropertyBag.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.classIs", args: [value,
1201
+ { kind: "strLit", value: typeKey({ kind: "classval", className: name }), type: STRING, loc }], type: BOOL, loc }, then: [{ kind: "return", value: bag, loc }], else_: null, loc });
1202
+ changed = true;
1203
+ }
1204
+ changed = this.normalizeDispatches(lowerer) || changed;
1205
+ for (const fn of this.generated)
1206
+ fn.speculativeDispatch = true;
825
1207
  return changed;
826
1208
  }
827
1209
  /** A known native class can access its shared bag directly. Keep the
@@ -856,6 +1238,79 @@ export class ClassDynamicDispatch {
856
1238
  lowerer.liftedFns.push(helper);
857
1239
  return helper;
858
1240
  }
1241
+ normalizedDispatches() {
1242
+ return [...(this.propertyBag ? [this.propertyBag] : []),
1243
+ ...[...this.properties.values()].map((dispatch) => dispatch.fn),
1244
+ ...[...this.computed.values()].map((dispatch) => dispatch.fn),
1245
+ ...[...this.dispatches.values()].flatMap((dispatch) => [dispatch.fn, dispatch.callback])];
1246
+ }
1247
+ constructorPropertyBag(lowerer, loc) {
1248
+ if (!this.staticPropertyBag) {
1249
+ this.staticPropertyBag = { name: "%dyn.class.staticProperties", params: [{ localId: "value", name: "value", type: DYN }], returnType: DYN,
1250
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }],
1251
+ body: [{ kind: "return", value: varRef("value", DYN, loc), loc }], loc };
1252
+ lowerer.liftedFns.push(this.staticPropertyBag);
1253
+ this.generated.add(this.staticPropertyBag);
1254
+ }
1255
+ return this.staticPropertyBag;
1256
+ }
1257
+ /** A base capsule and a derived capsule refer to the same native object.
1258
+ * Select its actual layout once, so each member table needs one branch
1259
+ * per class rather than one per possible base/derived capsule pair. */
1260
+ normalizeDispatches(lowerer) {
1261
+ const entries = [];
1262
+ for (const name of this.boxed) {
1263
+ const capsule = lowerer.classes.get(name);
1264
+ if (!capsule || capsule.builtinEmitter || capsule.builtinStream || capsule.builtinError)
1265
+ continue;
1266
+ const children = [];
1267
+ const visit = (info) => {
1268
+ for (const child of info.subclasses) {
1269
+ if (lowerer.classCanBeConstructed(child))
1270
+ children.push(child);
1271
+ visit(child);
1272
+ }
1273
+ };
1274
+ visit(capsule);
1275
+ if (children.length)
1276
+ entries.push({ capsule, children: children.reverse() });
1277
+ }
1278
+ if (!entries.length && !this.normalization)
1279
+ return false;
1280
+ const key = JSON.stringify(entries.map(({ capsule, children }) => [capsule.def.name, children.map((child) => child.def.name)]));
1281
+ let changed = key !== this.normalizationKey;
1282
+ if (!this.normalization) {
1283
+ const loc = this.propertyBag.loc;
1284
+ this.normalization = { name: "%dyn.class.normalize", params: [{ localId: "value", name: "value", type: DYN }], returnType: DYN,
1285
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }], body: [], loc };
1286
+ lowerer.liftedFns.push(this.normalization);
1287
+ this.generated.add(this.normalization);
1288
+ changed = true;
1289
+ }
1290
+ if (changed) {
1291
+ this.normalizationKey = key;
1292
+ const loc = this.normalization.loc;
1293
+ const value = varRef("value", DYN, loc);
1294
+ this.normalization.body = entries.map(({ capsule, children }) => {
1295
+ const type = { kind: "object", className: capsule.def.name };
1296
+ const receiver = { kind: "dynCheck", value, type, loc };
1297
+ return { kind: "if", cond: { kind: "libCall", fn: "dyn.typedRefIs", args: [value,
1298
+ { kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc }, then: children.map((child) => ({
1299
+ kind: "if", cond: { kind: "instanceOf", value: receiver, className: child.def.name, type: BOOL, loc }, then: [{
1300
+ kind: "return", value: { kind: "dynFrom", value: { kind: "downcast", value: receiver,
1301
+ type: { kind: "object", className: child.def.name }, loc }, type: DYN, loc }, loc,
1302
+ }], else_: null, loc,
1303
+ })), else_: null, loc };
1304
+ });
1305
+ this.normalization.body.push({ kind: "return", value, loc });
1306
+ }
1307
+ for (const fn of this.normalizedDispatches()) {
1308
+ fn.locals.find((local) => local.id === "p.0").mutable = true;
1309
+ fn.body.unshift({ kind: "assign", localId: "p.0", value: { kind: "call", callee: this.normalization.name,
1310
+ args: [varRef("p.0", DYN, fn.loc)], type: DYN, loc: fn.loc }, loc: fn.loc });
1311
+ }
1312
+ return changed;
1313
+ }
859
1314
  /** The hidden bag is part of the native object layout, so every capsule
860
1315
  * shares it and normal class tracing/disposal owns its values. Insert it
861
1316
  * at the same prefix offset throughout a hierarchy, including classes
@@ -878,14 +1333,19 @@ export class ClassDynamicDispatch {
878
1333
  }
879
1334
  /** A capsule retains its static type, while its object may be a subclass.
880
1335
  * Preorder insertion lets later branches test the most derived layout first. */
881
- propertyReceivers(lowerer, streams = false) {
1336
+ propertyReceivers(lowerer, streams = false, normalize = true) {
882
1337
  const plans = [];
1338
+ const seen = new Set();
883
1339
  for (const name of this.boxed) {
884
1340
  const capsule = lowerer.classes.get(name);
885
- if (!capsule || capsule.builtinEmitter || capsule.builtinStream && !streams || capsule.builtinError)
1341
+ if (!capsule || (capsule.builtinEmitter || capsule.builtinStream) && !streams || capsule.builtinError)
886
1342
  continue;
1343
+ const canonical = normalize && !capsule.builtinStream && !capsule.builtinEmitter;
887
1344
  const visit = (info) => {
888
- plans.push({ info, capsule, key: JSON.stringify([name, info.def.name]) });
1345
+ if ((!canonical || !seen.has(info.def.name)) && (info === capsule || lowerer.classCanBeConstructed(info))) {
1346
+ seen.add(info.def.name);
1347
+ plans.push({ info, capsule: canonical ? info : capsule, key: canonical ? info.def.name : JSON.stringify([name, info.def.name]) });
1348
+ }
889
1349
  for (const child of info.subclasses)
890
1350
  visit(child);
891
1351
  };
@@ -907,6 +1367,46 @@ export class ClassDynamicDispatch {
907
1367
  className: plan.info.def.name, type: BOOL, loc }, else_: { kind: "boolLit", value: false, type: BOOL, loc }, type: BOOL, loc },
908
1368
  };
909
1369
  }
1370
+ reflectPropertyBody(lowerer, dispatch, info, receiver) {
1371
+ const { name, write } = dispatch;
1372
+ const loc = dispatch.fn.loc;
1373
+ const actualReceiver = varRef("p.reflectReceiver", DYN, loc);
1374
+ const instance = lowerer.coerceToExpected(receiver, DYN);
1375
+ const key = { kind: "strLit", value: name, type: STRING, loc };
1376
+ const field = info.fields.get(name);
1377
+ const member = `${write ? "set" : "get"}:${name}`;
1378
+ const accessor = field ? null : findMethodOn(lowerer, info, member);
1379
+ const bag = { kind: "call", callee: this.propertyBag.name, args: [instance], type: DYN, loc };
1380
+ const fallback = { kind: "libCall", fn: write ? "dyn.reflectSet" : "dyn.reflectGet",
1381
+ args: [bag, lowerer.coerceToExpected(key, DYN), ...(write ? [varRef("p.1", DYN, loc)] : []), actualReceiver], type: write ? BOOL : DYN, loc };
1382
+ if (accessor && info.decl) {
1383
+ const method = classMethodValue(lowerer, info.decl, info, member, loc);
1384
+ if (method) {
1385
+ const call = { kind: "libCall", fn: "dyn.reflectApply", args: [lowerer.coerceToExpected(method, DYN), actualReceiver,
1386
+ { kind: "dynArrLit", elems: write ? [varRef("p.1", DYN, loc)] : [], type: DYN, loc }], type: DYN, loc };
1387
+ return [
1388
+ { kind: "if", cond: { kind: "libCall", fn: "dyn.hasOwn", args: [bag, key], type: BOOL, loc }, then: [{ kind: "return", value: fallback, loc }], else_: null, loc },
1389
+ ...(write ? [{ kind: "exprStmt", expr: call, loc }, { kind: "return", value: { kind: "boolLit", value: true, type: BOOL, loc }, loc }] : [{ kind: "return", value: call, loc }]),
1390
+ ];
1391
+ }
1392
+ }
1393
+ if (write && !field) {
1394
+ if (findMethodOn(lowerer, info, `get:${name}`) && !accessor)
1395
+ return [{ kind: "return", value: { kind: "boolLit", value: false, type: BOOL, loc }, loc }];
1396
+ return [{ kind: "return", value: fallback, loc }];
1397
+ }
1398
+ const body = this.propertyBody(lowerer, dispatch, info, receiver);
1399
+ if (!write)
1400
+ return transformStmtList(body, { stmt: (statement) => statement, expr: (expression) => expression.kind === "libCall" && expression.fn === "dyn.bagGet"
1401
+ ? { ...expression, fn: "dyn.reflectGet", args: [expression.args[0], lowerer.coerceToExpected(expression.args[1], DYN), actualReceiver] } : expression });
1402
+ return [
1403
+ { kind: "if", cond: { kind: "dynScalarEq", left: instance, right: actualReceiver, negated: true, type: BOOL, loc }, then: [{ kind: "return", value: {
1404
+ kind: "libCall", fn: "dyn.reflectDefine", args: [actualReceiver, lowerer.coerceToExpected(key, DYN), varRef("p.1", DYN, loc)], type: BOOL, loc,
1405
+ }, loc }], else_: null, loc },
1406
+ ...transformStmtList(body, { stmt: (statement) => statement.kind === "return" && !statement.value
1407
+ ? { ...statement, value: { kind: "boolLit", value: true, type: BOOL, loc } } : statement, expr: (expression) => expression }),
1408
+ ];
1409
+ }
910
1410
  propertyBody(lowerer, dispatch, info, receiver) {
911
1411
  const { name, write } = dispatch;
912
1412
  const loc = dispatch.fn.loc;
@@ -932,7 +1432,7 @@ export class ClassDynamicDispatch {
932
1432
  const bag = { kind: "call", callee: this.propertyBag.name, args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
933
1433
  const key = { kind: "strLit", value: name, type: STRING, loc };
934
1434
  return [{ kind: "return", value: { kind: "ternary", cond: { kind: "libCall", fn: "dyn.hasKey", args: [bag, key], type: BOOL, loc },
935
- then: { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, else_: cls, type: DYN, loc }, loc }];
1435
+ then: { kind: "libCall", fn: "dyn.bagGet", args: [bag, key, lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc }, else_: cls, type: DYN, loc }, loc }];
936
1436
  }
937
1437
  if (name === "toString" && findMethodOn(lowerer, info, name)?.declarer.builtinError) {
938
1438
  return [{ kind: "return", value: errorToStringMethod(lowerer, receiver), loc }];
@@ -958,19 +1458,20 @@ export class ClassDynamicDispatch {
958
1458
  // A capsule may have the base ABI even when its native object is
959
1459
  // a subclass (for example an inherited iterator returning this).
960
1460
  // Select the method declaration at extraction, before detaching it.
961
- const overrides = [...lowerer.classes.values()].filter((candidate) => candidate !== info && lowerer.isSubclassOf(candidate.def.name, info.def.name) &&
1461
+ const overrides = [...lowerer.classes.values()].filter((candidate) => candidate !== info && lowerer.classCanBeConstructed(candidate) && lowerer.isSubclassOf(candidate.def.name, info.def.name) &&
962
1462
  (candidate.methods.has(name) || candidate.genericMethods?.has(name)));
963
1463
  overrides.sort((a, b) => lowerer.isSubclassOf(a.def.name, b.def.name) ? -1 : lowerer.isSubclassOf(b.def.name, a.def.name) ? 1 : 0);
964
1464
  const body = [];
965
- if (isClassOwnEnumerableFieldName(name) && !name.startsWith("sym:")) {
1465
+ const symbol = symbolMemberKey(lowerer, info, name, loc);
1466
+ if (symbol || isClassOwnEnumerableFieldName(name) && !name.startsWith("sym:")) {
966
1467
  const prototypeMethod = lowerer.prototypeMethodAccesses.has(name);
967
1468
  if (prototypeMethod)
968
1469
  classPrototypeData(lowerer, info, loc, receiver);
969
1470
  const bag = { kind: "call", callee: this.propertyBag.name,
970
1471
  args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
971
- const key = { kind: "strLit", value: name, type: STRING, loc };
972
- body.push({ kind: "if", cond: { kind: "libCall", fn: "dyn.hasKey", args: [bag, key], type: BOOL, loc },
973
- then: [{ kind: "return", value: { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, loc }], else_: null, loc });
1472
+ const key = symbol ?? { kind: "strLit", value: name, type: STRING, loc };
1473
+ body.push({ kind: "if", cond: { kind: "libCall", fn: symbol ? "dyn.hasOwnComputed" : "dyn.hasKey", args: [bag, key], type: BOOL, loc },
1474
+ then: [{ kind: "return", value: { kind: "libCall", fn: symbol ? "dyn.reflectGet" : "dyn.bagGet", args: [bag, key, lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc }, loc }], else_: null, loc });
974
1475
  if (prototypeMethod) {
975
1476
  body.push({ kind: "return", value: dynUndefinedExpr(loc), loc });
976
1477
  return body;
@@ -987,8 +1488,12 @@ export class ClassDynamicDispatch {
987
1488
  return body;
988
1489
  }
989
1490
  }
990
- if (!field && !accessor)
991
- return [{ kind: "return", value: dynUndefinedExpr(loc), loc }];
1491
+ if (!field && !accessor) {
1492
+ const instance = lowerer.coerceToExpected(receiver, DYN);
1493
+ const bag = { kind: "call", callee: this.propertyBag.name, args: [instance], type: DYN, loc };
1494
+ return [{ kind: "return", value: { kind: "libCall", fn: "dyn.bagGet", args: [bag,
1495
+ { kind: "strLit", value: name, type: STRING, loc }, instance], type: DYN, loc }, loc }];
1496
+ }
992
1497
  const type = field ?? accessor.sig.ret;
993
1498
  if (!canConvertToDyn(type, getRecord, getUnion))
994
1499
  return fence();
@@ -1005,16 +1510,24 @@ export class ClassDynamicDispatch {
1005
1510
  args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
1006
1511
  const key = { kind: "strLit", value: name, type: STRING, loc };
1007
1512
  return [{ kind: "if", cond: { kind: "libCall", fn: "dyn.hasOwn", args: [bag, key], type: BOOL, loc },
1008
- then: [{ kind: "return", value: { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, loc }], else_: null, loc },
1513
+ then: [{ kind: "return", value: { kind: "libCall", fn: "dyn.bagGet", args: [bag, key, lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc }, loc }], else_: null, loc },
1009
1514
  { kind: "return", value: boxed, loc }];
1010
1515
  }
1516
+ if (field && info.def.tracksOwnFields) {
1517
+ const instance = lowerer.coerceToExpected(receiver, DYN);
1518
+ const bag = { kind: "call", callee: this.propertyBag.name, args: [instance], type: DYN, loc };
1519
+ const symbol = symbolMemberKey(lowerer, info, name, loc);
1520
+ const key = symbol ?? { kind: "strLit", value: name, type: STRING, loc };
1521
+ return [{ kind: "return", value: { kind: "ternary", cond: { kind: "libCall", fn: symbol ? "dyn.hasOwnComputed" : "dyn.hasOwn", args: [bag, key], type: BOOL, loc },
1522
+ then: boxed, else_: { kind: "libCall", fn: symbol ? "dyn.reflectGet" : "dyn.bagGet", args: [bag, key, instance], type: DYN, loc }, type: DYN, loc }, loc }];
1523
+ }
1011
1524
  return [{ kind: "return", value: boxed, loc }];
1012
1525
  }
1013
1526
  if (!field && !accessor && isClassOwnEnumerableFieldName(name) && !name.startsWith("sym:") && !findMethodOn(lowerer, info, `get:${name}`)) {
1014
1527
  const bag = { kind: "call", callee: this.propertyBag.name,
1015
1528
  args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
1016
- return [{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [bag,
1017
- { kind: "strLit", value: name, type: STRING, loc }, varRef("p.1", DYN, loc)], type: VOID, loc }, loc },
1529
+ return [{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.bagSet", args: [bag,
1530
+ { kind: "strLit", value: name, type: STRING, loc }, varRef("p.1", DYN, loc), lowerer.coerceToExpected(receiver, DYN)], type: VOID, loc }, loc },
1018
1531
  { kind: "return", value: null, loc }];
1019
1532
  }
1020
1533
  if (!field && !accessor)
@@ -1039,17 +1552,23 @@ export class ClassDynamicDispatch {
1039
1552
  methodBody(lowerer, dispatch, info, receiver) {
1040
1553
  const { method, loc } = dispatch.source;
1041
1554
  const fence = () => [{ kind: "runtimeFence", code: "SC2020", message: `calling '${method}' on this native class through an untyped value is not supported yet`, loc }];
1042
- if (info.builtinStream) {
1555
+ if (info.builtinStream || emitterRooted(lowerer, info) && EMITTER_METHODS.has(method) && !findMethodOn(lowerer, info, method)) {
1043
1556
  const incoming = dispatch.source.args.map((_, i) => varRef(`p.${i + 1}`, DYN, loc));
1044
1557
  const registering = ["on", "addListener", "once", "prependListener", "prependOnceListener"].includes(method);
1045
1558
  const removing = method === "off" || method === "removeListener";
1046
1559
  const ret = (value) => [{ kind: "return", value: lowerer.coerceToExpected(value, DYN), loc }];
1560
+ if (method === "emit") {
1561
+ if (!incoming.length)
1562
+ return fence();
1563
+ return ret({ kind: "libCall", fn: "emitter.emitFlex", args: [receiver,
1564
+ lowerer.coerceToExpected(incoming[0], STRING), ...incoming.slice(1)], type: BOOL, loc });
1565
+ }
1047
1566
  if (registering || removing) {
1048
1567
  if (incoming.length < 2)
1049
1568
  return fence();
1050
1569
  const event = lowerer.coerceToExpected(incoming[0], STRING);
1051
1570
  const cb = incoming[1];
1052
- return [{ kind: "exprStmt", expr: { kind: "libCall", fn: "emitter.checkListener", args: [cb], type: VOID, loc }, loc }, ...ret({ kind: "libCall", fn: registering ? "stream.onDyn" : "emitter.offDyn", args: [receiver, event, cb, ...(registering ? [
1571
+ return [{ kind: "exprStmt", expr: { kind: "libCall", fn: "emitter.checkListener", args: [cb], type: VOID, loc }, loc }, ...ret({ kind: "libCall", fn: registering ? info.builtinStream ? "stream.onDyn" : "emitter.onFlex" : "emitter.offDyn", args: [receiver, event, cb, ...(registering ? [
1053
1572
  { kind: "boolLit", value: method === "once" || method === "prependOnceListener", type: BOOL, loc },
1054
1573
  { kind: "boolLit", value: method.startsWith("prepend"), type: BOOL, loc },
1055
1574
  ] : [])], type: receiver.type, loc })];
@@ -1149,7 +1668,7 @@ export class ClassDynamicDispatch {
1149
1668
  body.unshift({ kind: "if", cond: { kind: "dynTest", test: "undefined", negated: true, value: descriptor, type: BOOL, loc }, then: [
1150
1669
  { kind: "return", value: { kind: "dynCall", callee: { kind: "dynKeyGet", value: descriptor,
1151
1670
  key: { kind: "strLit", value: "value", type: STRING, loc }, type: DYN, loc }, receiver: varRef("p.0", DYN, loc),
1152
- args: incoming, calleeName: dispatch.source.calleeName, type: DYN, loc }, loc },
1671
+ args: incoming, calleeName: dispatch.source.calleeName, calleeNameValue: varRef("p.calleeName", STRING, loc), type: DYN, loc }, loc },
1153
1672
  ], else_: null, loc });
1154
1673
  }
1155
1674
  return body;