@scriptc/compiler 0.2.9 → 0.3.0

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 (505) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +2 -36
  2. package/ambient/scriptc-overrides.d.ts +1 -25
  3. package/ambient/scriptc.d.ts +1 -9
  4. package/dist/backend/cycle-analysis.d.ts +56 -0
  5. package/dist/backend/cycle-analysis.js +175 -14
  6. package/dist/backend/cycle-analysis.js.map +1 -1
  7. package/dist/backend/cycle-analysis.test.js +95 -2
  8. package/dist/backend/cycle-analysis.test.js.map +1 -1
  9. package/dist/backend/cycle-mutability.d.ts +3 -7
  10. package/dist/backend/cycle-mutability.js +4 -30
  11. package/dist/backend/cycle-mutability.js.map +1 -1
  12. package/dist/backend/executable-features.d.ts +1 -1
  13. package/dist/backend/executable-features.js +3 -8
  14. package/dist/backend/executable-features.js.map +1 -1
  15. package/dist/backend/immutability.d.ts +57 -0
  16. package/dist/backend/immutability.js +178 -0
  17. package/dist/backend/immutability.js.map +1 -0
  18. package/dist/backend/immutability.test.js +131 -0
  19. package/dist/backend/immutability.test.js.map +1 -0
  20. package/dist/backend/library-pack.js +0 -2
  21. package/dist/backend/library-pack.js.map +1 -1
  22. package/dist/backend/linker.js +1 -1
  23. package/dist/backend/linker.js.map +1 -1
  24. package/dist/backend/llvm/borrowed-inputs.d.ts +9 -2
  25. package/dist/backend/llvm/borrowed-inputs.js +31 -3
  26. package/dist/backend/llvm/borrowed-inputs.js.map +1 -1
  27. package/dist/backend/llvm/checked-narrows.test.js +1 -1
  28. package/dist/backend/llvm/classes.d.ts +28 -0
  29. package/dist/backend/llvm/classes.js +48 -14
  30. package/dist/backend/llvm/classes.js.map +1 -1
  31. package/dist/backend/llvm/classes.test.js +134 -6
  32. package/dist/backend/llvm/classes.test.js.map +1 -1
  33. package/dist/backend/llvm/common.d.ts +13 -0
  34. package/dist/backend/llvm/common.js +13 -0
  35. package/dist/backend/llvm/common.js.map +1 -1
  36. package/dist/backend/llvm/constant-callbacks.d.ts +1 -1
  37. package/dist/backend/llvm/constant-callbacks.js +3 -3
  38. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  39. package/dist/backend/llvm/constant-callbacks.test.js +1 -1
  40. package/dist/backend/llvm/dense-array-access.js +5 -4
  41. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  42. package/dist/backend/llvm/dense-array-access.test.js +10 -2
  43. package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
  44. package/dist/backend/llvm/dyn.d.ts +2 -1
  45. package/dist/backend/llvm/dyn.js +3 -76
  46. package/dist/backend/llvm/dyn.js.map +1 -1
  47. package/dist/backend/llvm/emitter.d.ts +67 -19
  48. package/dist/backend/llvm/emitter.js +443 -226
  49. package/dist/backend/llvm/emitter.js.map +1 -1
  50. package/dist/backend/llvm/emitter.test.js +21 -3
  51. package/dist/backend/llvm/emitter.test.js.map +1 -1
  52. package/dist/backend/llvm/expr-async.test.js +1 -1
  53. package/dist/backend/llvm/expr-calls.js +14 -6
  54. package/dist/backend/llvm/expr-calls.js.map +1 -1
  55. package/dist/backend/llvm/expr-containers.js +41 -14
  56. package/dist/backend/llvm/expr-containers.js.map +1 -1
  57. package/dist/backend/llvm/expr-control.js +4 -107
  58. package/dist/backend/llvm/expr-control.js.map +1 -1
  59. package/dist/backend/llvm/expr-dispatch.d.ts +1 -2
  60. package/dist/backend/llvm/expr-dispatch.js +0 -41
  61. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  62. package/dist/backend/llvm/expr-dynamic.d.ts +1 -1
  63. package/dist/backend/llvm/expr-dynamic.js +13 -27
  64. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  65. package/dist/backend/llvm/expr-primitives.js +30 -3
  66. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  67. package/dist/backend/llvm/expr-primitives.test.js +55 -2
  68. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  69. package/dist/backend/llvm/expr-stream-bridges.js +4 -4
  70. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  71. package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
  72. package/dist/backend/llvm/lib-dispatch.js +0 -1
  73. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  74. package/dist/backend/llvm/lib-filesystem.js +0 -13
  75. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  76. package/dist/backend/llvm/lib-shared.js +0 -8
  77. package/dist/backend/llvm/lib-shared.js.map +1 -1
  78. package/dist/backend/llvm/local-array-reads.js +3 -3
  79. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  80. package/dist/backend/llvm/local-array-reads.test.js +1 -1
  81. package/dist/backend/llvm/local-union-storage.test.js +1 -1
  82. package/dist/backend/llvm/loop-array-borrows.d.ts +5 -2
  83. package/dist/backend/llvm/loop-array-borrows.js +27 -8
  84. package/dist/backend/llvm/loop-array-borrows.js.map +1 -1
  85. package/dist/backend/llvm/map-number-lookup.d.ts +14 -0
  86. package/dist/backend/llvm/map-number-lookup.js +33 -2
  87. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  88. package/dist/backend/llvm/map-read-lifetimes.d.ts +5 -0
  89. package/dist/backend/llvm/map-read-lifetimes.js +20 -4
  90. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  91. package/dist/backend/llvm/map-read-lifetimes.test.js +25 -3
  92. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  93. package/dist/backend/llvm/reference-writes.d.ts +124 -0
  94. package/dist/backend/llvm/reference-writes.js +1090 -0
  95. package/dist/backend/llvm/reference-writes.js.map +1 -0
  96. package/dist/backend/llvm/shapes.d.ts +8 -5
  97. package/dist/backend/llvm/shapes.js +37 -13
  98. package/dist/backend/llvm/shapes.js.map +1 -1
  99. package/dist/backend/llvm/shapes.test.js +3 -3
  100. package/dist/backend/llvm/stack-unions.test.js +1 -1
  101. package/dist/backend/llvm/string-construction.test.js +1 -1
  102. package/dist/backend/llvm/string-lifetimes.test.js +40 -11
  103. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  104. package/dist/backend/llvm/string-reads.d.ts +3 -0
  105. package/dist/backend/llvm/string-reads.js +31 -0
  106. package/dist/backend/llvm/string-reads.js.map +1 -1
  107. package/dist/backend/llvm/switch-dispatch.d.ts +2 -2
  108. package/dist/backend/llvm/switch-dispatch.js +35 -22
  109. package/dist/backend/llvm/switch-dispatch.js.map +1 -1
  110. package/dist/backend/llvm/switch-dispatch.test.js +29 -11
  111. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  112. package/dist/backend/llvm/walk-borrows.d.ts +8 -1
  113. package/dist/backend/llvm/walk-borrows.js +15 -4
  114. package/dist/backend/llvm/walk-borrows.js.map +1 -1
  115. package/dist/backend/may-throw.d.ts +36 -2
  116. package/dist/backend/may-throw.js +308 -21
  117. package/dist/backend/may-throw.js.map +1 -1
  118. package/dist/backend/may-throw.test.js +147 -2
  119. package/dist/backend/may-throw.test.js.map +1 -1
  120. package/dist/backend/native/artifact-stamps.js +0 -2
  121. package/dist/backend/native/artifact-stamps.js.map +1 -1
  122. package/dist/backend/native/cache-warm.d.ts +1 -1
  123. package/dist/backend/native/cache-warm.js +2 -3
  124. package/dist/backend/native/cache-warm.js.map +1 -1
  125. package/dist/backend/native/compiler-fingerprint.d.ts +1 -1
  126. package/dist/backend/native/compiler-fingerprint.js +4 -7
  127. package/dist/backend/native/compiler-fingerprint.js.map +1 -1
  128. package/dist/backend/native/contracts.d.ts +3 -28
  129. package/dist/backend/native/driver.js +1 -3
  130. package/dist/backend/native/driver.js.map +1 -1
  131. package/dist/backend/native/executable.d.ts +0 -15
  132. package/dist/backend/native/executable.js +17 -172
  133. package/dist/backend/native/executable.js.map +1 -1
  134. package/dist/backend/native/tool-identity.js +0 -1
  135. package/dist/backend/native/tool-identity.js.map +1 -1
  136. package/dist/backend/native/vendor.d.ts +1 -1
  137. package/dist/backend/native/vendor.js +1 -1
  138. package/dist/backend/native/vendor.js.map +1 -1
  139. package/dist/backend/native-link-info-core.d.ts +0 -2
  140. package/dist/backend/native-link-info-core.js.map +1 -1
  141. package/dist/backend/native-toolchain.test.js +23 -33
  142. package/dist/backend/native-toolchain.test.js.map +1 -1
  143. package/dist/backend/publication.d.ts +5 -0
  144. package/dist/backend/publication.js +10 -0
  145. package/dist/backend/publication.js.map +1 -1
  146. package/dist/backend/publication.test.js +1 -1
  147. package/dist/backend/runtime-abi.d.ts +2 -2
  148. package/dist/backend/runtime-abi.js +2 -2
  149. package/dist/backend/runtime-pack-core.d.ts +1 -2
  150. package/dist/backend/runtime-pack-core.js +5 -14
  151. package/dist/backend/runtime-pack-core.js.map +1 -1
  152. package/dist/backend/runtime-pack-targets.test.js +3 -8
  153. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  154. package/dist/backend/runtime-pack.test.js +22 -19
  155. package/dist/backend/runtime-pack.test.js.map +1 -1
  156. package/dist/backend/target-diagnostics.d.ts +0 -5
  157. package/dist/backend/target-diagnostics.js +1 -11
  158. package/dist/backend/target-diagnostics.js.map +1 -1
  159. package/dist/backend/target-diagnostics.test.js +1 -1
  160. package/dist/backend/vendor-archives.d.ts +1 -7
  161. package/dist/backend/vendor-archives.js +9 -189
  162. package/dist/backend/vendor-archives.js.map +1 -1
  163. package/dist/backend/vendor-inputs.d.ts +0 -1
  164. package/dist/backend/vendor-inputs.js +0 -1
  165. package/dist/backend/vendor-inputs.js.map +1 -1
  166. package/dist/backend/warm-cache.js +3 -5
  167. package/dist/backend/warm-cache.js.map +1 -1
  168. package/dist/cli/command.js +4 -8
  169. package/dist/cli/command.js.map +1 -1
  170. package/dist/cli/host.d.ts +1 -1
  171. package/dist/cli/usage.d.ts +1 -5
  172. package/dist/cli/usage.js +4 -9
  173. package/dist/cli/usage.js.map +1 -1
  174. package/dist/compat/fetch-profile.d.ts +2 -2
  175. package/dist/compat/fetch-profile.js +16 -25
  176. package/dist/compat/fetch-profile.js.map +1 -1
  177. package/dist/compile-types.d.ts +2 -17
  178. package/dist/contracts/check.d.ts +8 -0
  179. package/dist/contracts/check.js +1573 -0
  180. package/dist/contracts/check.js.map +1 -0
  181. package/dist/coverage/report.d.ts +5 -22
  182. package/dist/coverage/report.js +12 -76
  183. package/dist/coverage/report.js.map +1 -1
  184. package/dist/coverage/surface-manifest.d.ts +3 -3
  185. package/dist/coverage/surface-manifest.js +22 -27
  186. package/dist/coverage/surface-manifest.js.map +1 -1
  187. package/dist/coverage/verdict-cache.d.ts +1 -3
  188. package/dist/coverage/verdict-cache.js +2 -7
  189. package/dist/coverage/verdict-cache.js.map +1 -1
  190. package/dist/diagnostics/diagnostic.d.ts +11 -60
  191. package/dist/diagnostics/diagnostic.js +39 -118
  192. package/dist/diagnostics/diagnostic.js.map +1 -1
  193. package/dist/diagnostics/envelope.d.ts +3 -4
  194. package/dist/diagnostics/envelope.js +0 -9
  195. package/dist/diagnostics/envelope.js.map +1 -1
  196. package/dist/diagnostics/envelope.test.js +1 -1
  197. package/dist/diagnostics/envelope.test.js.map +1 -1
  198. package/dist/executable/executable-cache.d.ts +0 -3
  199. package/dist/executable/executable-cache.js +2 -4
  200. package/dist/executable/executable-cache.js.map +1 -1
  201. package/dist/executable/executable-cache.test.js +0 -4
  202. package/dist/executable/executable-cache.test.js.map +1 -1
  203. package/dist/executable/prepare.js +14 -2
  204. package/dist/executable/prepare.js.map +1 -1
  205. package/dist/frontend/analysis.js +10 -10
  206. package/dist/frontend/analysis.js.map +1 -1
  207. package/dist/frontend/cjs-syntax.js +0 -88
  208. package/dist/frontend/cjs-syntax.js.map +1 -1
  209. package/dist/frontend/contract-types.d.ts +50 -0
  210. package/dist/frontend/contract-types.js +232 -0
  211. package/dist/frontend/contract-types.js.map +1 -0
  212. package/dist/frontend/dts-paths.d.ts +1 -1
  213. package/dist/frontend/dts-paths.js +1 -1
  214. package/dist/frontend/lowering/abstract-equality.js +1 -4
  215. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  216. package/dist/frontend/lowering/array-values.js +4 -11
  217. package/dist/frontend/lowering/array-values.js.map +1 -1
  218. package/dist/frontend/lowering/builtins/arguments.js +0 -13
  219. package/dist/frontend/lowering/builtins/arguments.js.map +1 -1
  220. package/dist/frontend/lowering/builtins/crypto.d.ts +0 -4
  221. package/dist/frontend/lowering/builtins/crypto.js +0 -4
  222. package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
  223. package/dist/frontend/lowering/builtins/json.js +1 -39
  224. package/dist/frontend/lowering/builtins/json.js.map +1 -1
  225. package/dist/frontend/lowering/builtins/module-resolution.d.ts +0 -15
  226. package/dist/frontend/lowering/builtins/module-resolution.js +5 -44
  227. package/dist/frontend/lowering/builtins/module-resolution.js.map +1 -1
  228. package/dist/frontend/lowering/builtins/numbers.d.ts +0 -6
  229. package/dist/frontend/lowering/builtins/numbers.js +6 -57
  230. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  231. package/dist/frontend/lowering/builtins/promises.js +4 -67
  232. package/dist/frontend/lowering/builtins/promises.js.map +1 -1
  233. package/dist/frontend/lowering/builtins/workers.js +1 -1
  234. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  235. package/dist/frontend/lowering/call-arguments.d.ts +1 -37
  236. package/dist/frontend/lowering/call-arguments.js +11 -94
  237. package/dist/frontend/lowering/call-arguments.js.map +1 -1
  238. package/dist/frontend/lowering/call-signatures.d.ts +1 -6
  239. package/dist/frontend/lowering/call-signatures.js +16 -68
  240. package/dist/frontend/lowering/call-signatures.js.map +1 -1
  241. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +19 -4
  242. package/dist/frontend/lowering/class-dynamic-dispatch.js +168 -98
  243. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  244. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +44 -4
  245. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
  246. package/dist/frontend/lowering/class-method-mutations.js +0 -2
  247. package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
  248. package/dist/frontend/lowering/class-protocols.js +1 -1
  249. package/dist/frontend/lowering/class-protocols.js.map +1 -1
  250. package/dist/frontend/lowering/coercions/function-adapters.js +5 -35
  251. package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
  252. package/dist/frontend/lowering/coercions/state.d.ts +0 -1
  253. package/dist/frontend/lowering/coercions/state.js +0 -1
  254. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  255. package/dist/frontend/lowering/coercions/structural-plans.js +2 -4
  256. package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -1
  257. package/dist/frontend/lowering/coercions/structural.js +2 -12
  258. package/dist/frontend/lowering/coercions/structural.js.map +1 -1
  259. package/dist/frontend/lowering/coercions/unions.d.ts +12 -0
  260. package/dist/frontend/lowering/coercions/unions.js +63 -1
  261. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  262. package/dist/frontend/lowering/containers/array-callbacks.js +9 -28
  263. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  264. package/dist/frontend/lowering/containers/array-construction.js +2 -3
  265. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  266. package/dist/frontend/lowering/containers/array-methods.js +10 -54
  267. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  268. package/dist/frontend/lowering/containers/bytes.js +0 -1
  269. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  270. package/dist/frontend/lowering/containers/callback-arguments.js +2 -10
  271. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  272. package/dist/frontend/lowering/containers/collection-methods.js +1 -6
  273. package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
  274. package/dist/frontend/lowering/containers/indexed-objects.js +0 -2
  275. package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
  276. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +0 -6
  277. package/dist/frontend/lowering/containers/string-and-regexp.js +2 -10
  278. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  279. package/dist/frontend/lowering/expressions/evaluation-safety.js +0 -3
  280. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
  281. package/dist/frontend/lowering/expressions/native-proxy.js +1 -1
  282. package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
  283. package/dist/frontend/lowering/expressions/nullish-assignment.js +0 -1
  284. package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
  285. package/dist/frontend/lowering/expressions/object-literals.d.ts +0 -8
  286. package/dist/frontend/lowering/expressions/object-literals.js +8 -35
  287. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  288. package/dist/frontend/lowering/expressions/optional-chains.d.ts +0 -4
  289. package/dist/frontend/lowering/expressions/optional-chains.js +7 -67
  290. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  291. package/dist/frontend/lowering/function-context.js +0 -1
  292. package/dist/frontend/lowering/function-context.js.map +1 -1
  293. package/dist/frontend/lowering/generic-functions.js +0 -1
  294. package/dist/frontend/lowering/generic-functions.js.map +1 -1
  295. package/dist/frontend/lowering/lib-boundary.js +10 -77
  296. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  297. package/dist/frontend/lowering/lower-array-sort.js +1 -10
  298. package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
  299. package/dist/frontend/lowering/lower-array-sort.test.js +2 -2
  300. package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -1
  301. package/dist/frontend/lowering/lower-assert.js +2 -3
  302. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  303. package/dist/frontend/lowering/lower-async-iteration.js +1 -1
  304. package/dist/frontend/lowering/lower-calls.d.ts +2 -27
  305. package/dist/frontend/lowering/lower-calls.js +56 -465
  306. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  307. package/dist/frontend/lowering/lower-classes.js +11 -49
  308. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  309. package/dist/frontend/lowering/lower-comptime.js +2 -3
  310. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  311. package/dist/frontend/lowering/lower-enums.js +5 -8
  312. package/dist/frontend/lowering/lower-enums.js.map +1 -1
  313. package/dist/frontend/lowering/lower-event-emitter.js +3 -4
  314. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  315. package/dist/frontend/lowering/lower-expando.js +3 -5
  316. package/dist/frontend/lowering/lower-expando.js.map +1 -1
  317. package/dist/frontend/lowering/lower-exprs.d.ts +0 -8
  318. package/dist/frontend/lowering/lower-exprs.js +133 -908
  319. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  320. package/dist/frontend/lowering/lower-functions.js +2 -5
  321. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  322. package/dist/frontend/lowering/lower-inspect.js +1 -50
  323. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  324. package/dist/frontend/lowering/lower-modules.d.ts +0 -43
  325. package/dist/frontend/lowering/lower-modules.js +25 -329
  326. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  327. package/dist/frontend/lowering/lower-modules.test.js +5 -5
  328. package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
  329. package/dist/frontend/lowering/lower-server.js +0 -30
  330. package/dist/frontend/lowering/lower-server.js.map +1 -1
  331. package/dist/frontend/lowering/lower-stdlib.d.ts +98 -0
  332. package/dist/frontend/lowering/{lower-island.js → lower-stdlib.js} +97 -1160
  333. package/dist/frontend/lowering/lower-stdlib.js.map +1 -0
  334. package/dist/frontend/lowering/lower-stmts.d.ts +0 -16
  335. package/dist/frontend/lowering/lower-stmts.js +58 -1016
  336. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  337. package/dist/frontend/lowering/lower-stmts.test.js +1 -1
  338. package/dist/frontend/lowering/lower-stmts.test.js.map +1 -1
  339. package/dist/frontend/lowering/lower-stream.js +1 -3
  340. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  341. package/dist/frontend/lowering/lowerer.d.ts +26 -151
  342. package/dist/frontend/lowering/lowerer.js +120 -770
  343. package/dist/frontend/lowering/lowerer.js.map +1 -1
  344. package/dist/frontend/lowering/lowerer.test.js +4 -51
  345. package/dist/frontend/lowering/lowerer.test.js.map +1 -1
  346. package/dist/frontend/lowering/narrowing-stability.d.ts +100 -0
  347. package/dist/frontend/lowering/narrowing-stability.js +1174 -0
  348. package/dist/frontend/lowering/narrowing-stability.js.map +1 -0
  349. package/dist/frontend/lowering/narrowing-stability.test.js +150 -0
  350. package/dist/frontend/lowering/narrowing-stability.test.js.map +1 -0
  351. package/dist/frontend/lowering/optional-arguments.js +0 -2
  352. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  353. package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
  354. package/dist/frontend/lowering/strict-equality.js +1 -1
  355. package/dist/frontend/lowering/strict-equality.js.map +1 -1
  356. package/dist/frontend/lowering/surfaces.d.ts +6 -45
  357. package/dist/frontend/lowering/surfaces.js +5 -65
  358. package/dist/frontend/lowering/surfaces.js.map +1 -1
  359. package/dist/frontend/module-syntax.d.ts +0 -3
  360. package/dist/frontend/module-syntax.js.map +1 -1
  361. package/dist/frontend/npm-node.d.ts +0 -7
  362. package/dist/frontend/npm-node.js +0 -7
  363. package/dist/frontend/npm-node.js.map +1 -1
  364. package/dist/frontend/npm-static-rewrite-syntax.d.ts +1 -1
  365. package/dist/frontend/npm-static-rewrite-syntax.js +6 -82
  366. package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -1
  367. package/dist/frontend/npm-static.d.ts +1 -1
  368. package/dist/frontend/npm-static.js +6 -71
  369. package/dist/frontend/npm-static.js.map +1 -1
  370. package/dist/frontend/npm.d.ts +3 -319
  371. package/dist/frontend/npm.js +6 -1007
  372. package/dist/frontend/npm.js.map +1 -1
  373. package/dist/frontend/npm.test.js +2 -197
  374. package/dist/frontend/npm.test.js.map +1 -1
  375. package/dist/frontend/pipeline-native.js +1 -2
  376. package/dist/frontend/pipeline-native.js.map +1 -1
  377. package/dist/frontend/pipeline.d.ts +3 -11
  378. package/dist/frontend/pipeline.js +37 -56
  379. package/dist/frontend/pipeline.js.map +1 -1
  380. package/dist/frontend/pipeline.test.js +12 -6
  381. package/dist/frontend/pipeline.test.js.map +1 -1
  382. package/dist/frontend/program.d.ts +0 -34
  383. package/dist/frontend/program.js +13 -98
  384. package/dist/frontend/program.js.map +1 -1
  385. package/dist/frontend/provenance-core.d.ts +0 -4
  386. package/dist/frontend/provenance-core.js +4 -37
  387. package/dist/frontend/provenance-core.js.map +1 -1
  388. package/dist/frontend/provenance-registry.d.ts +0 -4
  389. package/dist/frontend/provenance-registry.js +0 -20
  390. package/dist/frontend/provenance-registry.js.map +1 -1
  391. package/dist/frontend/resolve.d.ts +0 -12
  392. package/dist/frontend/resolve.js +0 -34
  393. package/dist/frontend/resolve.js.map +1 -1
  394. package/dist/frontend/runtime-resolve.js +2 -2
  395. package/dist/frontend/runtime-resolve.js.map +1 -1
  396. package/dist/frontend/services-node.js +2 -3
  397. package/dist/frontend/services-node.js.map +1 -1
  398. package/dist/frontend/services.d.ts +1 -8
  399. package/dist/frontend/services.js +2 -22
  400. package/dist/frontend/services.js.map +1 -1
  401. package/dist/frontend/services.test.js +0 -38
  402. package/dist/frontend/services.test.js.map +1 -1
  403. package/dist/frontend/tsc-codes.js +0 -11
  404. package/dist/frontend/tsc-codes.js.map +1 -1
  405. package/dist/frontend/type-mapper.d.ts +2 -27
  406. package/dist/frontend/type-mapper.js +85 -153
  407. package/dist/frontend/type-mapper.js.map +1 -1
  408. package/dist/index.d.ts +1 -1
  409. package/dist/index.js +7 -10
  410. package/dist/index.js.map +1 -1
  411. package/dist/ir/api.d.ts +1 -1
  412. package/dist/ir/api.js +1 -1
  413. package/dist/ir/api.js.map +1 -1
  414. package/dist/ir/builtin-effects.js +0 -5
  415. package/dist/ir/builtin-effects.js.map +1 -1
  416. package/dist/ir/builtin-signatures.d.ts +1 -1
  417. package/dist/ir/builtin-signatures.js +1 -55
  418. package/dist/ir/builtin-signatures.js.map +1 -1
  419. package/dist/ir/constant-globals.test.js +1 -1
  420. package/dist/ir/constant-tables.test.js +1 -1
  421. package/dist/ir/final-fields.test.js +1 -1
  422. package/dist/ir/initializer-bindings.test.js +1 -1
  423. package/dist/ir/int32-slots.d.ts +29 -3
  424. package/dist/ir/int32-slots.js +152 -66
  425. package/dist/ir/int32-slots.js.map +1 -1
  426. package/dist/ir/int32-slots.test.js +1 -1
  427. package/dist/ir/integer-loops.js +3 -0
  428. package/dist/ir/integer-loops.js.map +1 -1
  429. package/dist/ir/integer-loops.test.js +26 -1
  430. package/dist/ir/integer-loops.test.js.map +1 -1
  431. package/dist/ir/integer-ranges.d.ts +20 -0
  432. package/dist/ir/integer-ranges.js +178 -21
  433. package/dist/ir/integer-ranges.js.map +1 -1
  434. package/dist/ir/integer-ranges.test.js +148 -3
  435. package/dist/ir/integer-ranges.test.js.map +1 -1
  436. package/dist/ir/ir.d.ts +44 -284
  437. package/dist/ir/ir.js +10 -259
  438. package/dist/ir/ir.js.map +1 -1
  439. package/dist/ir/ir.test.js +2 -7
  440. package/dist/ir/ir.test.js.map +1 -1
  441. package/dist/ir/runtime-features.d.ts +3 -30
  442. package/dist/ir/runtime-features.js +5 -41
  443. package/dist/ir/runtime-features.js.map +1 -1
  444. package/dist/ir/scalar-records.test.js +1 -1
  445. package/dist/ir/serialize.d.ts +6 -2
  446. package/dist/ir/serialize.js +11 -3
  447. package/dist/ir/serialize.js.map +1 -1
  448. package/dist/ir/traverse.js +0 -21
  449. package/dist/ir/traverse.js.map +1 -1
  450. package/dist/ir/unshare.d.ts +14 -0
  451. package/dist/ir/unshare.js +60 -0
  452. package/dist/ir/unshare.js.map +1 -0
  453. package/dist/ir/unshare.test.js +115 -0
  454. package/dist/ir/unshare.test.js.map +1 -0
  455. package/dist/ir/validate.js +9 -207
  456. package/dist/ir/validate.js.map +1 -1
  457. package/dist/ir/validate.test.js +6 -6
  458. package/dist/library/fence-eval.test.js +1 -1
  459. package/dist/library/int-infer.test.js +4 -4
  460. package/dist/library/library-cache.test.js +5 -5
  461. package/dist/library/prepare.js +14 -20
  462. package/dist/library/prepare.js.map +1 -1
  463. package/dist/native/cli.js +0 -2
  464. package/dist/native/cli.js.map +1 -1
  465. package/dist/native/compiler.test.js +2 -4
  466. package/dist/native/compiler.test.js.map +1 -1
  467. package/dist/native/prepare.js +2 -4
  468. package/dist/native/prepare.js.map +1 -1
  469. package/dist/native/sanitizer.js +2 -16
  470. package/dist/native/sanitizer.js.map +1 -1
  471. package/dist/native/sanitizer.test.js +0 -3
  472. package/dist/native/sanitizer.test.js.map +1 -1
  473. package/dist/startup-cache.js +0 -2
  474. package/dist/startup-cache.js.map +1 -1
  475. package/package.json +20 -20
  476. package/surface-manifest.json +80 -88
  477. package/dist/backend/llvm/expr-island.d.ts +0 -15
  478. package/dist/backend/llvm/expr-island.js +0 -709
  479. package/dist/backend/llvm/expr-island.js.map +0 -1
  480. package/dist/frontend/lowering/coercions/island.d.ts +0 -37
  481. package/dist/frontend/lowering/coercions/island.js +0 -350
  482. package/dist/frontend/lowering/coercions/island.js.map +0 -1
  483. package/dist/frontend/lowering/lower-island.d.ts +0 -229
  484. package/dist/frontend/lowering/lower-island.js.map +0 -1
  485. package/dist/frontend/lowering/lower-island.test.js +0 -56
  486. package/dist/frontend/lowering/lower-island.test.js.map +0 -1
  487. package/dist/frontend/npm-fetch-analysis.d.ts +0 -17
  488. package/dist/frontend/npm-fetch-analysis.js +0 -109
  489. package/dist/frontend/npm-fetch-analysis.js.map +0 -1
  490. package/dist/frontend/npm-fetch-analysis.test.js +0 -130
  491. package/dist/frontend/npm-fetch-analysis.test.js.map +0 -1
  492. package/dist/frontend/npm-fetch-node.d.ts +0 -2
  493. package/dist/frontend/npm-fetch-node.js +0 -12
  494. package/dist/frontend/npm-fetch-node.js.map +0 -1
  495. package/dist/frontend/npm-fetch-syntax.d.ts +0 -6
  496. package/dist/frontend/npm-fetch-syntax.js +0 -202
  497. package/dist/frontend/npm-fetch-syntax.js.map +0 -1
  498. package/dist/native/runtime-typescript.d.ts +0 -4
  499. package/dist/native/runtime-typescript.js +0 -74
  500. package/dist/native/runtime-typescript.js.map +0 -1
  501. package/dist/native/runtime-typescript.test.js +0 -41
  502. package/dist/native/runtime-typescript.test.js.map +0 -1
  503. /package/dist/{frontend/lowering/lower-island.test.d.ts → backend/immutability.test.d.ts} +0 -0
  504. /package/dist/frontend/{npm-fetch-analysis.test.d.ts → lowering/narrowing-stability.test.d.ts} +0 -0
  505. /package/dist/{native/runtime-typescript.test.d.ts → ir/unshare.test.d.ts} +0 -0
