@scriptc/compiler 0.3.3 → 0.3.5

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 (352) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +844 -36
  2. package/dist/backend/cycle-mutability.d.ts +5 -0
  3. package/dist/backend/cycle-mutability.js +62 -3
  4. package/dist/backend/cycle-mutability.js.map +1 -1
  5. package/dist/backend/executable-features.js +1 -0
  6. package/dist/backend/executable-features.js.map +1 -1
  7. package/dist/backend/library-pack.js +1 -0
  8. package/dist/backend/library-pack.js.map +1 -1
  9. package/dist/backend/llvm/array-search.js +1 -1
  10. package/dist/backend/llvm/array-search.js.map +1 -1
  11. package/dist/backend/llvm/call-lifetimes.d.ts +11 -1
  12. package/dist/backend/llvm/call-lifetimes.js +30 -3
  13. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  14. package/dist/backend/llvm/call-lifetimes.test.js +31 -1
  15. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  16. package/dist/backend/llvm/common.d.ts +5 -0
  17. package/dist/backend/llvm/common.js +7 -0
  18. package/dist/backend/llvm/common.js.map +1 -1
  19. package/dist/backend/llvm/constant-callbacks.d.ts +2 -21
  20. package/dist/backend/llvm/constant-callbacks.js +36 -219
  21. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  22. package/dist/backend/llvm/constant-callbacks.test.js +22 -0
  23. package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
  24. package/dist/backend/llvm/dense-array-access.d.ts +9 -0
  25. package/dist/backend/llvm/dense-array-access.js +29 -3
  26. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  27. package/dist/backend/llvm/dyn.d.ts +46 -1
  28. package/dist/backend/llvm/dyn.js +218 -33
  29. package/dist/backend/llvm/dyn.js.map +1 -1
  30. package/dist/backend/llvm/emitter.d.ts +87 -1
  31. package/dist/backend/llvm/emitter.js +568 -61
  32. package/dist/backend/llvm/emitter.js.map +1 -1
  33. package/dist/backend/llvm/expr-async.js +17 -7
  34. package/dist/backend/llvm/expr-async.js.map +1 -1
  35. package/dist/backend/llvm/expr-bytes.d.ts +9 -0
  36. package/dist/backend/llvm/expr-bytes.js +159 -64
  37. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  38. package/dist/backend/llvm/expr-callbacks.d.ts +11 -0
  39. package/dist/backend/llvm/expr-callbacks.js +53 -0
  40. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  41. package/dist/backend/llvm/expr-calls.d.ts +3 -1
  42. package/dist/backend/llvm/expr-calls.js +137 -3
  43. package/dist/backend/llvm/expr-calls.js.map +1 -1
  44. package/dist/backend/llvm/expr-containers.js +63 -29
  45. package/dist/backend/llvm/expr-containers.js.map +1 -1
  46. package/dist/backend/llvm/expr-primitives.js +9 -1
  47. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  48. package/dist/backend/llvm/expr-stream-bridges.js +4 -0
  49. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  50. package/dist/backend/llvm/integer-clones.d.ts +103 -0
  51. package/dist/backend/llvm/integer-clones.js +1336 -0
  52. package/dist/backend/llvm/integer-clones.js.map +1 -0
  53. package/dist/backend/llvm/integer-clones.test.d.ts +1 -0
  54. package/dist/backend/llvm/integer-clones.test.js +219 -0
  55. package/dist/backend/llvm/integer-clones.test.js.map +1 -0
  56. package/dist/backend/llvm/intern-tables.test.d.ts +1 -0
  57. package/dist/backend/llvm/intern-tables.test.js +77 -0
  58. package/dist/backend/llvm/intern-tables.test.js.map +1 -0
  59. package/dist/backend/llvm/lib-datagram.js +24 -0
  60. package/dist/backend/llvm/lib-datagram.js.map +1 -1
  61. package/dist/backend/llvm/lib-dispatch.js +96 -1
  62. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  63. package/dist/backend/llvm/lib-filesystem.js +51 -0
  64. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  65. package/dist/backend/llvm/lib-network.js +16 -0
  66. package/dist/backend/llvm/lib-network.js.map +1 -1
  67. package/dist/backend/llvm/lib-process.js +24 -0
  68. package/dist/backend/llvm/lib-process.js.map +1 -1
  69. package/dist/backend/llvm/lib-shared.d.ts +5 -0
  70. package/dist/backend/llvm/lib-shared.js +114 -3
  71. package/dist/backend/llvm/lib-shared.js.map +1 -1
  72. package/dist/backend/llvm/lib-stream.js +87 -0
  73. package/dist/backend/llvm/lib-stream.js.map +1 -1
  74. package/dist/backend/llvm/lib-test.js +9 -0
  75. package/dist/backend/llvm/lib-test.js.map +1 -1
  76. package/dist/backend/llvm/lib-tls.js +6 -0
  77. package/dist/backend/llvm/lib-tls.js.map +1 -1
  78. package/dist/backend/llvm/local-array-reads.js +1 -1
  79. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  80. package/dist/backend/llvm/map-entry-fusion.d.ts +28 -0
  81. package/dist/backend/llvm/map-entry-fusion.js +233 -0
  82. package/dist/backend/llvm/map-entry-fusion.js.map +1 -0
  83. package/dist/backend/llvm/map-entry-fusion.test.d.ts +1 -0
  84. package/dist/backend/llvm/map-entry-fusion.test.js +183 -0
  85. package/dist/backend/llvm/map-entry-fusion.test.js.map +1 -0
  86. package/dist/backend/llvm/map-number-lookup.d.ts +6 -0
  87. package/dist/backend/llvm/map-number-lookup.js +23 -1
  88. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  89. package/dist/backend/llvm/map-number-lookup.test.js +60 -5
  90. package/dist/backend/llvm/map-number-lookup.test.js.map +1 -1
  91. package/dist/backend/llvm/map-read-lifetimes.d.ts +11 -0
  92. package/dist/backend/llvm/map-read-lifetimes.js +92 -1
  93. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  94. package/dist/backend/llvm/map-read-lifetimes.test.js +131 -3
  95. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  96. package/dist/backend/llvm/publish.d.ts +21 -0
  97. package/dist/backend/llvm/publish.js +101 -11
  98. package/dist/backend/llvm/publish.js.map +1 -1
  99. package/dist/backend/llvm/publish.test.d.ts +1 -0
  100. package/dist/backend/llvm/publish.test.js +94 -0
  101. package/dist/backend/llvm/publish.test.js.map +1 -0
  102. package/dist/backend/llvm/record-results.js +76 -2
  103. package/dist/backend/llvm/record-results.js.map +1 -1
  104. package/dist/backend/llvm/record-sources.d.ts +7 -0
  105. package/dist/backend/llvm/record-sources.js +14 -0
  106. package/dist/backend/llvm/record-sources.js.map +1 -0
  107. package/dist/backend/llvm/reference-writes.js +5 -2
  108. package/dist/backend/llvm/reference-writes.js.map +1 -1
  109. package/dist/backend/llvm/regions.d.ts +4 -1
  110. package/dist/backend/llvm/regions.js +12 -3
  111. package/dist/backend/llvm/regions.js.map +1 -1
  112. package/dist/backend/llvm/shapes.d.ts +9 -3
  113. package/dist/backend/llvm/shapes.js +18 -14
  114. package/dist/backend/llvm/shapes.js.map +1 -1
  115. package/dist/backend/llvm/shapes.test.js +5 -3
  116. package/dist/backend/llvm/shapes.test.js.map +1 -1
  117. package/dist/backend/llvm/string-lifetimes.test.js +75 -1
  118. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  119. package/dist/backend/llvm/string-reads.js +2 -2
  120. package/dist/backend/llvm/string-reads.js.map +1 -1
  121. package/dist/backend/llvm/string-slices.js +2 -2
  122. package/dist/backend/llvm/string-slices.js.map +1 -1
  123. package/dist/backend/llvm/walkers.d.ts +17 -0
  124. package/dist/backend/llvm/walkers.js +123 -0
  125. package/dist/backend/llvm/walkers.js.map +1 -1
  126. package/dist/backend/may-throw.js +4 -0
  127. package/dist/backend/may-throw.js.map +1 -1
  128. package/dist/backend/native/contracts.d.ts +5 -1
  129. package/dist/backend/native/executable.js +16 -6
  130. package/dist/backend/native/executable.js.map +1 -1
  131. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  132. package/dist/backend/native/runtime-inputs.js +1 -0
  133. package/dist/backend/native/runtime-inputs.js.map +1 -1
  134. package/dist/backend/native-codegen-core.d.ts +4 -4
  135. package/dist/backend/native-codegen-core.js +4 -6
  136. package/dist/backend/native-codegen-core.js.map +1 -1
  137. package/dist/backend/native-codegen.test.js +7 -8
  138. package/dist/backend/native-codegen.test.js.map +1 -1
  139. package/dist/backend/native-link-info-core.d.ts +3 -0
  140. package/dist/backend/native-link-info-core.js.map +1 -1
  141. package/dist/backend/publication.js +3 -1
  142. package/dist/backend/publication.js.map +1 -1
  143. package/dist/backend/runtime-abi.d.ts +2 -2
  144. package/dist/backend/runtime-abi.js +2 -2
  145. package/dist/backend/runtime-pack-targets.test.js +2 -1
  146. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  147. package/dist/backend/runtime-pack.test.js +2 -1
  148. package/dist/backend/runtime-pack.test.js.map +1 -1
  149. package/dist/backend/target-diagnostics.js +7 -0
  150. package/dist/backend/target-diagnostics.js.map +1 -1
  151. package/dist/backend/warm-cache.js +1 -0
  152. package/dist/backend/warm-cache.js.map +1 -1
  153. package/dist/executable/executable-cache.d.ts +1 -0
  154. package/dist/executable/executable-cache.js +1 -0
  155. package/dist/executable/executable-cache.js.map +1 -1
  156. package/dist/executable/executable-cache.test.js +1 -0
  157. package/dist/executable/executable-cache.test.js.map +1 -1
  158. package/dist/frontend/experimental-import.d.ts +5 -1
  159. package/dist/frontend/experimental-import.js +28 -3
  160. package/dist/frontend/experimental-import.js.map +1 -1
  161. package/dist/frontend/lowering/assert-instances.d.ts +38 -0
  162. package/dist/frontend/lowering/assert-instances.js +269 -0
  163. package/dist/frontend/lowering/assert-instances.js.map +1 -0
  164. package/dist/frontend/lowering/builtin-calls.js +3 -0
  165. package/dist/frontend/lowering/builtin-calls.js.map +1 -1
  166. package/dist/frontend/lowering/builtins/callable-aliases.js +54 -8
  167. package/dist/frontend/lowering/builtins/callable-aliases.js.map +1 -1
  168. package/dist/frontend/lowering/builtins/child-process-async.d.ts +8 -6
  169. package/dist/frontend/lowering/builtins/child-process-async.js +45 -29
  170. package/dist/frontend/lowering/builtins/child-process-async.js.map +1 -1
  171. package/dist/frontend/lowering/builtins/compression.d.ts +18 -0
  172. package/dist/frontend/lowering/builtins/compression.js +175 -2
  173. package/dist/frontend/lowering/builtins/compression.js.map +1 -1
  174. package/dist/frontend/lowering/builtins/constants.js +7 -2
  175. package/dist/frontend/lowering/builtins/constants.js.map +1 -1
  176. package/dist/frontend/lowering/builtins/crypto.d.ts +6 -0
  177. package/dist/frontend/lowering/builtins/crypto.js +111 -1
  178. package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
  179. package/dist/frontend/lowering/builtins/errors.js +23 -0
  180. package/dist/frontend/lowering/builtins/errors.js.map +1 -1
  181. package/dist/frontend/lowering/builtins/filesystem-calls.d.ts +2 -0
  182. package/dist/frontend/lowering/builtins/filesystem-calls.js +5 -0
  183. package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -1
  184. package/dist/frontend/lowering/builtins/filesystem.js +60 -0
  185. package/dist/frontend/lowering/builtins/filesystem.js.map +1 -1
  186. package/dist/frontend/lowering/builtins/fs-streams.d.ts +26 -0
  187. package/dist/frontend/lowering/builtins/fs-streams.js +131 -0
  188. package/dist/frontend/lowering/builtins/fs-streams.js.map +1 -0
  189. package/dist/frontend/lowering/builtins/module-calls.js +5 -1
  190. package/dist/frontend/lowering/builtins/module-calls.js.map +1 -1
  191. package/dist/frontend/lowering/builtins/timers.d.ts +3 -0
  192. package/dist/frontend/lowering/builtins/timers.js +109 -23
  193. package/dist/frontend/lowering/builtins/timers.js.map +1 -1
  194. package/dist/frontend/lowering/call-signatures.js +6 -0
  195. package/dist/frontend/lowering/call-signatures.js.map +1 -1
  196. package/dist/frontend/lowering/class-dynamic-dispatch.js +216 -20
  197. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  198. package/dist/frontend/lowering/containers/bytes.js +33 -12
  199. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  200. package/dist/frontend/lowering/devirtualize.js +20 -7
  201. package/dist/frontend/lowering/devirtualize.js.map +1 -1
  202. package/dist/frontend/lowering/intern-tables.d.ts +13 -0
  203. package/dist/frontend/lowering/intern-tables.js +536 -0
  204. package/dist/frontend/lowering/intern-tables.js.map +1 -0
  205. package/dist/frontend/lowering/lower-assert.js +700 -61
  206. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  207. package/dist/frontend/lowering/lower-async-iteration.js +11 -2
  208. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -1
  209. package/dist/frontend/lowering/lower-calls.js +75 -6
  210. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  211. package/dist/frontend/lowering/lower-classes.d.ts +3 -0
  212. package/dist/frontend/lowering/lower-classes.js +43 -0
  213. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  214. package/dist/frontend/lowering/lower-dgram.js +132 -67
  215. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  216. package/dist/frontend/lowering/lower-event-emitter.d.ts +9 -1
  217. package/dist/frontend/lowering/lower-event-emitter.js +78 -0
  218. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  219. package/dist/frontend/lowering/lower-exprs.d.ts +4 -0
  220. package/dist/frontend/lowering/lower-exprs.js +144 -58
  221. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  222. package/dist/frontend/lowering/lower-inspect.d.ts +6 -0
  223. package/dist/frontend/lowering/lower-inspect.js +138 -39
  224. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  225. package/dist/frontend/lowering/lower-mock.d.ts +11 -0
  226. package/dist/frontend/lowering/lower-mock.js +504 -0
  227. package/dist/frontend/lowering/lower-mock.js.map +1 -0
  228. package/dist/frontend/lowering/lower-modules.js +7 -0
  229. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  230. package/dist/frontend/lowering/lower-server.d.ts +4 -0
  231. package/dist/frontend/lowering/lower-server.js +448 -30
  232. package/dist/frontend/lowering/lower-server.js.map +1 -1
  233. package/dist/frontend/lowering/lower-stmts.js +35 -8
  234. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  235. package/dist/frontend/lowering/lower-stream.d.ts +10 -6
  236. package/dist/frontend/lowering/lower-stream.js +794 -27
  237. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  238. package/dist/frontend/lowering/lower-test.js +86 -8
  239. package/dist/frontend/lowering/lower-test.js.map +1 -1
  240. package/dist/frontend/lowering/lowerer.d.ts +25 -1
  241. package/dist/frontend/lowering/lowerer.js +74 -9
  242. package/dist/frontend/lowering/lowerer.js.map +1 -1
  243. package/dist/frontend/lowering/pairs-snapshot.d.ts +6 -0
  244. package/dist/frontend/lowering/pairs-snapshot.js +49 -0
  245. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  246. package/dist/frontend/lowering/record-sources.d.ts +15 -0
  247. package/dist/frontend/lowering/record-sources.js +46 -0
  248. package/dist/frontend/lowering/record-sources.js.map +1 -0
  249. package/dist/frontend/lowering/surfaces.d.ts +4 -0
  250. package/dist/frontend/lowering/surfaces.js +94 -16
  251. package/dist/frontend/lowering/surfaces.js.map +1 -1
  252. package/dist/frontend/lowering/zlib-constants.d.ts +1 -0
  253. package/dist/frontend/lowering/zlib-constants.js +181 -0
  254. package/dist/frontend/lowering/zlib-constants.js.map +1 -0
  255. package/dist/frontend/mock-shapes.d.ts +10 -0
  256. package/dist/frontend/mock-shapes.js +76 -0
  257. package/dist/frontend/mock-shapes.js.map +1 -0
  258. package/dist/frontend/pipeline.js +5 -3
  259. package/dist/frontend/pipeline.js.map +1 -1
  260. package/dist/frontend/program.js +6 -1
  261. package/dist/frontend/program.js.map +1 -1
  262. package/dist/frontend/resolve.d.ts +4 -0
  263. package/dist/frontend/resolve.js +14 -0
  264. package/dist/frontend/resolve.js.map +1 -1
  265. package/dist/frontend/tsc-codes.js +1 -0
  266. package/dist/frontend/tsc-codes.js.map +1 -1
  267. package/dist/frontend/type-mapper.d.ts +8 -0
  268. package/dist/frontend/type-mapper.js +155 -9
  269. package/dist/frontend/type-mapper.js.map +1 -1
  270. package/dist/index.js +2 -1
  271. package/dist/index.js.map +1 -1
  272. package/dist/ir/analysis.d.ts +10 -0
  273. package/dist/ir/analysis.js +35 -0
  274. package/dist/ir/analysis.js.map +1 -1
  275. package/dist/ir/analysis.test.js +69 -1
  276. package/dist/ir/analysis.test.js.map +1 -1
  277. package/dist/ir/builtin-effects.js +72 -0
  278. package/dist/ir/builtin-effects.js.map +1 -1
  279. package/dist/ir/builtin-signatures.d.ts +1 -1
  280. package/dist/ir/builtin-signatures.js +238 -15
  281. package/dist/ir/builtin-signatures.js.map +1 -1
  282. package/dist/ir/capture-values.d.ts +107 -0
  283. package/dist/ir/capture-values.js +850 -0
  284. package/dist/ir/capture-values.js.map +1 -0
  285. package/dist/ir/capture-values.test.d.ts +1 -0
  286. package/dist/ir/capture-values.test.js +268 -0
  287. package/dist/ir/capture-values.test.js.map +1 -0
  288. package/dist/ir/field-values.d.ts +16 -0
  289. package/dist/ir/field-values.js +128 -0
  290. package/dist/ir/field-values.js.map +1 -0
  291. package/dist/ir/field-values.test.d.ts +1 -0
  292. package/dist/ir/field-values.test.js +160 -0
  293. package/dist/ir/field-values.test.js.map +1 -0
  294. package/dist/ir/final-fields.d.ts +4 -1
  295. package/dist/ir/final-fields.js +11 -3
  296. package/dist/ir/final-fields.js.map +1 -1
  297. package/dist/ir/function-references.test.js +1 -1
  298. package/dist/ir/function-references.test.js.map +1 -1
  299. package/dist/ir/init-only-globals.d.ts +16 -0
  300. package/dist/ir/init-only-globals.js +121 -0
  301. package/dist/ir/init-only-globals.js.map +1 -0
  302. package/dist/ir/init-only-globals.test.d.ts +1 -0
  303. package/dist/ir/init-only-globals.test.js +115 -0
  304. package/dist/ir/init-only-globals.test.js.map +1 -0
  305. package/dist/ir/initializer-bindings.d.ts +18 -1
  306. package/dist/ir/initializer-bindings.js +91 -1
  307. package/dist/ir/initializer-bindings.js.map +1 -1
  308. package/dist/ir/initializer-bindings.test.js +57 -2
  309. package/dist/ir/initializer-bindings.test.js.map +1 -1
  310. package/dist/ir/int32-slots.d.ts +22 -0
  311. package/dist/ir/int32-slots.js +376 -13
  312. package/dist/ir/int32-slots.js.map +1 -1
  313. package/dist/ir/int32-slots.test.js +91 -8
  314. package/dist/ir/int32-slots.test.js.map +1 -1
  315. package/dist/ir/integer-loop-facts.d.ts +19 -1
  316. package/dist/ir/integer-loop-facts.js +155 -7
  317. package/dist/ir/integer-loop-facts.js.map +1 -1
  318. package/dist/ir/integer-ranges.d.ts +8 -5
  319. package/dist/ir/integer-ranges.js +147 -55
  320. package/dist/ir/integer-ranges.js.map +1 -1
  321. package/dist/ir/integer-ranges.test.js +225 -7
  322. package/dist/ir/integer-ranges.test.js.map +1 -1
  323. package/dist/ir/ir.d.ts +29 -2
  324. package/dist/ir/ir.js +17 -0
  325. package/dist/ir/ir.js.map +1 -1
  326. package/dist/ir/property-bags.d.ts +14 -0
  327. package/dist/ir/property-bags.js +121 -0
  328. package/dist/ir/property-bags.js.map +1 -0
  329. package/dist/ir/receiver-readers.d.ts +21 -0
  330. package/dist/ir/receiver-readers.js +217 -0
  331. package/dist/ir/receiver-readers.js.map +1 -0
  332. package/dist/ir/runtime-features.d.ts +1 -0
  333. package/dist/ir/runtime-features.js +34 -2
  334. package/dist/ir/runtime-features.js.map +1 -1
  335. package/dist/ir/validate.js +221 -5
  336. package/dist/ir/validate.js.map +1 -1
  337. package/dist/ir/validate.test.js +21 -0
  338. package/dist/ir/validate.test.js.map +1 -1
  339. package/dist/library/int-infer.js +3 -0
  340. package/dist/library/int-infer.js.map +1 -1
  341. package/dist/library/ir-policy.js +1 -0
  342. package/dist/library/ir-policy.js.map +1 -1
  343. package/dist/native/cli.js +1 -0
  344. package/dist/native/cli.js.map +1 -1
  345. package/dist/native/compiler.js +1 -3
  346. package/dist/native/compiler.js.map +1 -1
  347. package/dist/native/prepare.js +1 -0
  348. package/dist/native/prepare.js.map +1 -1
  349. package/dist/native/sanitizer.test.js +1 -0
  350. package/dist/native/sanitizer.test.js.map +1 -1
  351. package/package.json +20 -20
  352. package/surface-manifest.json +896 -49