@@ -2,30 +2,12 @@ import { emitLlvmLayouts } from "./layouts.js";
2
2
  import { FIRST_EMITTED_CYC_KIND, inlineAllocSupported } from "./alloc.js";
3
3
  import { llvmBytes as llBytes } from "../literals.js";
4
4
  import { InternalCompilerError } from "../../errors.js";
5
- /** Lower typed IR to LLVM, using the runtime's C ABI.
6
- *
7
- * Reference-counted temporaries own one reference. Declarations, assignments,
8
- * returns and arguments move that ownership; frame and scope exits release
9
- * everything remaining. Slot-backed entries release the slot's current value.
10
- *
11
- * Exceptions use a pending flag and explicit cleanup edges. Throwing calls
12
- * test scr_exc_pending before consuming results, release the scopes crossed,
13
- * and branch to the innermost handler or return to the caller. Catch bindings
14
- * hold ScrCaught snapshots; finally runs on normal, throwing and returning
15
- * paths. This keeps ownership cleanup intact without nonlocal jumps.
16
- *
17
- * Program objects are emitted by the packaged LLVM helper and linked with
18
- * runtime packs. Sanitizer development builds use an external LLVM driver.
19
- * Dynamic values and islands share the runtime ABI; embedded npm modules use
20
- * compressed source tables that the runtime inflates on demand.
21
- */
22
5
  import { createHash } from "node:crypto";
23
- import { deflateRawSync } from "node:zlib";
24
6
  import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible, undefinedArmTag, } from "../../ir/analysis.js";
25
7
  import { emitLibraryIdentityLines } from "../library-identity-markers.js";
26
8
  import { loopTestsEveryIteration } from "../loop-polls.js";
27
9
  import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeEquals, typeKey, STRING, VOID, } from "../../ir/ir.js";
28
- import { moduleRuntimeFeatures, moduleEmbedsBuiltin, moduleEmbedsCompressedNpm, NPM_COMPRESS_MIN, } from "../../ir/runtime-features.js";
10
+ import { moduleRuntimeFeatures } from "../../ir/runtime-features.js";
29
11
  import { matchIntegerArrayForLoop, matchIntegerBytesForLoop, matchIntegerCountedForLoop, } from "../../ir/integer-loops.js";
30
12
  import { emitCountedLoopLimit } from "./counted-loops.js";
31
13
  import { emitLiteralSwitch } from "./switch-dispatch.js";
@@ -36,6 +18,7 @@ import { scalarizeNumericRecords } from "../../ir/scalar-records.js";
36
18
  import { specializeNumericCalls } from "../../ir/numeric-call-specialization.js";
37
19
  import { everyExprChild, everyStmtChild, everyStmtList } from "../../ir/traverse.js";
38
20
  import { analyzeIntegerRanges, INT32_RANGE } from "../../ir/integer-ranges.js";
21
+ import { unshareNodes } from "../../ir/unshare.js";
39
22
  import { analyzeInt32Slots } from "../../ir/int32-slots.js";
40
23
  import { analyzeFinalFields, constructorClass } from "../../ir/final-fields.js";
41
24
  import { foldConstantGlobals } from "../../ir/constant-globals.js";
@@ -56,10 +39,11 @@ import { CheckedNarrows, emitCheckedNarrowReceiver } from "./checked-narrows.js"
56
39
  import { emitDenseArrayGet, emitDenseArraySet } from "./dense-array-access.js";
57
40
  import { emitStackMapRead, findMapReadLifetimes, matchMapRead, } from "./map-read-lifetimes.js";
58
41
  import { emitBorrowedFieldSequence, guardedValue } from "./borrowed-receivers.js";
59
- import { emitBorrowedInput } from "./borrowed-inputs.js";
42
+ import { borrowableInputs, emitBorrowedInput } from "./borrowed-inputs.js";
60
43
  import { analyzeWalks, findWalkBorrows } from "./walk-borrows.js";
61
44
  import { findReboundParameters } from "./rebound-parameters.js";
62
45
  import { ReferenceEffects } from "./reference-effects.js";
46
+ import { ReferenceWrites } from "./reference-writes.js";
63
47
  import { LlvmDebugInfo } from "./debug-info.js";
64
48
  import { StackCallbacks } from "./stack-callbacks.js";
65
49
  import { findLoopArrayBorrows } from "./loop-array-borrows.js";
@@ -75,9 +59,9 @@ import { emitBorrowedResultCall, emitCallExpr } from "./expr-calls.js";
75
59
  import { emitDynamicExpr } from "./expr-dynamic.js";
76
60
  import { emitIntrinsicExpr, emitSerializationExpr, emitAsyncExpr } from "./expr-async.js";
77
61
  import { PublishWalkers } from "./publish.js";
62
+ import { computeImmutableFields, stableGlobals } from "../immutability.js";
78
63
  import { computePublishedTypes } from "../publication.js";
79
- import { emitJsInteropExpr, emitExpr } from "./expr-dispatch.js";
80
- import { emitJsMarshal, emitJsOp, emitJsExit, islandAdapter, islandTypedAdapter, } from "./expr-island.js";
64
+ import { emitExpr } from "./expr-dispatch.js";
81
65
  import { dynKind, raceAdapterFor, genResultThunkFor, childExitThunkFor, childExitSignalThunkFor, childDataThunkFor, execFileThunkFor, ipcMessageThunkFor, ipcSendThunkFor, emitterFixedAdapter, wrapEmitterListener, unwrapNullableClosure, closeBindThunkFor, closeOverrideWrapFor, } from "./expr-callbacks.js";
82
66
  import { streamDataAdapter, streamDoneFnFor, cryptoBytesThunkFor, fsRenameThunkFor, streamCbThunkFor, zlibBytesThunkFor, } from "./expr-stream-callbacks.js";
83
67
  import { emitStableReceiver, resolveThunkFor, tagInSet, arrPush, emitArrayCopyLoop, emitStrIntrinsic, emitArrIntrinsic, emitDiscardedSplice, wrapNullable, emitMapNew, mapSet, emitMapLikeIntrinsic, emitSetNew, } from "./expr-containers.js";
@@ -97,7 +81,7 @@ import { LlWalkers } from "./walkers.js";
97
81
  import { isObjectArm, NULLABLE_ABSENT, NULLABLE_NULL, NullableUnions } from "./nullable-unions.js";
98
82
  import { emitNullablePresent, emitUnionPeek, emitUnionTag } from "./union-repr.js";
99
83
  import { literalCapWord, literalCapWord32 } from "./string-key-hash.js";
100
- import { NUMBER_MAP_ENTRY_DECL } from "./map-number-lookup.js";
84
+ import { NUMBER_MAP_ENTRY_DECL, STRING_MAP_ENTRY_DECL } from "./map-number-lookup.js";
101
85
  import { STRING_READ_HELPERS } from "./string-reads.js";
102
86
  import { emitConcatInputs, emitMixedConcat, numberPart, stringParts, } from "./string-construction.js";
103
87
  import { arrNewCall, boxAccess, boxNewCall, boxReleaseSym, computeTraced, elemAccess, emitInlineRcHelpers, emitNullableRetainWrappers, FN_ATTRS, inlineRcDecls, llFieldType, releaseSym, retainSym, traceArg, typedRefConstructor, vAdapters, } from "./shapes.js";
@@ -127,14 +111,18 @@ function ffiCallbackDummyLl(callback) {
127
111
  return "i32 0";
128
112
  }
129
113
  }