@@ -0,0 +1,850 @@
1
+ import { typeEquals, } from "./ir.js";
2
+ import { exprFunctionReference, moduleFunctionReferences } from "./function-references.js";
3
+ import { AmbientReceiverReaders } from "./receiver-readers.js";
4
+ import { reparseStmt } from "./serialize.js";
5
+ import { everyExprChild, everyStmtChild, everyStmtList, mapExprChildren, mapStmtChildren, } from "./traverse.js";
6
+ /** Captured bindings that hold one value for their whole observable life
7
+ * read through plain immutable locals.
8
+ *
9
+ * A captured binding lives in a shared box: the declaring function and
10
+ * every closure environment that captures it address the same cell, so a
11
+ * write anywhere is visible everywhere. When nothing ever writes the cell
12
+ * after its declaration fills it, the box is only a carrier: every read
13
+ * observes the declaration's value. Analyses and code generation exclude
14
+ * boxed locals, though, so a boxed `const N` turns all integer index math
15
+ * that reads it into checked f64 arithmetic and every reference read into
16
+ * an out-of-line retained box load.
17
+ *
18
+ * This pass keeps the boxes (environments still capture and trace them
19
+ * exactly as before, so ownership and cycle collection are unchanged) and
20
+ * moves the reads onto plain locals:
21
+ *
22
+ * - In the declaring function, `varDecl x = init` becomes
23
+ * `varDecl x' = init; varDecl x = x'`, and every read of `x` reads `x'`.
24
+ * An unchanged captured number parameter `p` gets `varDecl p' = p` at
25
+ * entry in the same way.
26
+ * - In a closure body, an entry `varDecl c' = c` copies the environment's
27
+ * value once and every read of `c` reads `c'`. When every creation site
28
+ * captures a declaration of the same closed numeric constant, `c'`
29
+ * starts from that literal instead, so range analysis sees the constant.
30
+ *
31
+ * Nested environments still capture the original box. A call through such
32
+ * a binding whose only value is an environment-free function (a nested
33
+ * function declaration or arrow that captures nothing) becomes a direct
34
+ * call of a capture-free copy of that body, as a call of a top-level
35
+ * function is: the closure value itself still exists for every other use.
36
+ *
37
+ * Soundness. A binding qualifies when its box is filled exactly once
38
+ * before any environment can observe it and nothing rebinds it in any
39
+ * function that shares the box:
40
+ *
41
+ * - the declaration is the binding's only `varDecl` and has an
42
+ * initializer that does not itself create an environment over it; a
43
+ * temporal-dead-zone binding (an environment exists before its
44
+ * initializing `assign`) qualifies only when no code can run any such
45
+ * environment, or read the binding, before that assignment (orderedTdz),
46
+ * and otherwise keeps every checked read,
47
+ * - no `assign`, `assignExpr`, `incDec`, `forOf`, catch or rethrow binding
48
+ * targets it, here or in any closure body reached through capture edges,
49
+ * - local classes keep their boxes (method bodies are not followed).
50
+ *
51
+ * A closure capture qualifies when every creation site supplies such a
52
+ * binding, an unchanged parameter, or a qualifying capture of the creating
53
+ * closure. A closure can only run after its creation, so its entry copy
54
+ * reads the final value. Every execution of a declaration (for example
55
+ * per loop iteration) fills its own fresh box, so environments created in
56
+ * different iterations keep seeing their own value. */
57
+ export function copyStableCaptures(mod) {
58
+ const flow = new CaptureFlow(mod.functions);
59
+ let changed = false;
60
+ const functions = mod.functions.map((fn) => {
61
+ const next = flow.rewrite(fn);
62
+ if (next !== fn)
63
+ changed = true;
64
+ return next;
65
+ });
66
+ if (!changed)
67
+ return mod;
68
+ const rewritten = new Map(functions.map((fn) => [fn.name, fn]));
69
+ for (const [name, clone] of flow.directClones()) {
70
+ const { captures: _captures, ...plain } = rewritten.get(name);
71
+ // Analyses key facts by node identity: the copy gets its own nodes.
72
+ functions.push({ ...plain, name: clone, body: plain.body.map(reparseStmt) });
73
+ }
74
+ // A closure body whose every use became a direct call is only reachable
75
+ // through its copy now.
76
+ const referenced = new Set(moduleFunctionReferences(mod));
77
+ for (const fn of functions)
78
+ everyStmtList(fn.body, {
79
+ stmt: () => true,
80
+ expr: (e) => {
81
+ const name = e.kind === "call" ? e.callee : exprFunctionReference(e);
82
+ if (name !== undefined)
83
+ referenced.add(name);
84
+ return true;
85
+ },
86
+ });
87
+ const clones = flow.directClones();
88
+ return {
89
+ ...mod,
90
+ functions: functions
91
+ .filter((fn) => !clones.has(fn.name) || referenced.has(fn.name))
92
+ .map((fn) => {
93
+ const clone = clones.get(fn.name);
94
+ return clone === undefined ? fn : forwardTo(fn, clone);
95
+ }),
96
+ };
97
+ }
98
+ /** A closure body that other uses still reach as a value, reduced to a call
99
+ * of its capture-free copy so the body exists once in the module. The copy
100
+ * takes the same parameters, and the body neither names its own closure nor
101
+ * reads the ambient receiver (CaptureFlow.direct), so the call is the whole
102
+ * behavior. */
103
+ function forwardTo(fn, clone) {
104
+ const call = {
105
+ kind: "call",
106
+ callee: clone,
107
+ args: fn.params.map((p) => ({ kind: "varRef", localId: p.localId, type: p.type, loc: fn.loc })),
108
+ type: fn.returnType,
109
+ loc: fn.loc,
110
+ };
111
+ const params = new Set(fn.params.map((p) => p.localId));
112
+ return {
113
+ ...fn,
114
+ // Parameters only flow into the call now: nothing captures them.
115
+ locals: fn.locals.filter((l) => params.has(l.id)).map(plainLocal),
116
+ body: fn.returnType.kind === "void"
117
+ ? [{ kind: "exprStmt", expr: call, loc: fn.loc }]
118
+ : [{ kind: "return", value: call, loc: fn.loc }],
119
+ };
120
+ }
121
+ /** A parameter local without the capture box or TDZ flag of its old body. */
122
+ function plainLocal(local) {
123
+ const plain = {
124
+ id: local.id,
125
+ name: local.name,
126
+ type: local.type,
127
+ mutable: local.mutable,
128
+ };
129
+ if (local.source !== undefined)
130
+ plain.source = local.source;
131
+ return plain;
132
+ }
133
+ /** The value of a closed numeric expression over literals (`1024`,
134
+ * `-1`, `4 * 256`), evaluated with the same IEEE double arithmetic. */
135
+ function constantNumber(e) {
136
+ if (e.type.kind !== "f64")
137
+ return null;
138
+ if (e.kind === "numLit")
139
+ return e.value;
140
+ if (e.kind === "unary" && e.op === "-") {
141
+ const operand = constantNumber(e.operand);
142
+ return operand === null ? null : -operand;
143
+ }
144
+ if (e.kind !== "bin")
145
+ return null;
146
+ const left = constantNumber(e.left);
147
+ const right = left === null ? null : constantNumber(e.right);
148
+ if (left === null || right === null)
149
+ return null;
150
+ switch (e.op) {
151
+ case "+":
152
+ return left + right;
153
+ case "-":
154
+ return left - right;
155
+ case "*":
156
+ return left * right;
157
+ case "/":
158
+ return left / right;
159
+ default:
160
+ return null;
161
+ }
162
+ }
163
+ /** Literals worth propagating: special values keep their declaration. */
164
+ function plainLiteral(value) {
165
+ return value !== null && Number.isFinite(value) && !Object.is(value, -0) ? value : null;
166
+ }
167
+ /** `body` without the definitions of the `dead` bindings: their empty
168
+ * declarations and the declarations or assignments binding a closure. */
169
+ function prune(body, dead) {
170
+ const list = (stmts) => stmts.flatMap((s) => {
171
+ switch (s.kind) {
172
+ case "varDecl":
173
+ return dead.has(s.localId) && (s.init === null || s.init.kind === "closure") ? [] : [s];
174
+ case "assign":
175
+ return dead.has(s.localId) && s.value.kind === "closure" ? [] : [s];
176
+ case "block":
177
+ return [{ ...s, body: list(s.body) }];
178
+ case "if":
179
+ return [{ ...s, then: list(s.then), else_: s.else_ === null ? null : list(s.else_) }];
180
+ case "while":
181
+ case "doWhile":
182
+ case "for":
183
+ case "forOf":
184
+ return [{ ...s, body: list(s.body) }];
185
+ case "tryCatch":
186
+ return [
187
+ {
188
+ ...s,
189
+ tryBody: list(s.tryBody),
190
+ catchBody: s.catchBody === null ? null : list(s.catchBody),
191
+ finallyBody: s.finallyBody === null ? null : list(s.finallyBody),
192
+ },
193
+ ];
194
+ default:
195
+ return [s];
196
+ }
197
+ });
198
+ return list(body);
199
+ }
200
+ /** Capture-flow facts over the whole module. Capture chains follow lexical
201
+ * nesting and cannot cycle; provisional memo entries only bound work on
202
+ * malformed input. */
203
+ export class CaptureFlow {
204
+ functions = new Map();
205
+ facts = new Map();
206
+ unchangedMemo = new Map();
207
+ stableMemo = new Map();
208
+ sites = new Map();
209
+ receivers;
210
+ directMemo = new Map();
211
+ /** Environment-free bodies called directly, and their copies' names. */
212
+ clones = new Map();
213
+ constructor(functions) {
214
+ for (const fn of functions)
215
+ this.functions.set(fn.name, fn);
216
+ this.receivers = new AmbientReceiverReaders(this.functions);
217
+ // Creation sites are collected while scanning each body.
218
+ for (const fn of functions)
219
+ this.factsOf(fn);
220
+ }
221
+ directClones() {
222
+ return this.clones;
223
+ }
224
+ /** A lifted body that captures nothing and can be entered like a declared
225
+ * function: synchronous, never naming its own closure, and unable to
226
+ * observe the ambient receiver a closure call would bracket. */
227
+ direct(fnName) {
228
+ const known = this.directMemo.get(fnName);
229
+ if (known !== undefined)
230
+ return known;
231
+ const fn = this.functions.get(fnName);
232
+ const result = fn !== undefined &&
233
+ fn.captures?.length === 0 &&
234
+ fn.classCaptures === undefined &&
235
+ fn.async !== true &&
236
+ fn.generator === undefined &&
237
+ everyStmtList(fn.body, { stmt: () => true, expr: (e) => e.kind !== "selfRef" }) &&
238
+ this.receivers.ignores(fn);
239
+ this.directMemo.set(fnName, result);
240
+ return result;
241
+ }
242
+ /** The environment-free function every execution binds to `id`, if any. */
243
+ target(fnName, id, depth = 0) {
244
+ const fn = this.functions.get(fnName);
245
+ if (fn === undefined || depth > 64)
246
+ return null;
247
+ const facts = this.factsOf(fn);
248
+ const own = facts.declarations.get(id);
249
+ if (own !== undefined) {
250
+ const local = facts.locals.get(id);
251
+ if (local?.boxed === true ? !this.stableOrigin(fn, id) : !this.plainConstant(fn, id))
252
+ return null;
253
+ const hoisted = facts.hoisted.get(id);
254
+ const value = own.initialized ? own.init : hoisted?.kind === "assign" ? hoisted.value : null;
255
+ return value?.kind === "closure" &&
256
+ value.captures.length === 0 &&
257
+ value.adapts !== true &&
258
+ this.direct(value.fnName)
259
+ ? value.fnName
260
+ : null;
261
+ }
262
+ const index = fn.captures?.findIndex((c) => c.localId === id) ?? -1;
263
+ if (index < 0 || !this.stableCapture(fnName, id))
264
+ return null;
265
+ let result = null;
266
+ for (const site of this.sites.get(fnName) ?? []) {
267
+ const source = site.captures[index];
268
+ const next = source === undefined ? null : this.target(site.owner, source, depth + 1);
269
+ if (next === null || (result !== null && result !== next))
270
+ return null;
271
+ result = next;
272
+ }
273
+ return result;
274
+ }
275
+ statementIds = new Map();
276
+ /** The bindings a statement reads (with counts) and captures, at any depth. */
277
+ idsIn(s) {
278
+ const known = this.statementIds.get(s);
279
+ if (known !== undefined)
280
+ return known;
281
+ const result = { reads: new Map(), captures: new Set() };
282
+ const expr = (e) => {
283
+ if (e.kind === "varRef")
284
+ result.reads.set(e.localId, (result.reads.get(e.localId) ?? 0) + 1);
285
+ else if (e.kind === "closure" || e.kind === "classRef")
286
+ for (const id of e.captures ?? [])
287
+ result.captures.add(id);
288
+ return everyExprChild(e, expr, stmt);
289
+ };
290
+ const stmt = (node) => everyStmtChild(node, expr, stmt);
291
+ stmt(s);
292
+ this.statementIds.set(s, result);
293
+ return result;
294
+ }
295
+ /** Reads of `id` in the statements of `body` after index `after`. */
296
+ readsAfter(body, after, id) {
297
+ let count = 0;
298
+ for (let k = after + 1; k < body.length; k++)
299
+ count += this.idsIn(body[k]).reads.get(id) ?? 0;
300
+ return count;
301
+ }
302
+ /** A temporal-dead-zone binding no read can reach before its
303
+ * initialization: every environment created before the initializing
304
+ * assignment is the direct value of an unshared local of this function
305
+ * whose every read follows that assignment, and so is every read of the
306
+ * binding itself. Its box is then filled before any code can observe it. */
307
+ orderedTdz(fn, id) {
308
+ const facts = this.factsOf(fn);
309
+ const init = facts.tdzInits.get(id);
310
+ if (init === undefined || (facts.writes.get(id) ?? 0) !== 1)
311
+ return false;
312
+ const { body, declared, initialized } = init;
313
+ if ((facts.reads.get(id) ?? 0) !== this.readsAfter(body, initialized, id))
314
+ return false;
315
+ for (let k = declared + 1; k < body.length; k++) {
316
+ const s = body[k];
317
+ if (!this.idsIn(s).captures.has(id))
318
+ continue;
319
+ if (k > initialized)
320
+ continue;
321
+ if (k === initialized)
322
+ return false;
323
+ let holder;
324
+ let value;
325
+ if (s.kind === "assign") {
326
+ holder = s.localId;
327
+ value = s.value;
328
+ }
329
+ else if (s.kind === "varDecl") {
330
+ holder = s.localId;
331
+ value = s.init;
332
+ }
333
+ else
334
+ return false;
335
+ if (value?.kind !== "closure")
336
+ return false;
337
+ const local = facts.locals.get(holder);
338
+ if (local === undefined || local.boxed === true || local.tdz === true)
339
+ return false;
340
+ const declaration = facts.declarations.get(holder);
341
+ const writes = facts.writes.get(holder) ?? 0;
342
+ if (declaration?.count !== 1 || writes !== (s.kind === "assign" ? 1 : 0))
343
+ return false;
344
+ if ((facts.reads.get(holder) ?? 0) !== this.readsAfter(body, initialized, holder))
345
+ return false;
346
+ }
347
+ // Every environment over the binding is created in this list.
348
+ let nodes = 0;
349
+ const count = (e) => {
350
+ if (e.kind === "closure" && e.captures.includes(id))
351
+ nodes++;
352
+ return true;
353
+ };
354
+ everyStmtList(body.slice(declared + 1), { stmt: () => true, expr: count });
355
+ const edges = facts.edges.filter((edge) => edge.source === id).length;
356
+ return nodes === edges && !facts.classCaptured.has(id);
357
+ }
358
+ /** An unboxed binding (no environment shares it) of `fn` itself that
359
+ * holds its single definition from its first read on. */
360
+ plainConstant(fn, id) {
361
+ const facts = this.factsOf(fn);
362
+ const local = facts.locals.get(id);
363
+ const declaration = facts.declarations.get(id);
364
+ if (local === undefined || local.boxed === true || local.tdz === true)
365
+ return false;
366
+ if (fn.params.some((p) => p.localId === id) || declaration?.count !== 1)
367
+ return false;
368
+ const writes = facts.writes.get(id) ?? 0;
369
+ return declaration.initialized ? writes === 0 : facts.hoisted.has(id) && writes === 1;
370
+ }
371
+ /** `callValue` through a binding bound to one environment-free function,
372
+ * as a direct call of its capture-free copy. */
373
+ directCall(fnName, e) {
374
+ if (e.receiver !== undefined || e.callee.kind !== "varRef")
375
+ return null;
376
+ const target = this.target(fnName, e.callee.localId);
377
+ const callee = target === null ? undefined : this.functions.get(target);
378
+ const ft = e.callee.type;
379
+ if (callee === undefined || ft.kind !== "func")
380
+ return null;
381
+ if (callee.params.length !== e.args.length ||
382
+ ft.params.length !== e.args.length ||
383
+ !typeEquals(callee.returnType, e.type) ||
384
+ !callee.params.every((p, i) => typeEquals(p.type, ft.params[i])))
385
+ return null;
386
+ let clone = this.clones.get(callee.name);
387
+ if (clone === undefined) {
388
+ clone = `${callee.name}%direct`;
389
+ this.clones.set(callee.name, clone);
390
+ }
391
+ return clone;
392
+ }
393
+ factsOf(fn) {
394
+ const known = this.facts.get(fn.name);
395
+ if (known !== undefined)
396
+ return known;
397
+ const facts = {
398
+ locals: new Map(fn.locals.map((local) => [local.id, local])),
399
+ declarations: new Map(),
400
+ writes: new Map(),
401
+ hoisted: new Map(),
402
+ callees: new Set(),
403
+ tdzInits: new Map(),
404
+ classCaptured: new Set(),
405
+ reads: new Map(),
406
+ edges: [],
407
+ };
408
+ this.facts.set(fn.name, facts);
409
+ const capturesIn = (e) => {
410
+ const ids = new Set();
411
+ const visitExpr = (node) => {
412
+ if (node.kind === "closure" || node.kind === "classRef")
413
+ for (const id of node.captures ?? [])
414
+ ids.add(id);
415
+ return everyExprChild(node, visitExpr, visitStmt);
416
+ };
417
+ const visitStmt = (node) => everyStmtChild(node, visitExpr, visitStmt);
418
+ visitExpr(e);
419
+ return ids;
420
+ };
421
+ const findHoisted = (body) => {
422
+ body.forEach((s, i) => {
423
+ if (s.kind !== "varDecl" || s.init !== null)
424
+ return;
425
+ for (let j = i + 1; j < body.length; j++) {
426
+ const next = body[j];
427
+ if (next.kind === "assign" && next.value.kind === "closure") {
428
+ if (next.localId === s.localId) {
429
+ facts.hoisted.set(s.localId, next);
430
+ return;
431
+ }
432
+ }
433
+ else if (next.kind !== "varDecl" || next.init !== null)
434
+ return;
435
+ }
436
+ });
437
+ };
438
+ const declare = (s, splittable) => {
439
+ const entry = facts.declarations.get(s.localId);
440
+ const initialized = s.init !== null && !capturesIn(s.init).has(s.localId);
441
+ const literal = s.init === null ? null : plainLiteral(constantNumber(s.init));
442
+ if (entry === undefined)
443
+ facts.declarations.set(s.localId, {
444
+ count: 1,
445
+ initialized,
446
+ splittable,
447
+ literal,
448
+ init: s.init,
449
+ });
450
+ else {
451
+ entry.count++;
452
+ entry.initialized = false;
453
+ entry.splittable = false;
454
+ entry.literal = null;
455
+ entry.init = null;
456
+ }
457
+ };
458
+ const write = (id) => {
459
+ facts.writes.set(id, (facts.writes.get(id) ?? 0) + 1);
460
+ };
461
+ const expr = (e) => {
462
+ switch (e.kind) {
463
+ case "varRef":
464
+ facts.reads.set(e.localId, (facts.reads.get(e.localId) ?? 0) + 1);
465
+ break;
466
+ case "assignExpr":
467
+ case "incDec":
468
+ write(e.localId);
469
+ break;
470
+ case "closure": {
471
+ let list = this.sites.get(e.fnName);
472
+ if (list === undefined)
473
+ this.sites.set(e.fnName, (list = []));
474
+ list.push({ owner: fn.name, captures: e.captures, adapts: e.adapts === true });
475
+ e.captures.forEach((source, index) => facts.edges.push({ target: e.fnName, index, source }));
476
+ break;
477
+ }
478
+ case "classRef":
479
+ for (const id of e.captures ?? [])
480
+ facts.classCaptured.add(id);
481
+ break;
482
+ case "callValue":
483
+ if (e.receiver === undefined && e.callee.kind === "varRef")
484
+ facts.callees.add(e.callee.localId);
485
+ break;
486
+ }
487
+ return everyExprChild(e, expr, (s) => stmt(s, false));
488
+ };
489
+ // `split` mirrors rewrite(): only statement lists reached through
490
+ // ordinary nesting from the body are expanded. Switch cases share one
491
+ // scope across dispatch targets, `for` heads are freshened per
492
+ // iteration, and statement lists inside expressions release their
493
+ // locals with the enclosing frame; declarations there stay as they are.
494
+ const findTdzInits = (body) => {
495
+ body.forEach((s, j) => {
496
+ if (s.kind !== "assign" || s.initializes !== true)
497
+ return;
498
+ const declared = body.findIndex((d, i) => i < j && d.kind === "varDecl" && d.localId === s.localId && d.init === null);
499
+ if (declared >= 0)
500
+ facts.tdzInits.set(s.localId, { body, declared, initialized: j });
501
+ });
502
+ };
503
+ const list = (body, split) => {
504
+ if (split) {
505
+ findHoisted(body);
506
+ findTdzInits(body);
507
+ }
508
+ return body.every((s) => stmt(s, split));
509
+ };
510
+ const stmt = (s, split) => {
511
+ switch (s.kind) {
512
+ case "varDecl":
513
+ declare(s, split);
514
+ return s.init === null || expr(s.init);
515
+ case "assign":
516
+ case "rethrow":
517
+ write(s.localId);
518
+ break;
519
+ case "forOf":
520
+ write(s.localId);
521
+ return expr(s.iterable) && list(s.body, split);
522
+ case "tryCatch":
523
+ if (s.catchLocalId !== null)
524
+ write(s.catchLocalId);
525
+ return (list(s.tryBody, split) &&
526
+ (s.catchBody === null || list(s.catchBody, split)) &&
527
+ (s.finallyBody === null || list(s.finallyBody, split)));
528
+ case "block":
529
+ return list(s.body, split);
530
+ case "if":
531
+ return expr(s.cond) && list(s.then, split) && (s.else_ === null || list(s.else_, split));
532
+ case "while":
533
+ return expr(s.cond) && list(s.body, split);
534
+ case "doWhile":
535
+ return list(s.body, split) && expr(s.cond);
536
+ case "for":
537
+ return ((s.init === null || stmt(s.init, false)) &&
538
+ (s.cond === null || expr(s.cond)) &&
539
+ (s.update === null || stmt(s.update, false)) &&
540
+ list(s.body, split));
541
+ }
542
+ return everyStmtChild(s, expr, (child) => stmt(child, false));
543
+ };
544
+ list(fn.body, true);
545
+ return facts;
546
+ }
547
+ local(fn, id) {
548
+ return this.factsOf(fn).locals.get(id);
549
+ }
550
+ /** Nothing rebinds `id` in `fnName` (declarations aside, and `ownWrites`
551
+ * writes of its own body) or in any body that shares its box through
552
+ * closure captures. */
553
+ unchanged(fnName, id, ownWrites = 0) {
554
+ const key = `${fnName}\0${id}\0${ownWrites}`;
555
+ const known = this.unchangedMemo.get(key);
556
+ if (known !== undefined)
557
+ return known;
558
+ this.unchangedMemo.set(key, false);
559
+ const fn = this.functions.get(fnName);
560
+ let result = false;
561
+ if (fn !== undefined) {
562
+ const facts = this.factsOf(fn);
563
+ result =
564
+ (facts.writes.get(id) ?? 0) === ownWrites &&
565
+ !facts.classCaptured.has(id) &&
566
+ facts.edges.every((edge) => {
567
+ if (edge.source !== id)
568
+ return true;
569
+ const capture = this.functions.get(edge.target)?.captures?.[edge.index];
570
+ return capture !== undefined && this.unchanged(edge.target, capture.localId);
571
+ });
572
+ }
573
+ this.unchangedMemo.set(key, result);
574
+ return result;
575
+ }
576
+ /** A boxed binding of `fn` itself (not a capture) whose box is filled
577
+ * once, before any environment observes it, and never written again. */
578
+ stableOrigin(fn, id) {
579
+ const local = this.local(fn, id);
580
+ if (local?.boxed !== true)
581
+ return false;
582
+ if (local.tdz === true) {
583
+ if (fn.captures?.some((c) => c.localId === id))
584
+ return false;
585
+ if (this.factsOf(fn).declarations.get(id)?.count !== 1)
586
+ return false;
587
+ return this.orderedTdz(fn, id) && this.unchanged(fn.name, id, 1);
588
+ }
589
+ if (fn.captures?.some((c) => c.localId === id))
590
+ return false;
591
+ if (fn.classCaptures?.some((c) => c.localId === id))
592
+ return false;
593
+ const facts = this.factsOf(fn);
594
+ const declaration = facts.declarations.get(id);
595
+ if (fn.params.some((p) => p.localId === id))
596
+ return declaration === undefined && this.unchanged(fn.name, id);
597
+ if (declaration?.count !== 1)
598
+ return false;
599
+ if (declaration.initialized)
600
+ return this.unchanged(fn.name, id);
601
+ return facts.hoisted.has(id) && this.unchanged(fn.name, id, 1);
602
+ }
603
+ /** A closure capture holding one value for the environment's life. */
604
+ stableCapture(fnName, id) {
605
+ const key = `${fnName}\0${id}`;
606
+ const known = this.stableMemo.get(key);
607
+ if (known !== undefined)
608
+ return known;
609
+ this.stableMemo.set(key, false);
610
+ const result = this.computeStableCapture(fnName, id);
611
+ this.stableMemo.set(key, result);
612
+ return result;
613
+ }
614
+ computeStableCapture(fnName, id) {
615
+ const fn = this.functions.get(fnName);
616
+ const index = fn?.captures?.findIndex((c) => c.localId === id) ?? -1;
617
+ if (fn === undefined || index < 0)
618
+ return false;
619
+ const sites = this.sites.get(fnName);
620
+ if (sites === undefined || sites.length === 0)
621
+ return false;
622
+ for (const site of sites) {
623
+ const owner = this.functions.get(site.owner);
624
+ const source = site.captures[index];
625
+ if (owner === undefined || source === undefined || site.adapts)
626
+ return false;
627
+ if (!this.stableOrigin(owner, source) && !this.stableCapture(site.owner, source))
628
+ return false;
629
+ }
630
+ return this.unchanged(fnName, id);
631
+ }
632
+ /** The numeric constant every execution binds to `id`, if any. */
633
+ literal(fnName, id, depth = 0) {
634
+ const fn = this.functions.get(fnName);
635
+ if (fn === undefined || depth > 64)
636
+ return null;
637
+ const own = this.factsOf(fn).declarations.get(id);
638
+ if (own !== undefined) {
639
+ if (!this.stableOrigin(fn, id))
640
+ return null;
641
+ const init = this.factsOf(fn).tdzInits.get(id);
642
+ const assign = init?.body[init.initialized];
643
+ return assign?.kind === "assign" ? plainLiteral(constantNumber(assign.value)) : own.literal;
644
+ }
645
+ const index = fn.captures?.findIndex((c) => c.localId === id) ?? -1;
646
+ if (index < 0 || !this.stableCapture(fnName, id))
647
+ return null;
648
+ let value = null;
649
+ for (const site of this.sites.get(fnName) ?? []) {
650
+ const source = site.captures[index];
651
+ const next = source === undefined ? null : this.literal(site.owner, source, depth + 1);
652
+ if (next === null || (value !== null && !Object.is(value, next)))
653
+ return null;
654
+ value = next;
655
+ }
656
+ return value;
657
+ }
658
+ rewrite(fn) {
659
+ const facts = this.factsOf(fn);
660
+ const reads = new Map();
661
+ const splits = new Map();
662
+ const entry = [];
663
+ const locals = [];
664
+ const used = new Set(fn.locals.map((l) => l.id));
665
+ const fresh = (local) => {
666
+ let k = 0;
667
+ while (used.has(`${local.name}.${k}`))
668
+ k++;
669
+ const id = `${local.name}.${k}`;
670
+ used.add(id);
671
+ const copy = { id, name: local.name, type: local.type, mutable: false };
672
+ if (local.source !== undefined)
673
+ copy.source = local.source;
674
+ locals.push(copy);
675
+ return copy;
676
+ };
677
+ const adapted = (this.sites.get(fn.name) ?? []).some((site) => site.adapts);
678
+ // Bindings nothing writes after their only initialization. Marking
679
+ // them immutable lets later analyses (callback resolution, borrowing)
680
+ // treat them as the constants they are.
681
+ const immutable = new Set();
682
+ for (const local of fn.locals) {
683
+ if (local.boxed !== true)
684
+ continue;
685
+ const capture = fn.captures?.some((c) => c.localId === local.id) === true;
686
+ const param = fn.params.some((p) => p.localId === local.id);
687
+ if (capture && local.mutable && this.stableCapture(fn.name, local.id))
688
+ immutable.add(local.id);
689
+ if (!facts.reads.has(local.id))
690
+ continue;
691
+ if (capture) {
692
+ if (adapted || !this.stableCapture(fn.name, local.id))
693
+ continue;
694
+ const copy = fresh(local);
695
+ const literal = local.type.kind === "f64" ? this.literal(fn.name, local.id) : null;
696
+ const init = literal !== null
697
+ ? { kind: "numLit", value: literal, type: local.type, loc: fn.loc }
698
+ : { kind: "varRef", localId: local.id, type: local.type, loc: fn.loc };
699
+ entry.push({ kind: "varDecl", localId: copy.id, init, loc: fn.loc });
700
+ reads.set(local.id, copy.id);
701
+ }
702
+ else if (param) {
703
+ // Unchanged captured parameters already keep their value in a plain
704
+ // slot (the backend boxes them lazily); a copy only exposes numbers
705
+ // to range analysis.
706
+ if (local.type.kind !== "f64" || !this.stableOrigin(fn, local.id))
707
+ continue;
708
+ const copy = fresh(local);
709
+ entry.push({
710
+ kind: "varDecl",
711
+ localId: copy.id,
712
+ init: { kind: "varRef", localId: local.id, type: local.type, loc: fn.loc },
713
+ loc: fn.loc,
714
+ });
715
+ reads.set(local.id, copy.id);
716
+ }
717
+ else {
718
+ if (!this.stableOrigin(fn, local.id))
719
+ continue;
720
+ const declaration = facts.declarations.get(local.id);
721
+ // Hoisted function and temporal-dead-zone bindings split at their
722
+ // assignment, which is only recorded in expandable statement lists.
723
+ if (declaration.initialized
724
+ ? !declaration.splittable
725
+ : !facts.hoisted.has(local.id) && !facts.tdzInits.has(local.id))
726
+ continue;
727
+ const copy = fresh(local);
728
+ splits.set(local.id, copy.id);
729
+ reads.set(local.id, copy.id);
730
+ if (declaration.initialized && local.mutable)
731
+ immutable.add(local.id);
732
+ }
733
+ }
734
+ const calls = [...facts.callees].some((id) => this.target(fn.name, id) !== null);
735
+ if (reads.size === 0 && immutable.size === 0 && !calls)
736
+ return fn;
737
+ const localsById = new Map(fn.locals.map((l) => [l.id, l]));
738
+ const expr = (e) => {
739
+ if (e.kind === "varRef") {
740
+ const to = reads.get(e.localId);
741
+ return to === undefined ? e : { ...e, localId: to };
742
+ }
743
+ if (e.kind === "callValue" && e.callee.kind === "varRef") {
744
+ const callee = this.directCall(fn.name, e);
745
+ if (callee !== null)
746
+ return { kind: "call", callee, args: e.args.map(expr), type: e.type, loc: e.loc };
747
+ }
748
+ return mapExprChildren(e, expr, single);
749
+ };
750
+ const single = (s) => mapStmtChildren(s, expr, single);
751
+ const list = (body) => body.flatMap(many);
752
+ const many = (s) => {
753
+ switch (s.kind) {
754
+ case "varDecl": {
755
+ const to = splits.get(s.localId);
756
+ if (to === undefined || s.init === null)
757
+ break;
758
+ const local = localsById.get(s.localId);
759
+ return [
760
+ { ...s, localId: to, init: expr(s.init) },
761
+ {
762
+ kind: "varDecl",
763
+ localId: s.localId,
764
+ init: { kind: "varRef", localId: to, type: local.type, loc: s.loc },
765
+ loc: s.loc,
766
+ },
767
+ ];
768
+ }
769
+ case "assign": {
770
+ const to = splits.get(s.localId);
771
+ const init = facts.tdzInits.get(s.localId);
772
+ const splitHere = facts.hoisted.get(s.localId) === s || init?.body[init.initialized] === s;
773
+ if (to === undefined || !splitHere)
774
+ break;
775
+ const local = localsById.get(s.localId);
776
+ return [
777
+ { kind: "varDecl", localId: to, init: expr(s.value), loc: s.loc },
778
+ { ...s, value: { kind: "varRef", localId: to, type: local.type, loc: s.loc } },
779
+ ];
780
+ }
781
+ case "block":
782
+ return [{ ...s, body: list(s.body) }];
783
+ case "if":
784
+ return [
785
+ {
786
+ ...s,
787
+ cond: expr(s.cond),
788
+ then: list(s.then),
789
+ else_: s.else_ === null ? null : list(s.else_),
790
+ },
791
+ ];
792
+ case "while":
793
+ return [{ ...s, cond: expr(s.cond), body: list(s.body) }];
794
+ case "doWhile":
795
+ return [{ ...s, body: list(s.body), cond: expr(s.cond) }];
796
+ case "for":
797
+ return [
798
+ {
799
+ ...s,
800
+ init: s.init === null ? null : single(s.init),
801
+ cond: s.cond === null ? null : expr(s.cond),
802
+ update: s.update === null ? null : single(s.update),
803
+ body: list(s.body),
804
+ },
805
+ ];
806
+ case "forOf":
807
+ return [{ ...s, iterable: expr(s.iterable), body: list(s.body) }];
808
+ case "tryCatch":
809
+ return [
810
+ {
811
+ ...s,
812
+ tryBody: list(s.tryBody),
813
+ catchBody: s.catchBody === null ? null : list(s.catchBody),
814
+ finallyBody: s.finallyBody === null ? null : list(s.finallyBody),
815
+ },
816
+ ];
817
+ }
818
+ return [single(s)];
819
+ };
820
+ const kept = fn.locals.map((local) => immutable.has(local.id) ? immutableLocal(local) : local);
821
+ if (reads.size === 0 && !calls)
822
+ return { ...fn, locals: kept };
823
+ const body = list(fn.body);
824
+ // Direct calls can leave an entry copy unread.
825
+ const readIds = new Set();
826
+ everyStmtList(body, {
827
+ stmt: () => true,
828
+ expr: (e) => {
829
+ if (e.kind === "varRef")
830
+ readIds.add(e.localId);
831
+ return true;
832
+ },
833
+ });
834
+ const live = entry.filter((s) => s.kind !== "varDecl" || readIds.has(s.localId));
835
+ // An unshared function binding whose every call became direct is dead:
836
+ // its definition only creates a closure, which then never exists.
837
+ const dead = new Set([...facts.callees].filter((id) => !readIds.has(id) &&
838
+ facts.locals.get(id)?.boxed !== true &&
839
+ this.target(fn.name, id) !== null));
840
+ const pruned = dead.size === 0 ? body : prune(body, dead);
841
+ const liveIds = new Set(live.map((s) => (s.kind === "varDecl" ? s.localId : "")));
842
+ const added = locals.filter((local) => liveIds.has(local.id) || !entry.some((s) => s.kind === "varDecl" && s.localId === local.id));
843
+ return { ...fn, locals: [...kept, ...added], body: [...live, ...pruned] };
844
+ }
845
+ }
846
+ /** The same local, read-only: nothing writes it after its declaration. */
847
+ function immutableLocal(local) {
848
+ return { ...local, mutable: false };
849
+ }
850
+ //# sourceMappingURL=capture-values.js.map