130
- export function emitLlvmModule(mod, options = {}) {
114
+ export function emitLlvmModule(input, options = {}) {
115
+ // Facts are keyed by node identity: emit the same module whether it was
116
+ // lowered in memory or read back from its serialized form.
117
+ const mod = unshareNodes(input);
131
118
  // Keep source storage intact for debugger inspection in dev builds.
132
119
  return new LlEmitter(options.debugSources === undefined
133
120
  ? scalarizeNumericRecords(specializeNumericCalls(foldConstantGlobals(mod), mangleFunction))
134
121
  : mod, options).emit();
135
122
  }
136
123
  /** Keep large translation units below the host's single-string limit. */
137
- export function emitLlvmModuleSource(mod, options = {}) {
124
+ export function emitLlvmModuleSource(input, options = {}) {
125
+ const mod = unshareNodes(input);
138
126
  const parts = new LlEmitter(options.debugSources === undefined
139
127
  ? scalarizeNumericRecords(specializeNumericCalls(foldConstantGlobals(mod), mangleFunction))
140
128
  : mod, options).emitParts();
@@ -302,6 +290,12 @@ export class LlEmitter {
302
290
  * fiberless-async analysis scans it for suspension entry points. */
303
291
  fnDefText = new Map();
304
292
  referenceEffects;
293
+ /** Field-sensitive reference writes (null in debug builds). */
294
+ referenceWrites = null;
295
+ borrowedReturnKeyCache = new Map();
296
+ /** The statement list being emitted and the current index, for blocks
297
+ * whose bindings end with the list (emitBlock and function bodies). */
298
+ stmtCursor = null;
305
299
  optionalArrayReads;
306
300
  checkedNarrows;
307
301
  callArrayReads = new Map();
@@ -345,6 +339,9 @@ export class LlEmitter {
345
339
  ffiHasRetainedCallback;
346
340
  ffiHasForeignCallback;
347
341
  globalTypes = new Map();
342
+ /** Module constants that keep one value once initialized
343
+ * (immutability.ts stableGlobals). */
344
+ stableGlobals = new Set();
348
345
  tdzGlobals = new Map();
349
346
  constantNumericTables;
350
347
  /** May-throw analysis (computeMayThrow): pending
@@ -360,6 +357,13 @@ export class LlEmitter {
360
357
  /** Static types whose values can be published (@scriptc/threads): their
361
358
  * stores carry the frozen-object guard. Null without publish calls. */
362
359
  publishedTypes;
360
+ /** Construction-only class and record fields (immutability.ts); null
361
+ * where the facts are disabled. */
362
+ immutableFields;
363
+ /** Constructor function names: their bodies may still write their own
364
+ * `this`'s construction-only fields. */
365
+ constructorFunctions;
366
+ inConstructor = false;
363
367
  /** Method names with at least one may-throw implementation — the
364
368
  * virtualCall pending check's key. */
365
369
  mayThrowMethods = new Set();
@@ -436,6 +440,12 @@ export class LlEmitter {
436
440
  initializerBindings;
437
441
  /** Whole-program int32 storage and parameter/result facts. */
438
442
  int32Slots;
443
+ /** Immutable module constants (contract types) whose graphs become
444
+ * immortal once initialized: their reads need no reference counting. */
445
+ immortalGlobals;
446
+ /** ProgramLifetime hierarchy roots (contract types): their instances are
447
+ * immortal from construction (classes.ts programLifetimeClass). */
448
+ programLifetime;
439
449
  numericLocals = new Map();
440
450
  captureIds = new Set();
441
451
  /** Active induction bindings retain their width independently of size_t. */
@@ -495,6 +505,8 @@ export class LlEmitter {
495
505
  this.constantNumericTables = findConstantNumericTables(mod);
496
506
  this.initializerBindings = findInitializerBindings(mod);
497
507
  this.int32Slots = analyzeInt32Slots(mod);
508
+ this.immortalGlobals = new Set(mod.contracts?.immortalGlobals ?? []);
509
+ this.programLifetime = new Set(mod.contracts?.programLifetime ?? []);
498
510
  this.finalFields = analyzeFinalFields(mod);
499
511
  this.sizeType = options.pointerBits === 32 ? "i32" : "i64";
500
512
  this.ffiExtendNarrowIntegers =
@@ -549,6 +561,8 @@ export class LlEmitter {
549
561
  this.workerEntryPolls = mt.workerEntryPolls ?? null;
550
562
  this.stackChecks = mt.stackChecks ?? null;
551
563
  this.publishedTypes = computePublishedTypes(mod);
564
+ this.immutableFields = computeImmutableFields(mod);
565
+ this.constructorFunctions = new Set((mod.classes ?? []).map((c) => `%${c.name}.constructor`));
552
566
  this.indirectMayThrow = mt.indirect || mod.workers === true;
553
567
  for (const cls of mod.classes ?? []) {
554
568
  for (const m of cls.methods ?? []) {
@@ -568,6 +582,8 @@ export class LlEmitter {
568
582
  this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById, (id) => this.nullableUnions.has(id));
569
583
  this.checkedNarrows = new CheckedNarrows(this.fnByName);
570
584
  this.referenceEffects = new ReferenceEffects(this.fnByName, (call) => this.optionalArrayReads.get(call) !== null, mod.classes ?? []);
585
+ if (this.debug === null)
586
+ this.referenceWrites = new ReferenceWrites(this.fnByName, mod.classes ?? [], (call) => this.optionalArrayReads.get(call) !== null, new Set((mod.globals ?? []).map((g) => g.id)), { unions: mod.unions, records: mod.records }, this.immutableFields);
571
587
  const walkParameters = new Map();
572
588
  if (this.debug === null) {
573
589
  const host = {
@@ -575,9 +591,10 @@ export class LlEmitter {
575
591
  borrowsWithoutOwning: (e) => this.borrowsWithoutOwning(e),
576
592
  };
577
593
  for (const fn of this.fnByName.values()) {
578
- if (!this.referenceEffects.functions.has(fn.name))
594
+ const preserving = this.referenceEffects.functions.has(fn.name);
595
+ if (!preserving && this.referenceWrites === null)
579
596
  continue;
580
- const walks = findWalkBorrows(fn, host);
597
+ const walks = findWalkBorrows(fn, preserving ? host : this.unwrittenWalkHost(fn, host));
581
598
  if (walks.size === 0)
582
599
  continue;
583
600
  this.walkBorrowsByFunction.set(fn.name, walks);
@@ -661,6 +678,7 @@ export class LlEmitter {
661
678
  },
662
679
  pendingTestLines: (dest) => this.pendingTestLines(dest),
663
680
  virtualEntry: (implFn) => this.virtualEntry(implFn),
681
+ programLifetime: this.programLifetime,
664
682
  };
665
683
  this.walkers = new LlWalkers(this.shapeHost);
666
684
  this.dyn = new LlDyn(this.shapeHost);
@@ -682,6 +700,7 @@ export class LlEmitter {
682
700
  if (g.tdz)
683
701
  this.tdzGlobals.set(g.id, g.name);
684
702
  }
703
+ this.stableGlobals = stableGlobals(mod);
685
704
  // Runtime error, EventEmitter, and stream classes have known layouts.
686
705
  // Subclasses embed the ScrEmitter/ScrStream prefixes from classes.ts.
687
706
  const classes = mod.classes ?? [];
@@ -1087,23 +1106,6 @@ export class LlEmitter {
1087
1106
  const asyncDefs = this.emitAsyncScaffolding();
1088
1107
  const ffiCallbacks = this.emitFfiCallbackDefs();
1089
1108
  const hasNoInlineRecordClone = [...this.recordCloneShapes].some((shapeId) => (this.recordsById.get(shapeId)?.fields.length ?? 0) >= 16);
1090
- const embedded = this.mod.embedded;
1091
- const usesIsland = embedded !== undefined && embedded.modules.length > 0;
1092
- const storeNpmText = (text) => {
1093
- const plain = Buffer.from(text, "utf8");
1094
- if (text.length < NPM_COMPRESS_MIN)
1095
- return { bytes: plain, raw: 0 };
1096
- const deflated = deflateRawSync(plain, { level: 9 });
1097
- return deflated.length < plain.length
1098
- ? { bytes: deflated, raw: plain.length }
1099
- : { bytes: plain, raw: 0 };
1100
- };
1101
- const npmStored = usesIsland
1102
- ? embedded.modules.map((m) => ({
1103
- src: storeNpmText(m.source),
1104
- esm: m.esm === undefined ? null : storeNpmText(m.esm),
1105
- }))
1106
- : [];
1107
1109
  // Module globals, FIRST OCCURRENCE per id: a class-expression static
1108
1110
  // instantiated through several mixin applications registers one global
1109
1111
  // id several times (2041-mixin-values) — C's tentative definitions
@@ -1167,24 +1169,14 @@ export class LlEmitter {
1167
1169
  const usesDgram = runtimeFeatures.dgram;
1168
1170
  const usesHttp2 = runtimeFeatures.http2;
1169
1171
  const usesHttp = runtimeFeatures.http;
1170
- // Fetch-referencing programs register the native fetch bridge before
1171
- // any island entry (the engine's lazy boot consults it) — native/executable.ts
1172
- // compiles scr_fetch.c on the same predicate.
1172
+ // Fetch-referencing programs register native fetch before main.
1173
+ // Runtime selection compiles scr_fetch.c on the same predicate.
1173
1174
  const usesFetch = runtimeFeatures.fetch;
1174
- const embedsZlib = moduleEmbedsBuiltin(this.mod, "node:zlib");
1175
- const embedsNet = moduleEmbedsBuiltin(this.mod, "node:http") ||
1176
- moduleEmbedsBuiltin(this.mod, "node:https") ||
1177
- moduleEmbedsBuiltin(this.mod, "node:net") ||
1178
- moduleEmbedsBuiltin(this.mod, "node:tls");
1179
- const snapshotsTlsCa = runtimeFeatures.tls ||
1180
- runtimeFeatures.tlsCa ||
1181
- moduleEmbedsBuiltin(this.mod, "node:https") ||
1182
- moduleEmbedsBuiltin(this.mod, "node:tls");
1175
+ const snapshotsTlsCa = runtimeFeatures.tls || runtimeFeatures.tlsCa;
1183
1176
  // The process verdict has the same precedence as the C reference
1184
1177
  // emitter: node:test owns the final status when present; otherwise an
1185
1178
  // embedded process.exitCode owns it; ordinary programs return zero.
1186
1179
  const usesNodeTest = runtimeFeatures.nodeTest;
1187
- const programExitUsesIsland = !usesNodeTest && usesIsland;
1188
1180
  // Declared NOW — the extern block flushes before main assembles.
1189
1181
  if (usesEvents)
1190
1182
  this.declare(`declare void @scr_events_install()`);
@@ -1221,19 +1213,6 @@ export class LlEmitter {
1221
1213
  this.declare(`declare void @scr_http_dyn_install()`);
1222
1214
  if (usesFetch)
1223
1215
  this.declare(`declare void @scr_fetch_install()`);
1224
- if (embedsZlib)
1225
- this.declare(`declare void @scr_zlib_island_install()`);
1226
- if (embedsNet)
1227
- this.declare(`declare void @scr_net_island_install()`);
1228
- if (usesIsland) {
1229
- this.declare(`declare void @scr_island_modules(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
1230
- this.declare(`declare void @scr_island_entry_module(ptr)`);
1231
- this.declare(`declare i32 @scr_island_exit_code()`);
1232
- if (moduleEmbedsCompressedNpm(this.mod)) {
1233
- this.declare(`declare void @scr_island_set_inflate(ptr)`);
1234
- this.declare(`declare zeroext i1 @scr_zlib_inflate_exact(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
1235
- }
1236
- }
1237
1216
  if (usesNodeTest)
1238
1217
  this.declare(`declare i32 @scr_test_exit_code()`);
1239
1218
  if (snapshotsTlsCa) {
@@ -1272,7 +1251,6 @@ export class LlEmitter {
1272
1251
  // fibers as abandoned, so the RC audit downgrades exactly like the
1273
1252
  // async loop-exhaustion story.
1274
1253
  const runsLoop = this.usesTimers ||
1275
- usesIsland ||
1276
1254
  this.ffiHasForeignCallback ||
1277
1255
  this.mod.functions.some((f) => f.async === true || f.generator !== undefined);
1278
1256
  const uncaughtReleases = entryMayThrow && !asyncEntry ? globalReleaseLines("gu") : [];
@@ -1299,9 +1277,6 @@ export class LlEmitter {
1299
1277
  this.declare(`declare void @scr_promise_rethrow_top_level(ptr)`);
1300
1278
  this.declare(`declare void @scr_promise_release(ptr)`);
1301
1279
  this.declare(`declare void @scr_exit_code_note(i32)`);
1302
- if (programExitUsesIsland && inlineExitListeners) {
1303
- this.declare(`declare ${this.sizeType} @scr_island_exit_code_version()`);
1304
- }
1305
1280
  }
1306
1281
  const topPendingExitLines = () => {
1307
1282
  if (!asyncEntry)
@@ -1310,28 +1285,18 @@ export class LlEmitter {
1310
1285
  if (usesNodeTest) {
1311
1286
  lines.push(` %tla_program_exit = call i32 @scr_test_exit_code()`);
1312
1287
  }
1313
- else if (usesIsland) {
1314
- lines.push(` %tla_program_exit = call i32 @scr_island_exit_code()`);
1315
- }
1316
- if (usesNodeTest || usesIsland) {
1288
+ if (usesNodeTest) {
1317
1289
  lines.push(` %tla_program_exit_zero = icmp eq i32 %tla_program_exit, 0`, ` %tla_exit_status = select i1 %tla_program_exit_zero, i32 %tla_status, i32 %tla_program_exit`);
1318
1290
  }
1319
- const exitStatus = usesNodeTest || usesIsland ? "%tla_exit_status" : "%tla_status";
1320
- const tracksIslandExit = programExitUsesIsland && inlineExitListeners;
1321
- if (tracksIslandExit) {
1322
- lines.push(` %tla_exit_version = call ${this.sizeType} @scr_island_exit_code_version()`);
1323
- }
1291
+ const exitStatus = usesNodeTest ? "%tla_exit_status" : "%tla_status";
1324
1292
  // finish_top_level initially notes 13. Replace that hint before exit
1325
1293
  // listeners run when a higher-priority verdict was already selected.
1326
1294
  lines.push(` call void @scr_exit_code_note(i32 ${exitStatus})`);
1327
1295
  for (const line of exitListenerLines("xp"))
1328
1296
  lines.push(line);
1329
- if (tracksIslandExit) {
1330
- lines.push(` %tla_exit_version_after = call ${this.sizeType} @scr_island_exit_code_version()`, ` %tla_exit_changed = icmp ne ${this.sizeType} %tla_exit_version_after, %tla_exit_version`, ` br i1 %tla_exit_changed, label %tla_exit_updated, label %tla_exit_unchanged`, `tla_exit_updated:`, ` %tla_listener_exit = call i32 @scr_island_exit_code()`, ` call void @scr_exit_code_note(i32 %tla_listener_exit)`, ` br label %tla_exit_done`, `tla_exit_unchanged:`, ` br label %tla_exit_done`, `tla_exit_done:`, ` %tla_final_exit = phi i32 [ %tla_listener_exit, %tla_exit_updated ], [ ${exitStatus}, %tla_exit_unchanged ]`);
1331
- }
1332
1297
  for (const line of topPendingReleases)
1333
1298
  lines.push(line);
1334
- lines.push(` ret i32 ${tracksIslandExit ? "%tla_final_exit" : exitStatus}`);
1299
+ lines.push(` ret i32 ${exitStatus}`);
1335
1300
  return lines;
1336
1301
  };
1337
1302
  // LIBRARY mode: the runtime entry points the generated library
@@ -1403,12 +1368,14 @@ export class LlEmitter {
1403
1368
  `%ScrFfiTable = type { ptr, ${this.sizeType}, ${this.sizeType}, ptr, i8, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ptr }`,
1404
1369
  `%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr, ptr }`,
1405
1370
  // ScrArr mirrors scr_runtime.h field-for-field: { rc, len, cap, elem,
1406
- // elem_retain, elem_release, elem_trace, data, present, ext }, where
1407
- // ext points at the out-of-line sparse, property and metadata storage
1408
- // (NULL until first used). Live dynamic stream commits swap its dense,
1409
- // presence and out-of-line storage while preserving the target
1410
- // object's identity.
1411
- `%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, ptr, ptr, ptr, ptr }`,
1371
+ // cyc, ops, data, present, ext } (field numbers in SCR_ARR_FIELD),
1372
+ // where ops points at the element type's constant RC descriptor
1373
+ // (%ScrArrOps, SCR_ELEM_REF only) and ext at the out-of-line sparse,
1374
+ // property and metadata storage (NULL until first used). Live dynamic
1375
+ // stream commits swap its dense, presence and out-of-line storage while
1376
+ // preserving the target object's identity.
1377
+ `%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr }`,
1378
+ `%ScrArrOps = type { ptr, ptr, ptr }`,
1412
1379
  // The ScrMap prefix { rc, key_kind, val_kind, val_retain, val_release,
1413
1380
  // val_trace, key_retain, key_release, key_trace } — the inline RC
1414
1381
  // fast paths test the two trace slots (shapes.ts).
@@ -1416,14 +1383,19 @@ export class LlEmitter {
1416
1383
  ? []
1417
1384
  : [`%ScrMapRc = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr }`]),
1418
1385
  // The complete ScrMap and its { key, val, hash } entry, read by the
1419
- // inline number-key lookup (sc_map_entry_f64): entries is field 12,
1420
- // the direct index and its length fields 18 and 19.
1421
- ...(this.decls.has(NUMBER_MAP_ENTRY_DECL)
1386
+ // inline number-key lookup (sc_map_entry_f64): field 11 is the i64
1387
+ // absent-key filter, entries is field 13, the direct index and its
1388
+ // length fields 19 and 20.
1389
+ ...(this.decls.has(NUMBER_MAP_ENTRY_DECL) || this.decls.has(STRING_MAP_ENTRY_DECL)
1422
1390
  ? [
1423
- `%ScrMapIx = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ptr, ptr, ${this.sizeType} }`,
1424
- `%ScrMapEntry = type { i64, i64, i64 }`,
1391
+ `%ScrMapIx = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr, ${this.sizeType}, ${this.sizeType}, i64, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ptr, ptr, ${this.sizeType} }`,
1425
1392
  ]
1426
1393
  : []),
1394
+ // String-keyed reference reads load the value slot of the entry
1395
+ // scr_map_entry_str returns (map-number-lookup.ts).
1396
+ ...(this.decls.has(NUMBER_MAP_ENTRY_DECL) || this.decls.has(STRING_MAP_ENTRY_DECL)
1397
+ ? [`%ScrMapEntry = type { i64, i64, i64 }`]
1398
+ : []),
1427
1399
  // The runtime error prefix { rc, vt, name, message, code, cause } and the
1428
1400
  // class-object shape { rc, pre, post, ctor, name } — field reads on
1429
1401
  // builtin errors and classval loads GEP through these.
@@ -1551,56 +1523,16 @@ export class LlEmitter {
1551
1523
  // strings — a no-op.
1552
1524
  const n = inst.slots.length;
1553
1525
  const present = n === 0 ? "zeroinitializer" : `[ ${inst.slots.map(() => "i8 1").join(", ")} ]`;
1554
- out.push(`@${inst.sym}_data = internal constant [${n} x ptr] [ ${inst.slots.map((s) => `ptr ${s}`).join(", ")} ]`, `@${inst.sym}_present = internal constant [${n} x i8] ${present}`, `@${inst.sym} = internal global %ScrArr { ${this.sizeType} -1, ${this.sizeType} ${n}, ${this.sizeType} ${n}, i32 2, ptr null, ptr null, ptr null, ptr @${inst.sym}_data, ptr @${inst.sym}_present, ptr null }`);
1526
+ out.push(`@${inst.sym}_data = internal constant [${n} x ptr] [ ${inst.slots.map((s) => `ptr ${s}`).join(", ")} ]`, `@${inst.sym}_present = internal constant [${n} x i8] ${present}`, `@${inst.sym} = internal global %ScrArr { ${this.sizeType} -1, ${this.sizeType} ${n}, ${this.sizeType} ${n}, i32 2, i32 0, ptr null, ptr @${inst.sym}_data, ptr @${inst.sym}_present, ptr null }`);
1555
1527
  }
1556
1528
  if (this.templateStringsInstances.size > 0)
1557
1529
  out.push(``);
1558
- const islandEntryPath = usesIsland ? this.cstr(this.mod.sourceFile) : null;
1559
1530
  for (const [text, c] of this.cstrs) {
1560
1531
  // NUL-terminated byte-array constants for scr_jb_puts and indentation.
1561
1532
  out.push(`@${c.sym} = internal constant [${c.len + 1} x i8] c"${llStrBytes(text)}"`);
1562
1533
  }
1563
1534
  if (this.cstrs.size > 0)
1564
1535
  out.push(``);
1565
- if (usesIsland) {
1566
- const fmt = { esm: 0, cjs: 1, json: 2 };
1567
- const edgeKind = { any: 0, import: 1, require: 2 };
1568
- embedded.modules.forEach((m, i) => {
1569
- const stored = npmStored[i];
1570
- const key = Buffer.from(m.key, "utf8");
1571
- out.push(`@sc_npm_key_${i} = internal constant [${key.length + 1} x i8] c"${llBytes(key)}"`, `@sc_npm_src_${i} = internal constant [${stored.src.bytes.length + 1} x i8] c"${llBytes(stored.src.bytes)}"`);
1572
- if (stored.esm !== null) {
1573
- out.push(`@sc_npm_esm_${i} = internal constant [${stored.esm.bytes.length + 1} x i8] c"${llBytes(stored.esm.bytes)}"`);
1574
- }
1575
- });
1576
- const moduleRows = embedded.modules.map((m, i) => {
1577
- const stored = npmStored[i];
1578
- const esm = stored.esm === null
1579
- ? `ptr null, ${this.sizeType} 0, ${this.sizeType} 0`
1580
- : `ptr @sc_npm_esm_${i}, ${this.sizeType} ${stored.esm.bytes.length}, ${this.sizeType} ${stored.esm.raw}`;
1581
- return (`%ScrIslandModule { ptr @sc_npm_key_${i}, ptr @sc_npm_src_${i}, ` +
1582
- `${this.sizeType} ${stored.src.bytes.length}, ${this.sizeType} ${stored.src.raw}, ` +
1583
- `i32 ${fmt[m.format]}, ${esm} }`);
1584
- });
1585
- out.push(`@sc_npm_modules = internal constant [${moduleRows.length} x %ScrIslandModule] [ ${moduleRows.join(", ")} ]`);
1586
- embedded.edges.forEach((edge, i) => {
1587
- const parts = [
1588
- ["from", edge.from],
1589
- ["spec", edge.specifier],
1590
- ["to", edge.to],
1591
- ];
1592
- for (const [part, text] of parts) {
1593
- const bytes = Buffer.from(text, "utf8");
1594
- out.push(`@sc_npm_edge_${i}_${part} = internal constant [${bytes.length + 1} x i8] c"${llBytes(bytes)}"`);
1595
- }
1596
- });
1597
- if (embedded.edges.length > 0) {
1598
- const edgeRows = embedded.edges.map((edge, i) => `%ScrIslandEdge { ptr @sc_npm_edge_${i}_from, ptr @sc_npm_edge_${i}_spec, ` +
1599
- `ptr @sc_npm_edge_${i}_to, i32 ${edgeKind[edge.kind]} }`);
1600
- out.push(`@sc_npm_edges = internal constant [${edgeRows.length} x %ScrIslandEdge] [ ${edgeRows.join(", ")} ]`);
1601
- }
1602
- out.push(``);
1603
- }
1604
1536
  for (const line of ffiCallbacks.globals)
1605
1537
  out.push(line);
1606
1538
  if (ffiCallbacks.globals.length > 0)
@@ -1714,16 +1646,7 @@ export class LlEmitter {
1714
1646
  : []),
1715
1647
  // fs.watch programs fill the loop's watch hooks the same way —
1716
1648
  // scr_watch.c links only when this line is emitted.
1717
- ...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(this.ffiHasForeignCallback ? [` call void @scr_ffi_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(embedsZlib ? [` call void @scr_zlib_island_install()`] : []), ...(embedsNet ? [` call void @scr_net_island_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesDgram ? [` call void @scr_dgram_install()`] : []), ...(usesHttp2 ? [` call void @scr_http2_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(usesIsland
1718
- ? [
1719
- ` call void @scr_island_entry_module(ptr ${islandEntryPath})`,
1720
- ...(moduleEmbedsCompressedNpm(this.mod)
1721
- ? [` call void @scr_island_set_inflate(ptr @scr_zlib_inflate_exact)`]
1722
- : []),
1723
- ` call void @scr_island_modules(ptr @sc_npm_modules, ${this.sizeType} ${embedded.modules.length}, ` +
1724
- `ptr ${embedded.edges.length > 0 ? "@sc_npm_edges" : "null"}, ${this.sizeType} ${embedded.edges.length})`,
1725
- ]
1726
- : []), ...(asyncEntry
1649
+ ...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(this.ffiHasForeignCallback ? [` call void @scr_ffi_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesDgram ? [` call void @scr_dgram_install()`] : []), ...(usesHttp2 ? [` call void @scr_http2_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(asyncEntry
1727
1650
  ? [` %top = call ptr @${mangleAsyncSpawn(this.mod.entry)}()`]
1728
1651
  : [` call void @${mangleFunction(this.mod.entry)}()`]),
1729
1652
  // Uncaught exception from top-level code: Node exits 1.
@@ -1810,9 +1733,7 @@ export class LlEmitter {
1810
1733
  ]
1811
1734
  : []), ...exitListenerLines("xn"), ...globalReleases, ...(usesNodeTest
1812
1735
  ? [` %test_exit = call i32 @scr_test_exit_code()`, ` ret i32 %test_exit`]
1813
- : usesIsland
1814
- ? [` %island_exit = call i32 @scr_island_exit_code()`, ` ret i32 %island_exit`]
1815
- : [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``, ...this.inlineRcTail(flushedDecls),
1736
+ : [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``, ...this.inlineRcTail(flushedDecls),
1816
1737
  // sanitize_address is inert under the plain pipeline; the sanitized
1817
1738
  // lane's -fsanitize=address link activates instrumentation over the
1818
1739
  // emitted functions too (the runtime TUs get theirs from clang).
@@ -2078,11 +1999,44 @@ export class LlEmitter {
2078
1999
  const sz = this.sizeType;
2079
2000
  let mapTmp = 0;
2080
2001
  const mapIndex = exactIndexLines("%k", "%nd", sz, () => `%ix${mapTmp++}`);
2081
- defs.push(`define internal ptr @sc_map_entry_f64(ptr %m, double %k) ${FN_ATTRS} {`, `entry:`, ` %ndp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 19`, ` %nd = load ${sz}, ptr %ndp`,
2002
+ defs.push(`define internal ptr @sc_map_entry_f64(ptr %m, double %k) ${FN_ATTRS} {`, `entry:`, ` %ndp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 20`, ` %nd = load ${sz}, ptr %ndp`,
2082
2003
  // A non-integral key inside the table is as absent as one outside it.
2083
2004
  ...mapIndex.lines.map((line) => ` ${line}`), ` br i1 ${mapIndex.ok}, label %slot, label %outside`, `slot:`, sz === "i64"
2084
2005
  ? ` %i = add i64 ${mapIndex.wide}, 0`
2085
- : ` %i = trunc i64 ${mapIndex.wide} to ${sz}`, ` %dp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 18`, ` %dense = load ptr, ptr %dp`, ` %sp = getelementptr inbounds i32, ptr %dense, ${sz} %i`, ` %s = load i32, ptr %sp`, ` %empty = icmp eq i32 %s, 0`, ` br i1 %empty, label %miss, label %hit`, `hit:`, ` %e32 = sub i32 %s, 1`, sz === "i32" ? ` %e = add i32 %e32, 0` : ` %e = zext i32 %e32 to ${sz}`, ` %ep = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 12`, ` %entries = load ptr, ptr %ep`, ` %found = getelementptr inbounds %ScrMapEntry, ptr %entries, ${sz} %e`, ` ret ptr %found`, `miss:`, ` ret ptr null`, `outside:`, ` %dense.any = icmp ne ${sz} %nd, 0`, ` br i1 %dense.any, label %miss, label %slow`, `slow:`, ` %r = call ptr @scr_map_entry_f64(ptr %m, double %k)`, ` ret ptr %r`, `}`, ``);
2006
+ : ` %i = trunc i64 ${mapIndex.wide} to ${sz}`, ` %dp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 19`, ` %dense = load ptr, ptr %dp`, ` %sp = getelementptr inbounds i32, ptr %dense, ${sz} %i`, ` %s = load i32, ptr %sp`, ` %empty = icmp eq i32 %s, 0`, ` br i1 %empty, label %miss, label %hit`, `hit:`, ` %e32 = sub i32 %s, 1`, sz === "i32" ? ` %e = add i32 %e32, 0` : ` %e = zext i32 %e32 to ${sz}`, ` %ep = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 13`, ` %entries = load ptr, ptr %ep`, ` %found = getelementptr inbounds %ScrMapEntry, ptr %entries, ${sz} %e`, ` ret ptr %found`, `miss:`, ` ret ptr null`, `outside:`, ` %dense.any = icmp ne ${sz} %nd, 0`, ` br i1 %dense.any, label %miss, label %slow`, `slow:`, ` %r = call ptr @scr_map_entry_f64(ptr %m, double %k)`, ` ret ptr %r`, `}`, ``);
2007
+ }
2008
+ // String-key Map/Set lookups test the map's absent-key filter inline
2009
+ // (scr_map.c scr_map_filter_bits): a key whose two filter bits are not
2010
+ // both set is absent, so most misses on small and medium maps return
2011
+ // without a call. On 64-bit targets the key's cached hash h is the high
2012
+ // half of its capacity word, the runtime's widened hash is (h << 32) | h,
2013
+ // and the filter reads its bits 44..55, which are the capacity word's
2014
+ // own. A key without a cached hash, and every possible hit, take the
2015
+ // runtime lookup.
2016
+ if (this.decls.has(STRING_MAP_ENTRY_DECL)) {
2017
+ defs.push(`define internal ptr @sc_map_entry_str(ptr %m, ptr %k) ${FN_ATTRS} {`, `entry:`, ...(this.sizeType === "i64"
2018
+ ? [
2019
+ ` %fp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 11`,
2020
+ ` %filter = load i64, ptr %fp`,
2021
+ ` %capp = getelementptr inbounds %ScrStr, ptr %k, i32 0, i32 2`,
2022
+ ` %cap = load i64, ptr %capp`,
2023
+ ` %cached = icmp uge i64 %cap, 4294967296`,
2024
+ ` br i1 %cached, label %test, label %slow`,
2025
+ `test:`,
2026
+ ` %s1 = lshr i64 %cap, 44`,
2027
+ ` %i1 = and i64 %s1, 63`,
2028
+ ` %b1 = shl i64 1, %i1`,
2029
+ ` %s2 = lshr i64 %cap, 50`,
2030
+ ` %i2 = and i64 %s2, 63`,
2031
+ ` %b2 = shl i64 1, %i2`,
2032
+ ` %bits = or i64 %b1, %b2`,
2033
+ ` %have = and i64 %filter, %bits`,
2034
+ ` %maybe = icmp eq i64 %have, %bits`,
2035
+ ` br i1 %maybe, label %slow, label %absent`,
2036
+ `absent:`,
2037
+ ` ret ptr null`,
2038
+ ]
2039
+ : [` br label %slow`]), `slow:`, ` %r = call ptr @scr_map_entry_str(ptr %m, ptr %k)`, ` ret ptr %r`, `}`, ``);
2086
2040
  }
2087
2041
  // String comparisons enter through small internal wrappers that LLVM
2088
2042
  // inlines at each site: interned literals and shared values compare by
@@ -2261,8 +2215,8 @@ export class LlEmitter {
2261
2215
  * mentions (transitively), contains no suspension entry point. Such a
2262
2216
  * body always completes before the call returns, which is exactly the
2263
2217
  * behavior of a fiber that never suspends. Excluded conservatively:
2264
- * wasm32 (its coroutine lowering owns async bodies), programs embedding
2265
- * the dynamic engine, module-evaluation bodies (their promise caches),
2218
+ * wasm32 (its coroutine lowering owns async bodies), module-evaluation
2219
+ * bodies (their promise caches),
2266
2220
  * `dyn` results (resolution may adopt a thenable), and every program
2267
2221
  * that uses node:test subtests — `t.test()` runs a subtest inline and
2268
2222
  * awaits it on the CURRENT fiber even from synchronous code. Worker
@@ -2275,9 +2229,6 @@ export class LlEmitter {
2275
2229
  const eligible = new Set();
2276
2230
  if (this.wasi || this.mod.workers === true)
2277
2231
  return eligible;
2278
- const embedded = this.mod.embedded;
2279
- if (embedded !== undefined && embedded.modules.length > 0)
2280
- return eligible;
2281
2232
  const suspends = /@scr_(?:await_|module_await\b|promise_await_settled\b|test_sub\b|gen_yield_|async_gen_|fiber_)/;
2282
2233
  for (const decl of this.decls)
2283
2234
  if (/@scr_test_sub\b/.test(decl))
@@ -3280,15 +3231,6 @@ export class LlEmitter {
3280
3231
  this.declare(`declare void @scr_dyn_throw(ptr)`);
3281
3232
  B.line(`call void @scr_dyn_throw(ptr ${v.name})`);
3282
3233
  }
3283
- else if (t.kind === "jsval") {
3284
- const rc = vAdapters(this.shapeHost, t);
3285
- const test = "scr_jsval_is_object";
3286
- this.declare(`declare zeroext i1 @${test}(ptr)`);
3287
- this.declare(`declare void @scr_throw_ref_classified(ptr, ptr, ptr, ptr, i1 zeroext)`);
3288
- const object = B.tmp();
3289
- B.line(`${object} = call zeroext i1 @${test}(ptr ${v.name})`);
3290
- B.line(`call void @scr_throw_ref_classified(ptr ${v.name}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(this.shapeHost, t)}, i1 zeroext ${object})`);
3291
- }
3292
3234
  else {
3293
3235
  const rc = vAdapters(this.shapeHost, t);
3294
3236
  this.declare(`declare void @scr_throw_ref(ptr, ptr, ptr, ptr)`);
@@ -3326,7 +3268,7 @@ export class LlEmitter {
3326
3268
  else if (arm.kind === "string") {
3327
3269
  valueName = this.unionPeek(v.name, unionId);
3328
3270
  }
3329
- else if (arm.kind === "bigint" || arm.kind === "dyn" || arm.kind === "jsval") {
3271
+ else if (arm.kind === "bigint" || arm.kind === "dyn") {
3330
3272
  valueName = this.unionPeek(v.name, unionId);
3331
3273
  }
3332
3274
  const truthy = this.truthyOf(arm.kind, valueName, true);
@@ -3420,14 +3362,6 @@ export class LlEmitter {
3420
3362
  B.line(`${truthy} = call zeroext i1 @scr_dyn_truthy(ptr ${valueName})`);
3421
3363
  return truthy;
3422
3364
  }
3423
- case "jsval": {
3424
- this.declare(`declare i32 @scr_jsval_truthy(ptr)`);
3425
- const raw = B.tmp();
3426
- const truthy = B.tmp();
3427
- B.line(`${raw} = call i32 @scr_jsval_truthy(ptr ${valueName})`);
3428
- B.line(`${truthy} = icmp ne i32 ${raw}, 0`);
3429
- return truthy;
3430
- }
3431
3365
  default:
3432
3366
  throw new LlvmUnsupportedError(`truthy:${unionArm ? "union:" : ""}${kind}`);
3433
3367
  }
@@ -4086,6 +4020,7 @@ export class LlEmitter {
4086
4020
  const numericFn = withInitializerBindings(fn, initializerBindings);
4087
4021
  this.numericLocals = new Map(numericFn.locals.map((l) => [l.id, l]));
4088
4022
  this.borrowedParameters.clear();
4023
+ this.referenceWrites?.enter(fn);
4089
4024
  this.reboundOwnerSlots.clear();
4090
4025
  this.projectedParameters.clear();
4091
4026
  const projectedParameterIndexes = this.callLifetimes.parameters.get(fn.name);
@@ -4117,7 +4052,7 @@ export class LlEmitter {
4117
4052
  this.integerArrayBindings.clear();
4118
4053
  this.loopArrayBorrows =
4119
4054
  this.debug === null
4120
- ? findLoopArrayBorrows(fn, this.callLifetimes, this.referenceEffects, this.optionalArrayReads)
4055
+ ? findLoopArrayBorrows(fn, this.callLifetimes, this.referenceEffects, this.optionalArrayReads, this.referenceWrites)
4121
4056
  : new Set();
4122
4057
  this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.referenceEffects.functions, this.callLifetimes, this.optionalArrayReads, this.loopArrayBorrows, this.debug === null ? this.referenceEffects : undefined);
4123
4058
  this.callArrayReads = findCallArrayReads(fn, this.optionalArrayReads, this.referenceEffects.functions, this.callLifetimes, this.debug === null ? this.referenceEffects : undefined);
@@ -4137,6 +4072,7 @@ export class LlEmitter {
4137
4072
  this.currentReturnType = fn.returnType;
4138
4073
  this.currentGenerator = fn.generator ?? null;
4139
4074
  this.currentBorrowedReturn = this.borrowedReturns.has(fn.name);
4075
+ this.inConstructor = this.constructorFunctions.has(fn.name);
4140
4076
  this.currentWasiCoro = null;
4141
4077
  this.logArgSlots = 0;
4142
4078
  if (this.wasi && (fn.async === true || fn.generator !== undefined)) {
@@ -4297,7 +4233,7 @@ export class LlEmitter {
4297
4233
  this.emitStackCheck();
4298
4234
  if (this.workerEntryPolls?.has(fn.name))
4299
4235
  this.emitPendingCheck(undefined, true);
4300
- this.emitStmts(fn.body);
4236
+ this.emitStmts(fn.body, true);
4301
4237
  // Implicit exit of a void function: release the function scope unless
4302
4238
  // the body already terminated its final block (return, or a throw
4303
4239
  // whose unwind released everything down to depth 0).
@@ -4363,7 +4299,14 @@ export class LlEmitter {
4363
4299
  if (fn.captures !== undefined)
4364
4300
  params.unshift("ptr %sc_env");
4365
4301
  const ret = this.llType(fn.returnType);
4366
- const attrs = coro !== null ? "#1" : FN_ATTRS;
4302
+ let attrs = coro !== null ? "#1" : FN_ATTRS;
4303
+ // The shared indexed-read helper (lowerSafeIndexRead) is a dense bounds
4304
+ // test, a state test and a load behind cold runtime fallbacks, called
4305
+ // from every `a[i]` of one element type. Without the hint LLVM keeps the
4306
+ // hot instances out of line once they have many callers: every read pays
4307
+ // a call, and an integer index converts to a double and back across it.
4308
+ if (fn.name.startsWith("%arr.idxOr."))
4309
+ attrs = `inlinehint ${attrs}`;
4367
4310
  const debug = this.debugScope === null ? "" : ` !dbg ${this.debugScope}`;
4368
4311
  const symbol = borrowed ? mangleBorrowedFunction(fn.name) : mangleFunction(fn.name);
4369
4312
  const body = `define internal ${ret} @${symbol}(${params.join(", ")}) ${attrs}${debug} { ; ${fn.name}\n${B.render()}\n}`;
@@ -4460,6 +4403,10 @@ export class LlEmitter {
4460
4403
  this.canBorrowReceiver(value.else_) &&
4461
4404
  this.canBorrowCallArgument(value.then) &&
4462
4405
  this.canBorrowCallArgument(value.else_));
4406
+ // A construction-only field of a stable owner keeps its value until the
4407
+ // owner dies: no later operand or callee can replace and release it.
4408
+ if ((value.kind === "fieldGet" || value.kind === "recordGet") && this.immutableRead(value))
4409
+ return this.canBorrowCallArgument(value.obj);
4463
4410
  // An optional chain's bound receiver stays unchanged until its body
4464
4411
  // finishes (see emitReadReceiver).
4465
4412
  if (value.kind === "chainRecv")
@@ -4467,6 +4414,9 @@ export class LlEmitter {
4467
4414
  if (value.kind !== "varRef")
4468
4415
  return false;
4469
4416
  const binding = this.binding(value.localId);
4417
+ // A module constant owns its value for the rest of the program.
4418
+ if (binding.kind === "global")
4419
+ return this.stableGlobals.has(value.localId);
4470
4420
  return (binding.kind === "local" &&
4471
4421
  binding.local !== undefined &&
4472
4422
  (!binding.local.mutable ||
@@ -4476,15 +4426,212 @@ export class LlEmitter {
4476
4426
  !binding.local.boxed &&
4477
4427
  !binding.local.tdz);
4478
4428
  }
4429
+ /** A plain-pointer read of a construction-only field outside any
4430
+ * constructor (ImmutableFields). */
4431
+ immutableRead(e) {
4432
+ if (this.inConstructor || this.immutableFields === null)
4433
+ return false;
4434
+ const t = e.type.kind;
4435
+ if (t !== "object" && t !== "record" && t !== "array" && t !== "string")
4436
+ return false;
4437
+ return e.kind === "fieldGet"
4438
+ ? this.immutableFields.classField(e.className, e.field)
4439
+ : this.immutableFields.recordField(e.shapeId, e.field);
4440
+ }
4441
+ /** A projection read whose locations nothing writes while a borrowed
4442
+ * read of it is in use (ReferenceWrites), given the writes of that
4443
+ * extent. The root binding itself must stay alive across it too, which
4444
+ * its own location (`l:`, `g:`, `bx`) covers. */
4445
+ unwrittenRead(e, writes) {
4446
+ const rw = this.referenceWrites;
4447
+ if (rw === null)
4448
+ return false;
4449
+ const keys = rw.readKeys(e, (call) => this.borrowedReturnKeys(call.callee));
4450
+ return keys !== null && !rw.conflicts(keys, writes);
4451
+ }
4452
+ /** A stable binding initialized by a projection can borrow it when the
4453
+ * rest of its block (its whole lifetime) writes none of the projection's
4454
+ * locations: the source edge then keeps the value alive, and uses that
4455
+ * escape still retain their own copies. */
4456
+ unwrittenAlias(s) {
4457
+ const cursor = this.stmtCursor;
4458
+ const rw = this.referenceWrites;
4459
+ if (rw === null || s.init === null || cursor === null || cursor.stmts[cursor.index] !== s)
4460
+ return false;
4461
+ if (!this.plainReference(s.init.type) ||
4462
+ !this.canBorrowReceiver(s.init) ||
4463
+ !this.unownedWalk(s.init))
4464
+ return false;
4465
+ const writes = rw.union([rw.expr(s.init), rw.stmts(cursor.stmts.slice(cursor.index + 1))]);
4466
+ return this.unwrittenRead(s.init, writes);
4467
+ }
4468
+ /** An optional element read into a projection-only local can borrow the
4469
+ * element when the rest of the block writes no array element and none of
4470
+ * the locations keeping the array alive (which must itself be read
4471
+ * without a frame owner). */
4472
+ unwrittenLocalArrayRead(s, read) {
4473
+ const cursor = this.stmtCursor;
4474
+ const rw = this.referenceWrites;
4475
+ if (rw === null || s.init === null || cursor === null || cursor.stmts[cursor.index] !== s)
4476
+ return false;
4477
+ if (!this.unownedWalk(read.array))
4478
+ return false;
4479
+ const keys = rw.readKeys(read.array);
4480
+ if (keys === null || rw.expr(read.index) !== null)
4481
+ return false;
4482
+ const writes = rw.union([rw.expr(s.init), rw.stmts(cursor.stmts.slice(cursor.index + 1))]);
4483
+ return !rw.conflicts(["a", ...keys], writes);
4484
+ }
4485
+ /** An optional element read passed to a projection-only parameter can
4486
+ * borrow the element when the callee and later arguments (`writes`)
4487
+ * write no array element and nothing keeping the array alive. */
4488
+ unwrittenCallArrayRead(read, writes) {
4489
+ const rw = this.referenceWrites;
4490
+ if (rw === null)
4491
+ return false;
4492
+ const keys = rw.readKeys(read.array);
4493
+ if (keys === null || rw.expr(read.index) !== null)
4494
+ return false;
4495
+ return !rw.conflicts(["a", ...keys], writes);
4496
+ }
4497
+ /** `return x` of a local whose slot is an owned scope entry of the same
4498
+ * type: the return can take over the local's reference. */
4499
+ movableReturn(value) {
4500
+ if (value === null || value.kind !== "varRef" || !isRefCounted(value.type))
4501
+ return null;
4502
+ if (this.currentBorrowedReturn || this.currentWasiCoro !== null)
4503
+ return null;
4504
+ const binding = this.binding(value.localId);
4505
+ if (binding.kind !== "local" || binding.local === undefined || binding.local.boxed)
4506
+ return null;
4507
+ if (!typeEquals(binding.type, value.type) || !typeEquals(this.currentReturnType, value.type))
4508
+ return null;
4509
+ for (const scope of this.scopes) {
4510
+ const index = scope.findIndex((entry) => entry.slot === binding.slot);
4511
+ if (index < 0)
4512
+ continue;
4513
+ const entry = scope[index];
4514
+ if (entry.boxed || entry.lazy || !typeEquals(entry.type, value.type))
4515
+ return null;
4516
+ return { scope, entry, index };
4517
+ }
4518
+ return null;
4519
+ }
4520
+ /** A walk host for a body that may write the heap: each walk must read
4521
+ * only locations the whole body (with its callees) never writes;
4522
+ * rebinding the walk locals themselves releases nothing. */
4523
+ unwrittenWalkHost(fn, host) {
4524
+ return {
4525
+ ...host,
4526
+ unwritten: (value, candidates) => this.referenceWrites?.unwrittenWalk(fn, value, candidates, (call) => this.borrowedReturnKeys(call.callee)) ?? false,
4527
+ };
4528
+ }
4529
+ /** emitReadReceiver yields a plain pointer for `e` that no statement
4530
+ * frame owns, so it can outlive the statement (walk-borrows' walks, from
4531
+ * any unboxed plain-pointer binding, plus element reads). */
4532
+ unownedWalk(e) {
4533
+ switch (e.kind) {
4534
+ case "varRef": {
4535
+ const binding = this.binding(e.localId);
4536
+ if (binding.kind === "global")
4537
+ return this.plainReference(binding.type);
4538
+ return (binding.kind === "local" &&
4539
+ binding.local !== undefined &&
4540
+ !binding.local.boxed &&
4541
+ !binding.local.tdz &&
4542
+ this.plainReference(binding.local.type) &&
4543
+ !this.stringSlices.has(e.localId) &&
4544
+ !this.storedSplits.has(e.localId) &&
4545
+ !this.localUnionStorage.has(e.localId));
4546
+ }
4547
+ case "fieldGet":
4548
+ case "recordGet":
4549
+ return this.borrowsWithoutOwning(e) && this.unownedWalk(e.obj);
4550
+ case "unionNarrow":
4551
+ case "downcast":
4552
+ case "upcast":
4553
+ return this.borrowsWithoutOwning(e) && this.unownedWalk(e.value);
4554
+ case "unionWrap":
4555
+ if (!this.borrowsWithoutOwning(e))
4556
+ return false;
4557
+ return e.value.kind === "unitLit" || this.unownedWalk(e.value);
4558
+ case "ternary": {
4559
+ const arm = (v) => (v.kind === "libCall" && v.fn === "error.nodeThrow") || this.unownedWalk(v);
4560
+ return this.borrowsWithoutOwning(e) && arm(e.then) && arm(e.else_);
4561
+ }
4562
+ case "call":
4563
+ return (this.borrowsWithoutOwning(e) &&
4564
+ e.args.every((arg) => !isRefCounted(arg.type) || this.unownedWalk(arg)));
4565
+ case "arrayGet":
4566
+ return (!this.isStackUnionSource(e) &&
4567
+ this.optionalArrayReads.get(e) === null &&
4568
+ this.unownedWalk(e.arr));
4569
+ case "seqExpr": {
4570
+ const guarded = guardedValue(e);
4571
+ return guarded !== null && this.unownedWalk(guarded);
4572
+ }
4573
+ default:
4574
+ return false;
4575
+ }
4576
+ }
4577
+ /** Every location a borrowed-return callee's walks can read (its body's
4578
+ * field, record and element reads, through nested borrowed returns). */
4579
+ borrowedReturnKeys(callee) {
4580
+ if (!this.borrowedReturns.has(callee))
4581
+ return null;
4582
+ const known = this.borrowedReturnKeyCache.get(callee);
4583
+ if (known)
4584
+ return known;
4585
+ const rw = this.referenceWrites;
4586
+ const keys = new Set();
4587
+ const seen = new Set([callee]);
4588
+ const pending = [callee];
4589
+ while (pending.length > 0) {
4590
+ const fn = this.fnByName.get(pending.pop());
4591
+ if (!fn)
4592
+ continue;
4593
+ everyStmtList(fn.body, {
4594
+ stmt: () => true,
4595
+ expr: (e) => {
4596
+ if (e.kind === "fieldGet")
4597
+ keys.add(rw.fieldKey(e.className, e.field));
4598
+ else if (e.kind === "recordGet") {
4599
+ keys.add(`r:${e.field}`);
4600
+ keys.add("r:*");
4601
+ }
4602
+ else if (e.kind === "arrayGet")
4603
+ keys.add("a");
4604
+ else if (e.kind === "call" && this.borrowedReturns.has(e.callee) && !seen.has(e.callee)) {
4605
+ seen.add(e.callee);
4606
+ pending.push(e.callee);
4607
+ }
4608
+ return true;
4609
+ },
4610
+ });
4611
+ }
4612
+ const result = [...keys];
4613
+ this.borrowedReturnKeyCache.set(callee, result);
4614
+ return result;
4615
+ }
4479
4616
  // ── statements ──────────────────────────────────────────────────────────
4480
- emitStmts(stmts) {
4481
- for (const s of stmts) {
4482
- // Statements after a terminator are unreachable (dead code after
4483
- // return/break/continue). Skip them so no dropped SSA definition
4484
- // can leak forward.
4485
- if (this.B.isTerminated())
4486
- return;
4487
- this.emitStmt(s);
4617
+ emitStmts(stmts, scoped = false) {
4618
+ const saved = this.stmtCursor;
4619
+ const cursor = scoped ? { stmts, index: 0 } : null;
4620
+ try {
4621
+ for (let i = 0; i < stmts.length; i++) {
4622
+ // Statements after a terminator are unreachable (dead code after
4623
+ // return/break/continue). Skip them so no dropped SSA definition
4624
+ // can leak forward.
4625
+ if (this.B.isTerminated())
4626
+ return;
4627
+ if (cursor)
4628
+ cursor.index = i;
4629
+ this.stmtCursor = cursor;
4630
+ this.emitStmt(stmts[i]);
4631
+ }
4632
+ }
4633
+ finally {
4634
+ this.stmtCursor = saved;
4488
4635
  }
4489
4636
  }
4490
4637
  /** Emits a block in its own lexical scope (refcounted locals released at
@@ -4496,7 +4643,7 @@ export class LlEmitter {
4496
4643
  const scope = [];
4497
4644
  this.scopes.push(scope);
4498
4645
  setup?.(scope);
4499
- this.emitStmts(stmts);
4646
+ this.emitStmts(stmts, true);
4500
4647
  const ended = endsWithJump(stmts);
4501
4648
  this.scopes.pop();
4502
4649
  if (!ended)
@@ -4558,7 +4705,9 @@ export class LlEmitter {
4558
4705
  }
4559
4706
  const localRead = this.localArrayReads.get(s.localId);
4560
4707
  if (localRead) {
4561
- const owner = emitLocalArrayRead(this, localRead, b.slot);
4708
+ const owner = emitLocalArrayRead(this, !localRead.borrow && this.unwrittenLocalArrayRead(s, localRead)
4709
+ ? { ...localRead, borrow: true }
4710
+ : localRead, b.slot);
4562
4711
  if (owner)
4563
4712
  this.scopes[this.scopes.length - 1].push(owner);
4564
4713
  break;
@@ -4652,7 +4801,7 @@ export class LlEmitter {
4652
4801
  b.local &&
4653
4802
  isRefCounted(b.type) &&
4654
4803
  this.stableCallBindings.has(s.localId) &&
4655
- this.canBorrowCallArgument(s.init)) {
4804
+ (this.canBorrowCallArgument(s.init) || this.unwrittenAlias(s))) {
4656
4805
  const v = this.emitReadReceiver(s.init);
4657
4806
  B.line(`store ptr ${v.name}, ptr ${b.slot}`);
4658
4807
  break;
@@ -4719,6 +4868,32 @@ export class LlEmitter {
4719
4868
  }
4720
4869
  B.line(`store ${this.llType(b.type)} ${v.name}, ptr ${b.slot}`);
4721
4870
  this.storeIntegerView(s.localId, v, s.value);
4871
+ if (b.kind === "global" && this.immortalGlobals.has(s.localId)) {
4872
+ // An Immutable module constant (contracts/check.ts proved it is
4873
+ // fresh, publishable and never written): its graph becomes
4874
+ // immortal, so reads skip reference counting and the collector
4875
+ // never visits it.
4876
+ const lines = this.publishWalkers.emitPublish(v.name, v.type, s.loc);
4877
+ for (const line of lines)
4878
+ B.line(line.trimStart());
4879
+ // Reads of this global skip retains from here on, which is only
4880
+ // sound once the graph really is immortal. Node.js never fails
4881
+ // here, so a refusal means the contract proof was wrong: stop
4882
+ // rather than keep running with a mortal graph read as immortal.
4883
+ if (lines.length > 0) {
4884
+ this.declare(`declare zeroext i1 @scr_exc_pending()`);
4885
+ this.declare(`declare void @scr_trap(ptr)`);
4886
+ const refused = B.tmp();
4887
+ B.line(`${refused} = call zeroext i1 @scr_exc_pending()`);
4888
+ const stop = B.newLabel("immutable.refused");
4889
+ const done = B.newLabel("immutable.published");
4890
+ B.condBr(refused, stop, done);
4891
+ B.startBlock(stop);
4892
+ B.line(`call void @scr_trap(ptr ${this.cstr("scriptc: internal error: an Immutable value could not be published\n")})`);
4893
+ B.terminate("unreachable");
4894
+ B.startBlock(done);
4895
+ }
4896
+ }
4722
4897
  break;
4723
4898
  }
4724
4899
  case "exprStmt":
@@ -4816,14 +4991,14 @@ export class LlEmitter {
4816
4991
  const obj = this.emitStableReceiver(s.obj, [s.value]);
4817
4992
  // (The guard precedes the value here: the nullable store
4818
4993
  // evaluates it itself.)
4819
- this.emitPublishedFieldGuard(obj.name, s.className, s.field);
4994
+ this.emitStoreGuard(obj.name, s);
4820
4995
  this.emitNullableFieldStore(obj.name, s.className, s.field, nullable, s.value);
4821
4996
  break;
4822
4997
  }
4823
4998
  if (s.kind === "fieldSet" && this.int32Slots.isField(s.className, s.field)) {
4824
4999
  const obj = this.emitStableReceiver(s.obj, [s.value]);
4825
5000
  const v = this.emitExpr(s.value);
4826
- this.emitPublishedFieldGuard(obj.name, s.className, s.field);
5001
+ this.emitStoreGuard(obj.name, s);
4827
5002
  const { ptr } = this.classFieldPtr(obj.name, s.className, s.field);
4828
5003
  this.storeInt32Field(ptr, v, s.value);
4829
5004
  break;
@@ -4831,7 +5006,7 @@ export class LlEmitter {
4831
5006
  const obj = this.emitStableReceiver(s.obj, [s.value]);
4832
5007
  const v = this.emitExpr(s.value);
4833
5008
  if (s.kind === "fieldSet")
4834
- this.emitPublishedFieldGuard(obj.name, s.className, s.field);
5009
+ this.emitStoreGuard(obj.name, s);
4835
5010
  else if (this.publishedTypes?.recordGuarded(s.shapeId) === true)
4836
5011
  this.emitPublishedFieldGuard(obj.name, null, s.field);
4837
5012
  const { ptr, type } = s.kind === "fieldSet"
@@ -5194,7 +5369,11 @@ export class LlEmitter {
5194
5369
  if (integerArrayLoop && s.init?.kind === "varDecl" && s.init.init) {
5195
5370
  const value = this.emitExpr(s.init.init);
5196
5371
  start = B.tmp();
5197
- B.line(`${start} = fptoui double ${value.name} to ${this.sizeType}`);
5372
+ // The start is 0 or an enclosing array index plus one (at most
5373
+ // 2^32 - 1): on 64-bit targets the signed conversion is exact
5374
+ // and avoids x86-64's unsigned 64-bit conversion sequence.
5375
+ const conversion = this.sizeType === "i64" ? "fptosi" : "fptoui";
5376
+ B.line(`${start} = ${conversion} double ${value.name} to ${this.sizeType}`);
5198
5377
  this.integerArrayBindings.add(integerLoop.localId);
5199
5378
  }
5200
5379
  B.line(`store ${this.sizeType} ${start}, ptr ${integerSlot}`);
@@ -5500,10 +5679,32 @@ export class LlEmitter {
5500
5679
  // the actual ret. The parked value owns a synthetic slot-backed
5501
5680
  // scope entry during each copy so a throwing finally releases it.
5502
5681
  let v = null;
5503
- if (s.value !== null && this.currentBorrowedReturn) {
5682
+ const moved = this.finallyStack.length === 0 ? this.movableReturn(s.value) : null;
5683
+ if (moved !== null) {
5684
+ // Returning an owned local hands its reference to the caller: no
5685
+ // retain now and no release of the local below.
5686
+ const t = B.tmp();
5687
+ B.line(`${t} = load ptr, ptr ${moved.entry.slot}`);
5688
+ v = { name: t, type: s.value.type };
5689
+ moved.scope.splice(moved.scope.indexOf(moved.entry), 1);
5690
+ this.releaseForJump(0, 0);
5691
+ moved.scope.splice(moved.index, 0, moved.entry);
5692
+ }
5693
+ else if (s.value !== null && this.currentBorrowedReturn) {
5504
5694
  // A borrowed-return body hands back its walk without a reference
5505
- // (it has no try statements, so no finally can intervene).
5506
- v = this.emitReadReceiver(s.value);
5695
+ // (it has no try statements, so no finally can intervene). An
5696
+ // optional element read lends the element its array keeps alive.
5697
+ const read = this.optionalArrayReads.get(s.value);
5698
+ if (read) {
5699
+ const slot = B.slot();
5700
+ B.entryAllocas.push(`${slot} = alloca ptr`);
5701
+ emitLocalArrayRead(this, { ...read, borrow: true }, slot);
5702
+ const t = B.tmp();
5703
+ B.line(`${t} = load ptr, ptr ${slot}`);
5704
+ v = { name: t, type: s.value.type };
5705
+ }
5706
+ else
5707
+ v = this.emitReadReceiver(s.value);
5507
5708
  }
5508
5709
  else if (s.value !== null) {
5509
5710
  v = this.emitExpr(s.value);
@@ -5540,7 +5741,7 @@ export class LlEmitter {
5540
5741
  this.finallyStack = savedFinally;
5541
5742
  this.tryStack = savedTry;
5542
5743
  }
5543
- else {
5744
+ else if (moved === null) {
5544
5745
  this.releaseForJump(0, 0);
5545
5746
  }
5546
5747
  if (!B.isTerminated()) {
@@ -5798,7 +5999,7 @@ export class LlEmitter {
5798
5999
  const caseLabels = s.cases.map(() => B.newLabel("sw.c"));
5799
6000
  const defaultIdx = s.cases.findIndex((c) => c.test === null);
5800
6001
  const fallback = defaultIdx >= 0 ? caseLabels[defaultIdx] : end;
5801
- if (!emitLiteralSwitch(this, disc, s.cases, caseLabels, fallback)) {
6002
+ if (!emitLiteralSwitch(this, disc, s.disc, s.cases, caseLabels, fallback)) {
5802
6003
  s.cases.forEach((c, i) => {
5803
6004
  if (c.test === null)
5804
6005
  return;
@@ -6134,6 +6335,13 @@ export class LlEmitter {
6134
6335
  ``,
6135
6336
  ];
6136
6337
  }
6338
+ /** The frozen-object guard of a class field store, unless the store
6339
+ * provably targets a still-private `this` (PublishedTypes.privateStores). */
6340
+ emitStoreGuard(obj, s) {
6341
+ if (this.publishedTypes?.privateStores.has(s) === true)
6342
+ return;
6343
+ this.emitPublishedFieldGuard(obj, s.className, s.field);
6344
+ }
6137
6345
  /** The frozen-object guard of a class field or record field store:
6138
6346
  * Node's "Cannot assign to read only property" TypeError when the target
6139
6347
  * is published. `className` null means a record (`#<Object>`). */
@@ -6200,9 +6408,6 @@ export class LlEmitter {
6200
6408
  emitAsyncExpr(e) {
6201
6409
  return emitAsyncExpr(this, e);
6202
6410
  }
6203
- emitJsInteropExpr(e) {
6204
- return emitJsInteropExpr(this, e);
6205
- }
6206
6411
  /** Read a receiver whose value is consumed immediately by a field or tag
6207
6412
  * load. No user code may run between this read and its consumer. Locals
6208
6413
  * and their projections already have an owner; other expressions keep
@@ -6273,6 +6478,10 @@ export class LlEmitter {
6273
6478
  this.ownSlot(result.owner.slot, result.owner.type);
6274
6479
  return result.value;
6275
6480
  }
6481
+ // A borrowed-return helper (such as an optional element read) lends its
6482
+ // result like any borrowed-return call.
6483
+ if (e.kind === "call" && this.borrowedReturns.has(e.callee) && this.canBorrowReceiver(e))
6484
+ return emitBorrowedResultCall(this, e);
6276
6485
  const arrayRead = this.optionalArrayReads.get(e);
6277
6486
  if (arrayRead)
6278
6487
  return emitProjectedArrayRead(this, arrayRead);
@@ -6518,12 +6727,24 @@ export class LlEmitter {
6518
6727
  const wrap = this.borrowableNullableWrap(e);
6519
6728
  return wrap === "unit" || (wrap === "ref" && this.canBorrowReceiver(e.value));
6520
6729
  }
6521
- // A borrowed-return callee preserves edges, so its result stays
6522
- // reachable from borrowable arguments until the consumer runs.
6523
- case "call":
6524
- return (this.borrowedReturns.has(e.callee) &&
6525
- e.args.every((arg) => (!isRefCounted(arg.type) || this.canBorrowReceiver(arg)) &&
6526
- this.referenceEffects.preserves(arg)));
6730
+ // A borrowed-return result stays reachable from its borrowed
6731
+ // arguments until the consumer runs. Each argument is lent without a
6732
+ // snapshot, so neither the callee nor a later argument may replace
6733
+ // what it was read from: a callee that preserves every edge cannot,
6734
+ // and any other one must leave the argument's locations unwritten.
6735
+ case "call": {
6736
+ if (!this.borrowedReturns.has(e.callee))
6737
+ return false;
6738
+ if (!e.args.every((arg) => this.referenceEffects.preserves(arg)))
6739
+ return false;
6740
+ const preserving = this.referenceEffects.functions.has(e.callee);
6741
+ if (preserving)
6742
+ return e.args.every((arg) => !isRefCounted(arg.type) || this.canBorrowReceiver(arg));
6743
+ if (this.referenceWrites === null)
6744
+ return false;
6745
+ const lent = borrowableInputs(this, e.args, false, this.referenceWrites.summary(e.callee));
6746
+ return e.args.every((arg, i) => !isRefCounted(arg.type) || lent[i] === true);
6747
+ }
6527
6748
  // A required element read: the (borrowed) array owns the element
6528
6749
  // while an edge-preserving index computes.
6529
6750
  case "arrayGet":
@@ -6569,14 +6790,17 @@ export class LlEmitter {
6569
6790
  };
6570
6791
  const hostFor = (fn) => {
6571
6792
  const allowed = borrowedIds(fn);
6572
- return {
6793
+ const host = {
6573
6794
  pointerLocal: (local) => pointer(local.type),
6574
6795
  borrowsWithoutOwning: (e) => this.borrowsWithoutOwning(e),
6575
6796
  borrowedReturn: (callee) => this.borrowedReturns.has(callee),
6576
6797
  parameterAllowed: (id) => allowed.has(id),
6577
6798
  };
6799
+ return this.referenceEffects.functions.has(fn.name) ? host : this.unwrittenWalkHost(fn, host);
6578
6800
  };
6579
- const preserving = [...this.fnByName.values()].filter((fn) => this.referenceEffects.functions.has(fn.name));
6801
+ // With ReferenceWrites, a body that writes none of its walks' locations
6802
+ // qualifies like one that preserves every edge.
6803
+ const preserving = [...this.fnByName.values()].filter((fn) => this.referenceEffects.functions.has(fn.name) || this.referenceWrites !== null);
6580
6804
  const candidates = preserving.filter((fn) => {
6581
6805
  if (!pointer(fn.returnType) || fn.async || fn.generator || fn.captures)
6582
6806
  return false;
@@ -6597,6 +6821,8 @@ export class LlEmitter {
6597
6821
  if (!analyzeWalks(fn, hostFor(fn)).returnsWalk)
6598
6822
  continue;
6599
6823
  this.borrowedReturns.add(fn.name);
6824
+ // Nested borrowed returns extend the locations a caller depends on.
6825
+ this.borrowedReturnKeyCache.clear();
6600
6826
  changed = true;
6601
6827
  }
6602
6828
  }
@@ -6624,6 +6850,12 @@ export class LlEmitter {
6624
6850
  return this.nullableFieldGet(e) === null;
6625
6851
  case "recordGet":
6626
6852
  return true;
6853
+ // emitReadReceiver lends a required element of a borrowed array when
6854
+ // computing the index removes no edge.
6855
+ case "arrayGet":
6856
+ return (e.arr.type.kind === "array" &&
6857
+ this.optionalArrayReads.get(e) === null &&
6858
+ this.referenceEffects.preserves(e.index));
6627
6859
  case "unionNarrow":
6628
6860
  return this.nullableUnions.has(e.unionId);
6629
6861
  case "downcast":
@@ -6665,21 +6897,6 @@ export class LlEmitter {
6665
6897
  emitExpr(e) {
6666
6898
  return emitExpr(this, e);
6667
6899
  }
6668
- emitJsMarshal(e) {
6669
- return emitJsMarshal(this, e);
6670
- }
6671
- emitJsOp(e) {
6672
- return emitJsOp(this, e);
6673
- }
6674
- emitJsExit(e) {
6675
- return emitJsExit(this, e);
6676
- }
6677
- islandAdapter(arity, retKind) {
6678
- return islandAdapter(this, arity, retKind);
6679
- }
6680
- islandTypedAdapter(fn) {
6681
- return islandTypedAdapter(this, fn);
6682
- }
6683
6900
  dynKind(d) {
6684
6901
  return dynKind(this, d);
6685
6902
  }