@geastack/compiler 1.0.15

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 (966) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +149 -0
  3. package/dist/cli-analyze.d.ts +1 -0
  4. package/dist/cli-analyze.js +61 -0
  5. package/dist/cli-coverage.d.ts +1 -0
  6. package/dist/cli-coverage.js +329 -0
  7. package/dist/cli-emit.d.ts +10 -0
  8. package/dist/cli-emit.js +398 -0
  9. package/dist/cli-module-graph.d.ts +18 -0
  10. package/dist/cli-module-graph.js +123 -0
  11. package/dist/cli-package-source.d.ts +5 -0
  12. package/dist/cli-package-source.js +116 -0
  13. package/dist/cli-project.d.ts +2 -0
  14. package/dist/cli-project.js +60 -0
  15. package/dist/cli.d.ts +2 -0
  16. package/dist/cli.js +199 -0
  17. package/dist/compiler.d.ts +228 -0
  18. package/dist/compiler.js +1029 -0
  19. package/dist/conversion/algebra.d.ts +218 -0
  20. package/dist/conversion/algebra.js +155 -0
  21. package/dist/conversion/build.d.ts +67 -0
  22. package/dist/conversion/build.js +1378 -0
  23. package/dist/conversion/derive.d.ts +37 -0
  24. package/dist/conversion/derive.js +260 -0
  25. package/dist/conversion/graph-validation.d.ts +8 -0
  26. package/dist/conversion/graph-validation.js +97 -0
  27. package/dist/conversion/native-callable-adapter.d.ts +13 -0
  28. package/dist/conversion/native-callable-adapter.js +53 -0
  29. package/dist/conversion/native-class-reference.d.ts +12 -0
  30. package/dist/conversion/native-class-reference.js +39 -0
  31. package/dist/conversion/native-payload-transport.d.ts +25 -0
  32. package/dist/conversion/native-payload-transport.js +33 -0
  33. package/dist/conversion/native-selection.d.ts +49 -0
  34. package/dist/conversion/native-selection.js +135 -0
  35. package/dist/conversion/native-sum.d.ts +42 -0
  36. package/dist/conversion/native-sum.js +115 -0
  37. package/dist/conversion/nodes.d.ts +55 -0
  38. package/dist/conversion/nodes.js +91 -0
  39. package/dist/conversion/operand-view.d.ts +14 -0
  40. package/dist/conversion/operand-view.js +20 -0
  41. package/dist/conversion/record-view.d.ts +157 -0
  42. package/dist/conversion/record-view.js +273 -0
  43. package/dist/conversion/registry.d.ts +217 -0
  44. package/dist/conversion/registry.js +1 -0
  45. package/dist/diagnostics/model.d.ts +76 -0
  46. package/dist/diagnostics/model.js +60 -0
  47. package/dist/diagnostics/sweep.d.ts +31 -0
  48. package/dist/diagnostics/sweep.js +112 -0
  49. package/dist/identity/ids.d.ts +179 -0
  50. package/dist/identity/ids.js +212 -0
  51. package/dist/ir/absent-class-arm.d.ts +48 -0
  52. package/dist/ir/absent-class-arm.js +157 -0
  53. package/dist/ir/borrow-effects.d.ts +23 -0
  54. package/dist/ir/borrow-effects.js +151 -0
  55. package/dist/ir/borrowed-call-arguments.d.ts +40 -0
  56. package/dist/ir/borrowed-call-arguments.js +117 -0
  57. package/dist/ir/build.d.ts +168 -0
  58. package/dist/ir/build.js +459 -0
  59. package/dist/ir/call-dispatch.d.ts +38 -0
  60. package/dist/ir/call-dispatch.js +502 -0
  61. package/dist/ir/call-entry.d.ts +142 -0
  62. package/dist/ir/call-entry.js +386 -0
  63. package/dist/ir/callable-class-flow.d.ts +27 -0
  64. package/dist/ir/callable-class-flow.js +1386 -0
  65. package/dist/ir/callable-field-slots.d.ts +22 -0
  66. package/dist/ir/callable-field-slots.js +166 -0
  67. package/dist/ir/callable-identity-demand.d.ts +52 -0
  68. package/dist/ir/callable-identity-demand.js +460 -0
  69. package/dist/ir/callable-member-candidates.d.ts +9 -0
  70. package/dist/ir/callable-member-candidates.js +39 -0
  71. package/dist/ir/callable-records.d.ts +21 -0
  72. package/dist/ir/callable-records.js +411 -0
  73. package/dist/ir/captures.d.ts +26 -0
  74. package/dist/ir/captures.js +335 -0
  75. package/dist/ir/certificate.d.ts +45 -0
  76. package/dist/ir/certificate.js +47 -0
  77. package/dist/ir/certify/carrier-keys.d.ts +73 -0
  78. package/dist/ir/certify/carrier-keys.js +221 -0
  79. package/dist/ir/certify/function-source.d.ts +4 -0
  80. package/dist/ir/certify/function-source.js +28 -0
  81. package/dist/ir/certify/has-property-key.d.ts +88 -0
  82. package/dist/ir/certify/has-property-key.js +116 -0
  83. package/dist/ir/certify/instanceof-key.d.ts +24 -0
  84. package/dist/ir/certify/instanceof-key.js +35 -0
  85. package/dist/ir/certify/property-access-keys.d.ts +172 -0
  86. package/dist/ir/certify/property-access-keys.js +291 -0
  87. package/dist/ir/certify/property-access.d.ts +3 -0
  88. package/dist/ir/certify/property-access.js +597 -0
  89. package/dist/ir/certify/runtime-helper.d.ts +3 -0
  90. package/dist/ir/certify/runtime-helper.js +326 -0
  91. package/dist/ir/certify/typeof-operand.d.ts +14 -0
  92. package/dist/ir/certify/typeof-operand.js +16 -0
  93. package/dist/ir/certify.d.ts +123 -0
  94. package/dist/ir/certify.js +481 -0
  95. package/dist/ir/class-evaluation.d.ts +26 -0
  96. package/dist/ir/class-evaluation.js +50 -0
  97. package/dist/ir/class-static-fields.d.ts +60 -0
  98. package/dist/ir/class-static-fields.js +108 -0
  99. package/dist/ir/construct-entry.d.ts +26 -0
  100. package/dist/ir/construct-entry.js +117 -0
  101. package/dist/ir/dead-values.d.ts +90 -0
  102. package/dist/ir/dead-values.js +184 -0
  103. package/dist/ir/deferral.d.ts +99 -0
  104. package/dist/ir/deferral.js +409 -0
  105. package/dist/ir/dense-loops.d.ts +230 -0
  106. package/dist/ir/dense-loops.js +697 -0
  107. package/dist/ir/dominance.d.ts +82 -0
  108. package/dist/ir/dominance.js +268 -0
  109. package/dist/ir/emission-representations.d.ts +71 -0
  110. package/dist/ir/emission-representations.js +350 -0
  111. package/dist/ir/facts.d.ts +218 -0
  112. package/dist/ir/facts.js +181 -0
  113. package/dist/ir/fixed-data-definition.d.ts +19 -0
  114. package/dist/ir/fixed-data-definition.js +49 -0
  115. package/dist/ir/generator-split.d.ts +38 -0
  116. package/dist/ir/generator-split.js +329 -0
  117. package/dist/ir/hoist.d.ts +55 -0
  118. package/dist/ir/hoist.js +375 -0
  119. package/dist/ir/instantiation.d.ts +61 -0
  120. package/dist/ir/instantiation.js +137 -0
  121. package/dist/ir/integer-storage.d.ts +170 -0
  122. package/dist/ir/integer-storage.js +822 -0
  123. package/dist/ir/integers.d.ts +161 -0
  124. package/dist/ir/integers.js +539 -0
  125. package/dist/ir/integrity-restrictions.d.ts +50 -0
  126. package/dist/ir/integrity-restrictions.js +118 -0
  127. package/dist/ir/local-iterators.d.ts +4 -0
  128. package/dist/ir/local-iterators.js +27 -0
  129. package/dist/ir/lower-allocation.d.ts +4 -0
  130. package/dist/ir/lower-allocation.js +325 -0
  131. package/dist/ir/lower-completion.d.ts +15 -0
  132. package/dist/ir/lower-completion.js +25 -0
  133. package/dist/ir/lower-destructuring.d.ts +5 -0
  134. package/dist/ir/lower-destructuring.js +693 -0
  135. package/dist/ir/lower-element.d.ts +29 -0
  136. package/dist/ir/lower-element.js +111 -0
  137. package/dist/ir/lower-exceptions.d.ts +47 -0
  138. package/dist/ir/lower-exceptions.js +92 -0
  139. package/dist/ir/lower-flow.d.ts +137 -0
  140. package/dist/ir/lower-flow.js +600 -0
  141. package/dist/ir/lower-graph.d.ts +218 -0
  142. package/dist/ir/lower-graph.js +687 -0
  143. package/dist/ir/lower-invocation.d.ts +4 -0
  144. package/dist/ir/lower-invocation.js +514 -0
  145. package/dist/ir/lower-narrow.d.ts +53 -0
  146. package/dist/ir/lower-narrow.js +320 -0
  147. package/dist/ir/lower-operands.d.ts +272 -0
  148. package/dist/ir/lower-operands.js +461 -0
  149. package/dist/ir/lower-property.d.ts +21 -0
  150. package/dist/ir/lower-property.js +132 -0
  151. package/dist/ir/lower-protocol.d.ts +11 -0
  152. package/dist/ir/lower-protocol.js +118 -0
  153. package/dist/ir/lower-short-circuit.d.ts +41 -0
  154. package/dist/ir/lower-short-circuit.js +63 -0
  155. package/dist/ir/lower.d.ts +82 -0
  156. package/dist/ir/lower.js +1346 -0
  157. package/dist/ir/model.d.ts +1474 -0
  158. package/dist/ir/model.js +6 -0
  159. package/dist/ir/native-absent-property.d.ts +3 -0
  160. package/dist/ir/native-absent-property.js +13 -0
  161. package/dist/ir/native-array-transport.d.ts +7 -0
  162. package/dist/ir/native-array-transport.js +46 -0
  163. package/dist/ir/native-callable-bind.d.ts +9 -0
  164. package/dist/ir/native-callable-bind.js +30 -0
  165. package/dist/ir/native-carrier-predicate.d.ts +34 -0
  166. package/dist/ir/native-carrier-predicate.js +49 -0
  167. package/dist/ir/native-class-accessor.d.ts +18 -0
  168. package/dist/ir/native-class-accessor.js +52 -0
  169. package/dist/ir/native-class-construction.d.ts +16 -0
  170. package/dist/ir/native-class-construction.js +71 -0
  171. package/dist/ir/native-class-initialization.d.ts +15 -0
  172. package/dist/ir/native-class-initialization.js +56 -0
  173. package/dist/ir/native-class-origins.d.ts +13 -0
  174. package/dist/ir/native-class-origins.js +237 -0
  175. package/dist/ir/native-dictionary-transport.d.ts +6 -0
  176. package/dist/ir/native-dictionary-transport.js +37 -0
  177. package/dist/ir/native-enumeration.d.ts +14 -0
  178. package/dist/ir/native-enumeration.js +26 -0
  179. package/dist/ir/native-equality.d.ts +10 -0
  180. package/dist/ir/native-equality.js +18 -0
  181. package/dist/ir/native-field-owner.d.ts +37 -0
  182. package/dist/ir/native-field-owner.js +183 -0
  183. package/dist/ir/native-fixed-layout.d.ts +41 -0
  184. package/dist/ir/native-fixed-layout.js +232 -0
  185. package/dist/ir/native-host-construction.d.ts +53 -0
  186. package/dist/ir/native-host-construction.js +62 -0
  187. package/dist/ir/native-key-query.d.ts +4 -0
  188. package/dist/ir/native-key-query.js +38 -0
  189. package/dist/ir/native-merge-transport.d.ts +25 -0
  190. package/dist/ir/native-merge-transport.js +71 -0
  191. package/dist/ir/native-record-index-transport.d.ts +11 -0
  192. package/dist/ir/native-record-index-transport.js +34 -0
  193. package/dist/ir/native-record-index.d.ts +12 -0
  194. package/dist/ir/native-record-index.js +27 -0
  195. package/dist/ir/native-reflect-field.d.ts +5 -0
  196. package/dist/ir/native-reflect-field.js +33 -0
  197. package/dist/ir/native-sequence-transport.d.ts +7 -0
  198. package/dist/ir/native-sequence-transport.js +53 -0
  199. package/dist/ir/numeric-intrinsics.d.ts +13 -0
  200. package/dist/ir/numeric-intrinsics.js +57 -0
  201. package/dist/ir/object-value-conversions.d.ts +20 -0
  202. package/dist/ir/object-value-conversions.js +50 -0
  203. package/dist/ir/physical-class-reflection.d.ts +9 -0
  204. package/dist/ir/physical-class-reflection.js +57 -0
  205. package/dist/ir/presence-proof.d.ts +42 -0
  206. package/dist/ir/presence-proof.js +490 -0
  207. package/dist/ir/program-facts.d.ts +106 -0
  208. package/dist/ir/program-facts.js +319 -0
  209. package/dist/ir/proven-branches.d.ts +16 -0
  210. package/dist/ir/proven-branches.js +219 -0
  211. package/dist/ir/queries.d.ts +41 -0
  212. package/dist/ir/queries.js +213 -0
  213. package/dist/ir/reflection-demand.d.ts +64 -0
  214. package/dist/ir/reflection-demand.js +1394 -0
  215. package/dist/ir/refusal.d.ts +43 -0
  216. package/dist/ir/refusal.js +28 -0
  217. package/dist/ir/shake.d.ts +37 -0
  218. package/dist/ir/shake.js +1078 -0
  219. package/dist/ir/static-callables.d.ts +11 -0
  220. package/dist/ir/static-callables.js +163 -0
  221. package/dist/ir/string-layout-reuse.d.ts +13 -0
  222. package/dist/ir/string-layout-reuse.js +39 -0
  223. package/dist/ir/string-length-reuse.d.ts +8 -0
  224. package/dist/ir/string-length-reuse.js +47 -0
  225. package/dist/ir/transfer.d.ts +78 -0
  226. package/dist/ir/transfer.js +270 -0
  227. package/dist/ir/type-query-results.d.ts +25 -0
  228. package/dist/ir/type-query-results.js +96 -0
  229. package/dist/ir/typed-property-access.d.ts +100 -0
  230. package/dist/ir/typed-property-access.js +161 -0
  231. package/dist/ir/verify.d.ts +19 -0
  232. package/dist/ir/verify.js +355 -0
  233. package/dist/plugins/apple/constructors.d.ts +112 -0
  234. package/dist/plugins/apple/constructors.js +217 -0
  235. package/dist/plugins/apple/host.d.ts +181 -0
  236. package/dist/plugins/apple/host.js +262 -0
  237. package/dist/plugins/apple/jsx.d.ts +2 -0
  238. package/dist/plugins/apple/jsx.js +293 -0
  239. package/dist/plugins/apple/members.d.ts +16 -0
  240. package/dist/plugins/apple/members.js +190 -0
  241. package/dist/plugins/apple/plugin.d.ts +2 -0
  242. package/dist/plugins/apple/plugin.js +156 -0
  243. package/dist/plugins/gea/component-classes.d.ts +25 -0
  244. package/dist/plugins/gea/component-classes.js +162 -0
  245. package/dist/plugins/gea/component-inline.d.ts +13 -0
  246. package/dist/plugins/gea/component-inline.js +532 -0
  247. package/dist/plugins/gea/constructors.d.ts +23 -0
  248. package/dist/plugins/gea/constructors.js +100 -0
  249. package/dist/plugins/gea/contract.d.ts +150 -0
  250. package/dist/plugins/gea/contract.js +195 -0
  251. package/dist/plugins/gea/element-ref.d.ts +31 -0
  252. package/dist/plugins/gea/element-ref.js +45 -0
  253. package/dist/plugins/gea/host.d.ts +520 -0
  254. package/dist/plugins/gea/host.js +762 -0
  255. package/dist/plugins/gea/lower.d.ts +6 -0
  256. package/dist/plugins/gea/lower.js +195 -0
  257. package/dist/plugins/gea/members.d.ts +22 -0
  258. package/dist/plugins/gea/members.js +221 -0
  259. package/dist/plugins/gea/plugin.d.ts +22 -0
  260. package/dist/plugins/gea/plugin.js +307 -0
  261. package/dist/plugins/gea/prelude.d.ts +78 -0
  262. package/dist/plugins/gea/prelude.js +153 -0
  263. package/dist/plugins/gea/producer.d.ts +5 -0
  264. package/dist/plugins/gea/producer.js +52 -0
  265. package/dist/plugins/gea/reactive-slots.d.ts +49 -0
  266. package/dist/plugins/gea/reactive-slots.js +578 -0
  267. package/dist/plugins/gea/render-bridge.d.ts +6 -0
  268. package/dist/plugins/gea/render-bridge.js +281 -0
  269. package/dist/plugins/gea/slots.d.ts +15 -0
  270. package/dist/plugins/gea/slots.js +36 -0
  271. package/dist/plugins/gea/template-analysis.d.ts +151 -0
  272. package/dist/plugins/gea/template-analysis.js +346 -0
  273. package/dist/plugins/gea/user-agent-styles.d.ts +9 -0
  274. package/dist/plugins/gea/user-agent-styles.js +94 -0
  275. package/dist/plugins/installed.d.ts +19 -0
  276. package/dist/plugins/installed.js +21 -0
  277. package/dist/plugins/load.d.ts +7 -0
  278. package/dist/plugins/load.js +207 -0
  279. package/dist/plugins/model.d.ts +735 -0
  280. package/dist/plugins/model.js +47 -0
  281. package/dist/plugins/webgl/host.d.ts +77 -0
  282. package/dist/plugins/webgl/host.js +162 -0
  283. package/dist/plugins/webgl/plugin.d.ts +40 -0
  284. package/dist/plugins/webgl/plugin.js +99 -0
  285. package/dist/preflight/obligations.d.ts +206 -0
  286. package/dist/preflight/obligations.js +36 -0
  287. package/dist/preflight/report.d.ts +20 -0
  288. package/dist/preflight/report.js +61 -0
  289. package/dist/preflight/run.d.ts +39 -0
  290. package/dist/preflight/run.js +403 -0
  291. package/dist/project-preparation.d.ts +33 -0
  292. package/dist/project-preparation.js +314 -0
  293. package/dist/projection/abi.d.ts +82 -0
  294. package/dist/projection/abi.js +349 -0
  295. package/dist/projection/apply-arguments.d.ts +12 -0
  296. package/dist/projection/apply-arguments.js +20 -0
  297. package/dist/projection/bindings.d.ts +250 -0
  298. package/dist/projection/bindings.js +340 -0
  299. package/dist/projection/callee.d.ts +196 -0
  300. package/dist/projection/callee.js +444 -0
  301. package/dist/projection/class-property-presence.d.ts +28 -0
  302. package/dist/projection/class-property-presence.js +58 -0
  303. package/dist/projection/class-prototype.d.ts +5 -0
  304. package/dist/projection/class-prototype.js +23 -0
  305. package/dist/projection/class-storage.d.ts +26 -0
  306. package/dist/projection/class-storage.js +236 -0
  307. package/dist/projection/classes.d.ts +283 -0
  308. package/dist/projection/classes.js +944 -0
  309. package/dist/projection/coercions.d.ts +20 -0
  310. package/dist/projection/coercions.js +33 -0
  311. package/dist/projection/dispatch.d.ts +167 -0
  312. package/dist/projection/dispatch.js +379 -0
  313. package/dist/projection/fields.d.ts +123 -0
  314. package/dist/projection/fields.js +383 -0
  315. package/dist/projection/instance-test.d.ts +78 -0
  316. package/dist/projection/instance-test.js +156 -0
  317. package/dist/projection/slots.d.ts +146 -0
  318. package/dist/projection/slots.js +663 -0
  319. package/dist/projection/synthesized-abi.d.ts +61 -0
  320. package/dist/projection/synthesized-abi.js +94 -0
  321. package/dist/representation/array-index.d.ts +14 -0
  322. package/dist/representation/array-index.js +17 -0
  323. package/dist/representation/callable-object.d.ts +3 -0
  324. package/dist/representation/callable-object.js +6 -0
  325. package/dist/representation/collections.d.ts +84 -0
  326. package/dist/representation/collections.js +139 -0
  327. package/dist/representation/derive.d.ts +159 -0
  328. package/dist/representation/derive.js +2546 -0
  329. package/dist/representation/difference.d.ts +30 -0
  330. package/dist/representation/difference.js +240 -0
  331. package/dist/representation/field-descriptor-policy.d.ts +19 -0
  332. package/dist/representation/field-descriptor-policy.js +39 -0
  333. package/dist/representation/function-constructor.d.ts +13 -0
  334. package/dist/representation/function-constructor.js +9 -0
  335. package/dist/representation/host-abi.d.ts +32 -0
  336. package/dist/representation/host-abi.js +97 -0
  337. package/dist/representation/host-templates.d.ts +67 -0
  338. package/dist/representation/host-templates.js +94 -0
  339. package/dist/representation/intersection.d.ts +84 -0
  340. package/dist/representation/intersection.js +149 -0
  341. package/dist/representation/literal-destination.d.ts +43 -0
  342. package/dist/representation/literal-destination.js +133 -0
  343. package/dist/representation/merge.d.ts +18 -0
  344. package/dist/representation/merge.js +83 -0
  345. package/dist/representation/model.d.ts +1160 -0
  346. package/dist/representation/model.js +1019 -0
  347. package/dist/representation/numeric-index.d.ts +13 -0
  348. package/dist/representation/numeric-index.js +29 -0
  349. package/dist/representation/object-shape.d.ts +109 -0
  350. package/dist/representation/object-shape.js +123 -0
  351. package/dist/representation/optional.d.ts +12 -0
  352. package/dist/representation/optional.js +87 -0
  353. package/dist/representation/plan.d.ts +60 -0
  354. package/dist/representation/plan.js +78 -0
  355. package/dist/representation/policies.d.ts +589 -0
  356. package/dist/representation/policies.js +107 -0
  357. package/dist/representation/primitive-domain.d.ts +1 -0
  358. package/dist/representation/primitive-domain.js +1 -0
  359. package/dist/representation/primitives.d.ts +44 -0
  360. package/dist/representation/primitives.js +78 -0
  361. package/dist/representation/publish.d.ts +49 -0
  362. package/dist/representation/publish.js +648 -0
  363. package/dist/representation/record-fields.d.ts +96 -0
  364. package/dist/representation/record-fields.js +116 -0
  365. package/dist/representation/template-object.d.ts +11 -0
  366. package/dist/representation/template-object.js +26 -0
  367. package/dist/representation/union.d.ts +30 -0
  368. package/dist/representation/union.js +324 -0
  369. package/dist/representation/value-records.d.ts +3 -0
  370. package/dist/representation/value-records.js +307 -0
  371. package/dist/representation/verify.d.ts +17 -0
  372. package/dist/representation/verify.js +221 -0
  373. package/dist/semantics/alias-this-field-declaration-transform.d.ts +4 -0
  374. package/dist/semantics/alias-this-field-declaration-transform.js +207 -0
  375. package/dist/semantics/ambient-type-realization-transform.d.ts +62 -0
  376. package/dist/semantics/ambient-type-realization-transform.js +168 -0
  377. package/dist/semantics/ambient.d.ts +24 -0
  378. package/dist/semantics/ambient.js +119 -0
  379. package/dist/semantics/borrowed-builtin-call-bind-source-transform.d.ts +4 -0
  380. package/dist/semantics/borrowed-builtin-call-bind-source-transform.js +341 -0
  381. package/dist/semantics/callable-origins.d.ts +46 -0
  382. package/dist/semantics/callable-origins.js +354 -0
  383. package/dist/semantics/class-alias.d.ts +7 -0
  384. package/dist/semantics/class-alias.js +37 -0
  385. package/dist/semantics/class-heritage.d.ts +34 -0
  386. package/dist/semantics/class-heritage.js +104 -0
  387. package/dist/semantics/commonjs-wrapper.d.ts +69 -0
  388. package/dist/semantics/commonjs-wrapper.js +231 -0
  389. package/dist/semantics/constructor-slot-subclasses.d.ts +28 -0
  390. package/dist/semantics/constructor-slot-subclasses.js +112 -0
  391. package/dist/semantics/declaration-overlay-transform.d.ts +35 -0
  392. package/dist/semantics/declaration-overlay-transform.js +2157 -0
  393. package/dist/semantics/define-property-source-transform.d.ts +4 -0
  394. package/dist/semantics/define-property-source-transform.js +437 -0
  395. package/dist/semantics/diagnostic-source-preparation.d.ts +24 -0
  396. package/dist/semantics/diagnostic-source-preparation.js +82 -0
  397. package/dist/semantics/documentation-ranges.d.ts +22 -0
  398. package/dist/semantics/documentation-ranges.js +36 -0
  399. package/dist/semantics/dynamic-fallback.d.ts +86 -0
  400. package/dist/semantics/dynamic-fallback.js +249 -0
  401. package/dist/semantics/frontend-timing.d.ts +6 -0
  402. package/dist/semantics/frontend-timing.js +35 -0
  403. package/dist/semantics/frontend.d.ts +504 -0
  404. package/dist/semantics/frontend.js +1157 -0
  405. package/dist/semantics/host-declaration-provenance.d.ts +11 -0
  406. package/dist/semantics/host-declaration-provenance.js +18 -0
  407. package/dist/semantics/host-methods.d.ts +24 -0
  408. package/dist/semantics/host-methods.js +74 -0
  409. package/dist/semantics/host-protocols.d.ts +429 -0
  410. package/dist/semantics/host-protocols.js +1501 -0
  411. package/dist/semantics/inherited-accessor.d.ts +7 -0
  412. package/dist/semantics/inherited-accessor.js +48 -0
  413. package/dist/semantics/interface-families.d.ts +70 -0
  414. package/dist/semantics/interface-families.js +180 -0
  415. package/dist/semantics/interface-implementors.d.ts +53 -0
  416. package/dist/semantics/interface-implementors.js +87 -0
  417. package/dist/semantics/jsdoc-namepath-transform.d.ts +4 -0
  418. package/dist/semantics/jsdoc-namepath-transform.js +85 -0
  419. package/dist/semantics/model/coverage.d.ts +68 -0
  420. package/dist/semantics/model/coverage.js +73 -0
  421. package/dist/semantics/model/edges.d.ts +103 -0
  422. package/dist/semantics/model/edges.js +20 -0
  423. package/dist/semantics/model/graph.d.ts +55 -0
  424. package/dist/semantics/model/graph.js +129 -0
  425. package/dist/semantics/model/operands.d.ts +159 -0
  426. package/dist/semantics/model/operands.js +26 -0
  427. package/dist/semantics/model/operations.d.ts +496 -0
  428. package/dist/semantics/model/operations.js +2 -0
  429. package/dist/semantics/model/primitive-domain.d.ts +26 -0
  430. package/dist/semantics/model/primitive-domain.js +38 -0
  431. package/dist/semantics/model/selected-signature.d.ts +261 -0
  432. package/dist/semantics/model/selected-signature.js +55 -0
  433. package/dist/semantics/model/structural-type-table.d.ts +93 -0
  434. package/dist/semantics/model/structural-type-table.js +237 -0
  435. package/dist/semantics/model/structural-types.d.ts +367 -0
  436. package/dist/semantics/model/structural-types.js +200 -0
  437. package/dist/semantics/module-resolution.d.ts +23 -0
  438. package/dist/semantics/module-resolution.js +227 -0
  439. package/dist/semantics/new-callee-class-tag-source-transform.d.ts +4 -0
  440. package/dist/semantics/new-callee-class-tag-source-transform.js +64 -0
  441. package/dist/semantics/node-project.d.ts +13 -0
  442. package/dist/semantics/node-project.js +107 -0
  443. package/dist/semantics/node-source-hooks.d.ts +1 -0
  444. package/dist/semantics/node-source-hooks.js +63 -0
  445. package/dist/semantics/normalize/absent-globals.d.ts +143 -0
  446. package/dist/semantics/normalize/absent-globals.js +191 -0
  447. package/dist/semantics/normalize/arguments-objects.d.ts +43 -0
  448. package/dist/semantics/normalize/arguments-objects.js +60 -0
  449. package/dist/semantics/normalize/asserted-arm-absence.d.ts +28 -0
  450. package/dist/semantics/normalize/asserted-arm-absence.js +61 -0
  451. package/dist/semantics/normalize/assignment-patterns.d.ts +70 -0
  452. package/dist/semantics/normalize/assignment-patterns.js +147 -0
  453. package/dist/semantics/normalize/bag-absence.d.ts +8 -0
  454. package/dist/semantics/normalize/bag-absence.js +42 -0
  455. package/dist/semantics/normalize/binding-fixpoint.d.ts +36 -0
  456. package/dist/semantics/normalize/binding-fixpoint.js +140 -0
  457. package/dist/semantics/normalize/callback-parameter-contracts.d.ts +16 -0
  458. package/dist/semantics/normalize/callback-parameter-contracts.js +91 -0
  459. package/dist/semantics/normalize/cells/agreement.d.ts +46 -0
  460. package/dist/semantics/normalize/cells/agreement.js +34 -0
  461. package/dist/semantics/normalize/cells/candidates.d.ts +17 -0
  462. package/dist/semantics/normalize/cells/candidates.js +35 -0
  463. package/dist/semantics/normalize/cells/facts.d.ts +19 -0
  464. package/dist/semantics/normalize/cells/facts.js +90 -0
  465. package/dist/semantics/normalize/cells/index.d.ts +10 -0
  466. package/dist/semantics/normalize/cells/index.js +6 -0
  467. package/dist/semantics/normalize/cells/model.d.ts +98 -0
  468. package/dist/semantics/normalize/cells/model.js +2 -0
  469. package/dist/semantics/normalize/cells/policies/bag.d.ts +44 -0
  470. package/dist/semantics/normalize/cells/policies/bag.js +114 -0
  471. package/dist/semantics/normalize/cells/policies/collection.d.ts +42 -0
  472. package/dist/semantics/normalize/cells/policies/collection.js +218 -0
  473. package/dist/semantics/normalize/cells/policies/field.d.ts +28 -0
  474. package/dist/semantics/normalize/cells/policies/field.js +40 -0
  475. package/dist/semantics/normalize/cells/policies/index.d.ts +49 -0
  476. package/dist/semantics/normalize/cells/policies/index.js +58 -0
  477. package/dist/semantics/normalize/cells/policies/local.d.ts +18 -0
  478. package/dist/semantics/normalize/cells/policies/local.js +37 -0
  479. package/dist/semantics/normalize/cells/policies/parameter.d.ts +28 -0
  480. package/dist/semantics/normalize/cells/policies/parameter.js +39 -0
  481. package/dist/semantics/normalize/cells/policies/return.d.ts +26 -0
  482. package/dist/semantics/normalize/cells/policies/return.js +45 -0
  483. package/dist/semantics/normalize/cells/policies/shared.d.ts +33 -0
  484. package/dist/semantics/normalize/cells/policies/shared.js +45 -0
  485. package/dist/semantics/normalize/cells/policy.d.ts +72 -0
  486. package/dist/semantics/normalize/cells/policy.js +18 -0
  487. package/dist/semantics/normalize/cells/registry.d.ts +14 -0
  488. package/dist/semantics/normalize/cells/registry.js +17 -0
  489. package/dist/semantics/normalize/census-refusal.d.ts +62 -0
  490. package/dist/semantics/normalize/census-refusal.js +27 -0
  491. package/dist/semantics/normalize/census.d.ts +109 -0
  492. package/dist/semantics/normalize/census.js +667 -0
  493. package/dist/semantics/normalize/collection-bindings.d.ts +278 -0
  494. package/dist/semantics/normalize/collection-bindings.js +677 -0
  495. package/dist/semantics/normalize/commonjs-module-record.d.ts +21 -0
  496. package/dist/semantics/normalize/commonjs-module-record.js +289 -0
  497. package/dist/semantics/normalize/commonjs-require.d.ts +13 -0
  498. package/dist/semantics/normalize/commonjs-require.js +1527 -0
  499. package/dist/semantics/normalize/contribution.d.ts +63 -0
  500. package/dist/semantics/normalize/contribution.js +172 -0
  501. package/dist/semantics/normalize/dead-typeof-guards.d.ts +64 -0
  502. package/dist/semantics/normalize/dead-typeof-guards.js +152 -0
  503. package/dist/semantics/normalize/deferred-intrinsic-protocols.d.ts +57 -0
  504. package/dist/semantics/normalize/deferred-intrinsic-protocols.js +109 -0
  505. package/dist/semantics/normalize/derived-expression-type.d.ts +1070 -0
  506. package/dist/semantics/normalize/derived-expression-type.js +3463 -0
  507. package/dist/semantics/normalize/evaluation-order.d.ts +16 -0
  508. package/dist/semantics/normalize/evaluation-order.js +37 -0
  509. package/dist/semantics/normalize/field-bindings.d.ts +167 -0
  510. package/dist/semantics/normalize/field-bindings.js +1125 -0
  511. package/dist/semantics/normalize/flow/array-element-continuation.d.ts +27 -0
  512. package/dist/semantics/normalize/flow/array-element-continuation.js +1452 -0
  513. package/dist/semantics/normalize/flow/callable-array-origins.d.ts +42 -0
  514. package/dist/semantics/normalize/flow/callable-array-origins.js +318 -0
  515. package/dist/semantics/normalize/flow/callable-completions.d.ts +45 -0
  516. package/dist/semantics/normalize/flow/callable-completions.js +82 -0
  517. package/dist/semantics/normalize/flow/callable-reach.d.ts +237 -0
  518. package/dist/semantics/normalize/flow/callable-reach.js +7174 -0
  519. package/dist/semantics/normalize/flow/class-family-member-read.d.ts +16 -0
  520. package/dist/semantics/normalize/flow/class-family-member-read.js +992 -0
  521. package/dist/semantics/normalize/flow/collection-value-continuation.d.ts +21 -0
  522. package/dist/semantics/normalize/flow/collection-value-continuation.js +192 -0
  523. package/dist/semantics/normalize/flow/component-solver.d.ts +199 -0
  524. package/dist/semantics/normalize/flow/component-solver.js +587 -0
  525. package/dist/semantics/normalize/flow/computed-key-set.d.ts +84 -0
  526. package/dist/semantics/normalize/flow/computed-key-set.js +660 -0
  527. package/dist/semantics/normalize/flow/export-importers.d.ts +24 -0
  528. package/dist/semantics/normalize/flow/export-importers.js +269 -0
  529. package/dist/semantics/normalize/flow/host-text-sink.d.ts +25 -0
  530. package/dist/semantics/normalize/flow/host-text-sink.js +87 -0
  531. package/dist/semantics/normalize/flow/intrinsic-array-callback-frames.d.ts +84 -0
  532. package/dist/semantics/normalize/flow/intrinsic-array-callback-frames.js +180 -0
  533. package/dist/semantics/normalize/flow/intrinsic-data-definition.d.ts +35 -0
  534. package/dist/semantics/normalize/flow/intrinsic-data-definition.js +140 -0
  535. package/dist/semantics/normalize/flow/invocation-facts.d.ts +98 -0
  536. package/dist/semantics/normalize/flow/invocation-facts.js +257 -0
  537. package/dist/semantics/normalize/flow/member-call-forwarding.d.ts +122 -0
  538. package/dist/semantics/normalize/flow/member-call-forwarding.js +835 -0
  539. package/dist/semantics/normalize/flow/model.d.ts +357 -0
  540. package/dist/semantics/normalize/flow/model.js +177 -0
  541. package/dist/semantics/normalize/flow/native-collection-protocol.d.ts +24 -0
  542. package/dist/semantics/normalize/flow/native-collection-protocol.js +252 -0
  543. package/dist/semantics/normalize/flow/node-path-token.d.ts +3 -0
  544. package/dist/semantics/normalize/flow/node-path-token.js +53 -0
  545. package/dist/semantics/normalize/flow/object-binding-continuation.d.ts +11 -0
  546. package/dist/semantics/normalize/flow/object-binding-continuation.js +42 -0
  547. package/dist/semantics/normalize/flow/origin-authority.d.ts +56 -0
  548. package/dist/semantics/normalize/flow/origin-authority.js +2 -0
  549. package/dist/semantics/normalize/flow/owned-class-receivers.d.ts +35 -0
  550. package/dist/semantics/normalize/flow/owned-class-receivers.js +191 -0
  551. package/dist/semantics/normalize/flow/parameter-values.d.ts +27 -0
  552. package/dist/semantics/normalize/flow/parameter-values.js +251 -0
  553. package/dist/semantics/normalize/flow/proof-hypotheses.d.ts +66 -0
  554. package/dist/semantics/normalize/flow/proof-hypotheses.js +162 -0
  555. package/dist/semantics/normalize/flow/record-alias-closure.d.ts +27 -0
  556. package/dist/semantics/normalize/flow/record-alias-closure.js +298 -0
  557. package/dist/semantics/normalize/flow/seeded-origins.d.ts +43 -0
  558. package/dist/semantics/normalize/flow/seeded-origins.js +54 -0
  559. package/dist/semantics/normalize/flow/source-class-data.d.ts +132 -0
  560. package/dist/semantics/normalize/flow/source-class-data.js +838 -0
  561. package/dist/semantics/normalize/flow/source-class-instanceof.d.ts +13 -0
  562. package/dist/semantics/normalize/flow/source-class-instanceof.js +37 -0
  563. package/dist/semantics/normalize/flow/source-class-static-data.d.ts +16 -0
  564. package/dist/semantics/normalize/flow/source-class-static-data.js +47 -0
  565. package/dist/semantics/normalize/flow/source-construction-frames.d.ts +12 -0
  566. package/dist/semantics/normalize/flow/source-construction-frames.js +118 -0
  567. package/dist/semantics/normalize/flow/source-invocation-frame-layout.d.ts +34 -0
  568. package/dist/semantics/normalize/flow/source-invocation-frame-layout.js +78 -0
  569. package/dist/semantics/normalize/flow/source-invocation-receiver.d.ts +37 -0
  570. package/dist/semantics/normalize/flow/source-invocation-receiver.js +95 -0
  571. package/dist/semantics/normalize/flow/source-prototype-method-identity.d.ts +23 -0
  572. package/dist/semantics/normalize/flow/source-prototype-method-identity.js +81 -0
  573. package/dist/semantics/normalize/flow/source-record-data.d.ts +29 -0
  574. package/dist/semantics/normalize/flow/source-record-data.js +626 -0
  575. package/dist/semantics/normalize/flow/source-value-session.d.ts +77 -0
  576. package/dist/semantics/normalize/flow/source-value-session.js +3089 -0
  577. package/dist/semantics/normalize/flow/targets.d.ts +118 -0
  578. package/dist/semantics/normalize/flow/targets.js +405 -0
  579. package/dist/semantics/normalize/flow/value-flow.d.ts +36 -0
  580. package/dist/semantics/normalize/flow/value-flow.js +823 -0
  581. package/dist/semantics/normalize/flow/value-provenance.d.ts +31 -0
  582. package/dist/semantics/normalize/flow/value-provenance.js +112 -0
  583. package/dist/semantics/normalize/gating.d.ts +51 -0
  584. package/dist/semantics/normalize/gating.js +560 -0
  585. package/dist/semantics/normalize/generic-function-choice.d.ts +31 -0
  586. package/dist/semantics/normalize/generic-function-choice.js +115 -0
  587. package/dist/semantics/normalize/global-host-mutations.d.ts +51 -0
  588. package/dist/semantics/normalize/global-host-mutations.js +5989 -0
  589. package/dist/semantics/normalize/host-effect-contracts.d.ts +77 -0
  590. package/dist/semantics/normalize/host-effect-contracts.js +143 -0
  591. package/dist/semantics/normalize/host-mutation-computed-keys.d.ts +38 -0
  592. package/dist/semantics/normalize/host-mutation-computed-keys.js +26 -0
  593. package/dist/semantics/normalize/host-mutation-key-reader.d.ts +66 -0
  594. package/dist/semantics/normalize/host-mutation-key-reader.js +192 -0
  595. package/dist/semantics/normalize/host-mutation-keys.d.ts +114 -0
  596. package/dist/semantics/normalize/host-mutation-keys.js +183 -0
  597. package/dist/semantics/normalize/identities.d.ts +172 -0
  598. package/dist/semantics/normalize/identities.js +426 -0
  599. package/dist/semantics/normalize/implicit-arguments-tuple.d.ts +97 -0
  600. package/dist/semantics/normalize/implicit-arguments-tuple.js +197 -0
  601. package/dist/semantics/normalize/implicit-arguments.d.ts +11 -0
  602. package/dist/semantics/normalize/implicit-arguments.js +39 -0
  603. package/dist/semantics/normalize/instantiation.d.ts +73 -0
  604. package/dist/semantics/normalize/instantiation.js +399 -0
  605. package/dist/semantics/normalize/intrinsic-property-call.d.ts +18 -0
  606. package/dist/semantics/normalize/intrinsic-property-call.js +69 -0
  607. package/dist/semantics/normalize/intrinsic-prototype.d.ts +41 -0
  608. package/dist/semantics/normalize/intrinsic-prototype.js +87 -0
  609. package/dist/semantics/normalize/intrinsic-static-member.d.ts +14 -0
  610. package/dist/semantics/normalize/intrinsic-static-member.js +23 -0
  611. package/dist/semantics/normalize/jsdoc-type-names.d.ts +32 -0
  612. package/dist/semantics/normalize/jsdoc-type-names.js +512 -0
  613. package/dist/semantics/normalize/keyof.d.ts +32 -0
  614. package/dist/semantics/normalize/keyof.js +110 -0
  615. package/dist/semantics/normalize/local-bindings.d.ts +176 -0
  616. package/dist/semantics/normalize/local-bindings.js +1826 -0
  617. package/dist/semantics/normalize/logical-result-type.d.ts +16 -0
  618. package/dist/semantics/normalize/logical-result-type.js +102 -0
  619. package/dist/semantics/normalize/namespace-paths.d.ts +57 -0
  620. package/dist/semantics/normalize/namespace-paths.js +108 -0
  621. package/dist/semantics/normalize/normalize.d.ts +57 -0
  622. package/dist/semantics/normalize/normalize.js +68 -0
  623. package/dist/semantics/normalize/object-bag-bindings.d.ts +305 -0
  624. package/dist/semantics/normalize/object-bag-bindings.js +1290 -0
  625. package/dist/semantics/normalize/omitted-stated-parameter.d.ts +53 -0
  626. package/dist/semantics/normalize/omitted-stated-parameter.js +59 -0
  627. package/dist/semantics/normalize/open-type-form.d.ts +10 -0
  628. package/dist/semantics/normalize/open-type-form.js +32 -0
  629. package/dist/semantics/normalize/parameter-bindings.d.ts +361 -0
  630. package/dist/semantics/normalize/parameter-bindings.js +3339 -0
  631. package/dist/semantics/normalize/parameter-slot.d.ts +264 -0
  632. package/dist/semantics/normalize/parameter-slot.js +491 -0
  633. package/dist/semantics/normalize/physical-overload-result.d.ts +77 -0
  634. package/dist/semantics/normalize/physical-overload-result.js +99 -0
  635. package/dist/semantics/normalize/primitive-property-absence.d.ts +12 -0
  636. package/dist/semantics/normalize/primitive-property-absence.js +60 -0
  637. package/dist/semantics/normalize/producer-context.d.ts +376 -0
  638. package/dist/semantics/normalize/producer-context.js +1 -0
  639. package/dist/semantics/normalize/producers/allocations.d.ts +3 -0
  640. package/dist/semantics/normalize/producers/allocations.js +863 -0
  641. package/dist/semantics/normalize/producers/binding-kind.d.ts +37 -0
  642. package/dist/semantics/normalize/producers/binding-kind.js +36 -0
  643. package/dist/semantics/normalize/producers/bindings.d.ts +85 -0
  644. package/dist/semantics/normalize/producers/bindings.js +710 -0
  645. package/dist/semantics/normalize/producers/boundary.d.ts +34 -0
  646. package/dist/semantics/normalize/producers/boundary.js +123 -0
  647. package/dist/semantics/normalize/producers/class-lifecycle.d.ts +3 -0
  648. package/dist/semantics/normalize/producers/class-lifecycle.js +902 -0
  649. package/dist/semantics/normalize/producers/computations.d.ts +3 -0
  650. package/dist/semantics/normalize/producers/computations.js +613 -0
  651. package/dist/semantics/normalize/producers/control-loops.d.ts +30 -0
  652. package/dist/semantics/normalize/producers/control-loops.js +91 -0
  653. package/dist/semantics/normalize/producers/control.d.ts +30 -0
  654. package/dist/semantics/normalize/producers/control.js +923 -0
  655. package/dist/semantics/normalize/producers/declaration-lifecycle.d.ts +3 -0
  656. package/dist/semantics/normalize/producers/declaration-lifecycle.js +390 -0
  657. package/dist/semantics/normalize/producers/destructuring.d.ts +45 -0
  658. package/dist/semantics/normalize/producers/destructuring.js +1180 -0
  659. package/dist/semantics/normalize/producers/dynamic-language.d.ts +3 -0
  660. package/dist/semantics/normalize/producers/dynamic-language.js +70 -0
  661. package/dist/semantics/normalize/producers/erasure.d.ts +51 -0
  662. package/dist/semantics/normalize/producers/erasure.js +90 -0
  663. package/dist/semantics/normalize/producers/installed.d.ts +3 -0
  664. package/dist/semantics/normalize/producers/installed.js +55 -0
  665. package/dist/semantics/normalize/producers/invocations.d.ts +35 -0
  666. package/dist/semantics/normalize/producers/invocations.js +1796 -0
  667. package/dist/semantics/normalize/producers/iteration-yield.d.ts +59 -0
  668. package/dist/semantics/normalize/producers/iteration-yield.js +235 -0
  669. package/dist/semantics/normalize/producers/jsx.d.ts +3 -0
  670. package/dist/semantics/normalize/producers/jsx.js +360 -0
  671. package/dist/semantics/normalize/producers/mint.d.ts +39 -0
  672. package/dist/semantics/normalize/producers/mint.js +44 -0
  673. package/dist/semantics/normalize/producers/nullish.d.ts +16 -0
  674. package/dist/semantics/normalize/producers/nullish.js +27 -0
  675. package/dist/semantics/normalize/producers/object-assignment.d.ts +8 -0
  676. package/dist/semantics/normalize/producers/object-assignment.js +204 -0
  677. package/dist/semantics/normalize/producers/object-tag.d.ts +9 -0
  678. package/dist/semantics/normalize/producers/object-tag.js +119 -0
  679. package/dist/semantics/normalize/producers/optional-chain.d.ts +44 -0
  680. package/dist/semantics/normalize/producers/optional-chain.js +75 -0
  681. package/dist/semantics/normalize/producers/properties.d.ts +21 -0
  682. package/dist/semantics/normalize/producers/properties.js +1053 -0
  683. package/dist/semantics/normalize/producers/protocol.d.ts +67 -0
  684. package/dist/semantics/normalize/producers/protocol.js +695 -0
  685. package/dist/semantics/normalize/producers/references.d.ts +53 -0
  686. package/dist/semantics/normalize/producers/references.js +1027 -0
  687. package/dist/semantics/normalize/producers/shared.d.ts +561 -0
  688. package/dist/semantics/normalize/producers/shared.js +1683 -0
  689. package/dist/semantics/normalize/producers/spread-arguments.d.ts +72 -0
  690. package/dist/semantics/normalize/producers/spread-arguments.js +375 -0
  691. package/dist/semantics/normalize/producers/tagged-template.d.ts +5 -0
  692. package/dist/semantics/normalize/producers/tagged-template.js +307 -0
  693. package/dist/semantics/normalize/producers/tuple-spread.d.ts +189 -0
  694. package/dist/semantics/normalize/producers/tuple-spread.js +279 -0
  695. package/dist/semantics/normalize/property-key-domain.d.ts +36 -0
  696. package/dist/semantics/normalize/property-key-domain.js +150 -0
  697. package/dist/semantics/normalize/reachability.d.ts +189 -0
  698. package/dist/semantics/normalize/reachability.js +1057 -0
  699. package/dist/semantics/normalize/reassigned-bindings.d.ts +13 -0
  700. package/dist/semantics/normalize/reassigned-bindings.js +41 -0
  701. package/dist/semantics/normalize/record-storage-families.d.ts +11 -0
  702. package/dist/semantics/normalize/record-storage-families.js +109 -0
  703. package/dist/semantics/normalize/return-bindings.d.ts +260 -0
  704. package/dist/semantics/normalize/return-bindings.js +1311 -0
  705. package/dist/semantics/normalize/return-paths.d.ts +2 -0
  706. package/dist/semantics/normalize/return-paths.js +28 -0
  707. package/dist/semantics/normalize/script-global-redefinition.d.ts +44 -0
  708. package/dist/semantics/normalize/script-global-redefinition.js +75 -0
  709. package/dist/semantics/normalize/specialization.d.ts +201 -0
  710. package/dist/semantics/normalize/specialization.js +1824 -0
  711. package/dist/semantics/normalize/structural-array-element.d.ts +176 -0
  712. package/dist/semantics/normalize/structural-array-element.js +700 -0
  713. package/dist/semantics/normalize/structural-array-read.d.ts +13 -0
  714. package/dist/semantics/normalize/structural-array-read.js +124 -0
  715. package/dist/semantics/normalize/structural-callable.d.ts +12 -0
  716. package/dist/semantics/normalize/structural-callable.js +82 -0
  717. package/dist/semantics/normalize/structural-creation-order.d.ts +32 -0
  718. package/dist/semantics/normalize/structural-creation-order.js +111 -0
  719. package/dist/semantics/normalize/structural-declarations.d.ts +395 -0
  720. package/dist/semantics/normalize/structural-declarations.js +968 -0
  721. package/dist/semantics/normalize/structural-declared-body.d.ts +25 -0
  722. package/dist/semantics/normalize/structural-declared-body.js +147 -0
  723. package/dist/semantics/normalize/structural-generics.d.ts +56 -0
  724. package/dist/semantics/normalize/structural-generics.js +120 -0
  725. package/dist/semantics/normalize/structural-indexed-access.d.ts +11 -0
  726. package/dist/semantics/normalize/structural-indexed-access.js +126 -0
  727. package/dist/semantics/normalize/structural-instantiated-member.d.ts +64 -0
  728. package/dist/semantics/normalize/structural-instantiated-member.js +407 -0
  729. package/dist/semantics/normalize/structural-joins.d.ts +44 -0
  730. package/dist/semantics/normalize/structural-joins.js +143 -0
  731. package/dist/semantics/normalize/structural-layout-relevance.d.ts +29 -0
  732. package/dist/semantics/normalize/structural-layout-relevance.js +391 -0
  733. package/dist/semantics/normalize/structural-layout-type.d.ts +11 -0
  734. package/dist/semantics/normalize/structural-layout-type.js +1468 -0
  735. package/dist/semantics/normalize/structural-leaves.d.ts +49 -0
  736. package/dist/semantics/normalize/structural-leaves.js +125 -0
  737. package/dist/semantics/normalize/structural-local-union.d.ts +13 -0
  738. package/dist/semantics/normalize/structural-local-union.js +133 -0
  739. package/dist/semantics/normalize/structural-members.d.ts +26 -0
  740. package/dist/semantics/normalize/structural-members.js +84 -0
  741. package/dist/semantics/normalize/structural-mutable-method.d.ts +15 -0
  742. package/dist/semantics/normalize/structural-mutable-method.js +109 -0
  743. package/dist/semantics/normalize/structural-parts.d.ts +131 -0
  744. package/dist/semantics/normalize/structural-parts.js +737 -0
  745. package/dist/semantics/normalize/structural-receiver.d.ts +66 -0
  746. package/dist/semantics/normalize/structural-receiver.js +551 -0
  747. package/dist/semantics/normalize/structural-rules.d.ts +112 -0
  748. package/dist/semantics/normalize/structural-rules.js +93 -0
  749. package/dist/semantics/normalize/structural-self-reference.d.ts +150 -0
  750. package/dist/semantics/normalize/structural-self-reference.js +494 -0
  751. package/dist/semantics/normalize/structural.d.ts +202 -0
  752. package/dist/semantics/normalize/structural.js +4218 -0
  753. package/dist/semantics/normalize/unreduced-type-form.d.ts +41 -0
  754. package/dist/semantics/normalize/unreduced-type-form.js +53 -0
  755. package/dist/semantics/normalize/unresolvable-names.d.ts +56 -0
  756. package/dist/semantics/normalize/unresolvable-names.js +140 -0
  757. package/dist/semantics/package-sources.d.ts +16 -0
  758. package/dist/semantics/package-sources.js +365 -0
  759. package/dist/semantics/program.d.ts +151 -0
  760. package/dist/semantics/program.js +667 -0
  761. package/dist/semantics/prototype-install-source-transform.d.ts +4 -0
  762. package/dist/semantics/prototype-install-source-transform.js +341 -0
  763. package/dist/semantics/prototype-object-class-source-transform.d.ts +4 -0
  764. package/dist/semantics/prototype-object-class-source-transform.js +525 -0
  765. package/dist/semantics/prototype-reparenting.d.ts +50 -0
  766. package/dist/semantics/prototype-reparenting.js +317 -0
  767. package/dist/semantics/stable-checker.d.ts +10 -0
  768. package/dist/semantics/stable-checker.js +148 -0
  769. package/dist/semantics/subclass-member-overlay-transform.d.ts +35 -0
  770. package/dist/semantics/subclass-member-overlay-transform.js +1418 -0
  771. package/dist/semantics/symbol-keyed-expando-source-transform.d.ts +4 -0
  772. package/dist/semantics/symbol-keyed-expando-source-transform.js +66 -0
  773. package/dist/semantics/this-constructor-source-transform.d.ts +4 -0
  774. package/dist/semantics/this-constructor-source-transform.js +122 -0
  775. package/dist/semantics/uninstantiable-classes.d.ts +22 -0
  776. package/dist/semantics/uninstantiable-classes.js +75 -0
  777. package/dist/targets/cpp/balanced-units.d.ts +3 -0
  778. package/dist/targets/cpp/balanced-units.js +58 -0
  779. package/dist/targets/cpp/borrowed-call-entry.d.ts +30 -0
  780. package/dist/targets/cpp/borrowed-call-entry.js +68 -0
  781. package/dist/targets/cpp/captures.d.ts +68 -0
  782. package/dist/targets/cpp/captures.js +269 -0
  783. package/dist/targets/cpp/class-layout.d.ts +313 -0
  784. package/dist/targets/cpp/class-layout.js +483 -0
  785. package/dist/targets/cpp/class-properties/computed-method-value.d.ts +25 -0
  786. package/dist/targets/cpp/class-properties/computed-method-value.js +22 -0
  787. package/dist/targets/cpp/class-properties/emit-class-properties.d.ts +216 -0
  788. package/dist/targets/cpp/class-properties/emit-class-properties.js +917 -0
  789. package/dist/targets/cpp/class-properties/native-prototype.d.ts +11 -0
  790. package/dist/targets/cpp/class-properties/native-prototype.js +101 -0
  791. package/dist/targets/cpp/class-ref-transport.d.ts +12 -0
  792. package/dist/targets/cpp/class-ref-transport.js +32 -0
  793. package/dist/targets/cpp/conversions.d.ts +3 -0
  794. package/dist/targets/cpp/conversions.js +2027 -0
  795. package/dist/targets/cpp/deferral-safety.d.ts +82 -0
  796. package/dist/targets/cpp/deferral-safety.js +86 -0
  797. package/dist/targets/cpp/direct-call-receivers.d.ts +37 -0
  798. package/dist/targets/cpp/direct-call-receivers.js +79 -0
  799. package/dist/targets/cpp/document.d.ts +74 -0
  800. package/dist/targets/cpp/document.js +106 -0
  801. package/dist/targets/cpp/emit-allocation.d.ts +60 -0
  802. package/dist/targets/cpp/emit-allocation.js +573 -0
  803. package/dist/targets/cpp/emit-arrays.d.ts +119 -0
  804. package/dist/targets/cpp/emit-arrays.js +1047 -0
  805. package/dist/targets/cpp/emit-binding-reference.d.ts +17 -0
  806. package/dist/targets/cpp/emit-binding-reference.js +120 -0
  807. package/dist/targets/cpp/emit-bindings.d.ts +158 -0
  808. package/dist/targets/cpp/emit-bindings.js +769 -0
  809. package/dist/targets/cpp/emit-buffers.d.ts +156 -0
  810. package/dist/targets/cpp/emit-buffers.js +573 -0
  811. package/dist/targets/cpp/emit-callable.d.ts +230 -0
  812. package/dist/targets/cpp/emit-callable.js +2154 -0
  813. package/dist/targets/cpp/emit-carrier-members.d.ts +406 -0
  814. package/dist/targets/cpp/emit-carrier-members.js +1224 -0
  815. package/dist/targets/cpp/emit-coercion.d.ts +26 -0
  816. package/dist/targets/cpp/emit-coercion.js +39 -0
  817. package/dist/targets/cpp/emit-context.d.ts +1882 -0
  818. package/dist/targets/cpp/emit-context.js +904 -0
  819. package/dist/targets/cpp/emit-dynamic-properties.d.ts +174 -0
  820. package/dist/targets/cpp/emit-dynamic-properties.js +1249 -0
  821. package/dist/targets/cpp/emit-equality.d.ts +39 -0
  822. package/dist/targets/cpp/emit-equality.js +472 -0
  823. package/dist/targets/cpp/emit-exceptions.d.ts +104 -0
  824. package/dist/targets/cpp/emit-exceptions.js +513 -0
  825. package/dist/targets/cpp/emit-field-owner.d.ts +4 -0
  826. package/dist/targets/cpp/emit-field-owner.js +27 -0
  827. package/dist/targets/cpp/emit-in.d.ts +31 -0
  828. package/dist/targets/cpp/emit-in.js +426 -0
  829. package/dist/targets/cpp/emit-instanceof.d.ts +19 -0
  830. package/dist/targets/cpp/emit-instanceof.js +601 -0
  831. package/dist/targets/cpp/emit-integers.d.ts +68 -0
  832. package/dist/targets/cpp/emit-integers.js +184 -0
  833. package/dist/targets/cpp/emit-iterator.d.ts +100 -0
  834. package/dist/targets/cpp/emit-iterator.js +1137 -0
  835. package/dist/targets/cpp/emit-json.d.ts +73 -0
  836. package/dist/targets/cpp/emit-json.js +823 -0
  837. package/dist/targets/cpp/emit-jsx.d.ts +74 -0
  838. package/dist/targets/cpp/emit-jsx.js +806 -0
  839. package/dist/targets/cpp/emit-mixed-binary.d.ts +51 -0
  840. package/dist/targets/cpp/emit-mixed-binary.js +60 -0
  841. package/dist/targets/cpp/emit-namespaces.d.ts +101 -0
  842. package/dist/targets/cpp/emit-namespaces.js +133 -0
  843. package/dist/targets/cpp/emit-narrowing.d.ts +738 -0
  844. package/dist/targets/cpp/emit-narrowing.js +3967 -0
  845. package/dist/targets/cpp/emit-native-equality.d.ts +3 -0
  846. package/dist/targets/cpp/emit-native-equality.js +14 -0
  847. package/dist/targets/cpp/emit-native-selection.d.ts +5 -0
  848. package/dist/targets/cpp/emit-native-selection.js +37 -0
  849. package/dist/targets/cpp/emit-object-tag.d.ts +4 -0
  850. package/dist/targets/cpp/emit-object-tag.js +108 -0
  851. package/dist/targets/cpp/emit-owned-record.d.ts +2 -0
  852. package/dist/targets/cpp/emit-owned-record.js +15 -0
  853. package/dist/targets/cpp/emit-presence.d.ts +78 -0
  854. package/dist/targets/cpp/emit-presence.js +376 -0
  855. package/dist/targets/cpp/emit-properties.d.ts +105 -0
  856. package/dist/targets/cpp/emit-properties.js +1640 -0
  857. package/dist/targets/cpp/emit-record-view.d.ts +20 -0
  858. package/dist/targets/cpp/emit-record-view.js +354 -0
  859. package/dist/targets/cpp/emit-return.d.ts +5 -0
  860. package/dist/targets/cpp/emit-return.js +237 -0
  861. package/dist/targets/cpp/emit-sum-widening.d.ts +10 -0
  862. package/dist/targets/cpp/emit-sum-widening.js +69 -0
  863. package/dist/targets/cpp/emit-tonumber.d.ts +38 -0
  864. package/dist/targets/cpp/emit-tonumber.js +177 -0
  865. package/dist/targets/cpp/emit-tostring.d.ts +92 -0
  866. package/dist/targets/cpp/emit-tostring.js +556 -0
  867. package/dist/targets/cpp/emit-typeof.d.ts +49 -0
  868. package/dist/targets/cpp/emit-typeof.js +317 -0
  869. package/dist/targets/cpp/emit-union-properties.d.ts +181 -0
  870. package/dist/targets/cpp/emit-union-properties.js +1281 -0
  871. package/dist/targets/cpp/emit.d.ts +45 -0
  872. package/dist/targets/cpp/emit.js +2225 -0
  873. package/dist/targets/cpp/error-types.d.ts +4 -0
  874. package/dist/targets/cpp/error-types.js +11 -0
  875. package/dist/targets/cpp/function-source-reads.d.ts +72 -0
  876. package/dist/targets/cpp/function-source-reads.js +148 -0
  877. package/dist/targets/cpp/host/atomics.d.ts +25 -0
  878. package/dist/targets/cpp/host/atomics.js +63 -0
  879. package/dist/targets/cpp/host/core-globals.d.ts +108 -0
  880. package/dist/targets/cpp/host/core-globals.js +158 -0
  881. package/dist/targets/cpp/host/dynamic-argument-members.d.ts +42 -0
  882. package/dist/targets/cpp/host/dynamic-argument-members.js +61 -0
  883. package/dist/targets/cpp/host/emit-host-arity.d.ts +139 -0
  884. package/dist/targets/cpp/host/emit-host-arity.js +232 -0
  885. package/dist/targets/cpp/host/emit-host-invoke.d.ts +91 -0
  886. package/dist/targets/cpp/host/emit-host-invoke.js +1105 -0
  887. package/dist/targets/cpp/host/emit-host-object.d.ts +55 -0
  888. package/dist/targets/cpp/host/emit-host-object.js +2206 -0
  889. package/dist/targets/cpp/host/emit-host-properties.d.ts +69 -0
  890. package/dist/targets/cpp/host/emit-host-properties.js +533 -0
  891. package/dist/targets/cpp/host/emit-host-reflect.d.ts +4 -0
  892. package/dist/targets/cpp/host/emit-host-reflect.js +180 -0
  893. package/dist/targets/cpp/host/emit-host-value.d.ts +120 -0
  894. package/dist/targets/cpp/host/emit-host-value.js +392 -0
  895. package/dist/targets/cpp/host/host-members.d.ts +505 -0
  896. package/dist/targets/cpp/host/host-members.js +819 -0
  897. package/dist/targets/cpp/host/host-method-aliases.d.ts +30 -0
  898. package/dist/targets/cpp/host/host-method-aliases.js +76 -0
  899. package/dist/targets/cpp/host/native-protocols.d.ts +47 -0
  900. package/dist/targets/cpp/host/native-protocols.js +183 -0
  901. package/dist/targets/cpp/host/object-protocol.d.ts +351 -0
  902. package/dist/targets/cpp/host/object-protocol.js +671 -0
  903. package/dist/targets/cpp/host-namespace-reads.d.ts +83 -0
  904. package/dist/targets/cpp/host-namespace-reads.js +113 -0
  905. package/dist/targets/cpp/manifest/capabilities.d.ts +39 -0
  906. package/dist/targets/cpp/manifest/capabilities.js +1484 -0
  907. package/dist/targets/cpp/manifest.d.ts +23 -0
  908. package/dist/targets/cpp/manifest.js +113 -0
  909. package/dist/targets/cpp/native-error-base.d.ts +38 -0
  910. package/dist/targets/cpp/native-error-base.js +74 -0
  911. package/dist/targets/cpp/native-narrowing-transport.d.ts +12 -0
  912. package/dist/targets/cpp/native-narrowing-transport.js +151 -0
  913. package/dist/targets/cpp/native-selection-helpers.d.ts +11 -0
  914. package/dist/targets/cpp/native-selection-helpers.js +46 -0
  915. package/dist/targets/cpp/native-symbol-keys.d.ts +8 -0
  916. package/dist/targets/cpp/native-symbol-keys.js +34 -0
  917. package/dist/targets/cpp/ownership.d.ts +43 -0
  918. package/dist/targets/cpp/ownership.js +38 -0
  919. package/dist/targets/cpp/owning-conversion-input.d.ts +14 -0
  920. package/dist/targets/cpp/owning-conversion-input.js +12 -0
  921. package/dist/targets/cpp/property-read-claims.d.ts +62 -0
  922. package/dist/targets/cpp/property-read-claims.js +23 -0
  923. package/dist/targets/cpp/property-store-claims.d.ts +62 -0
  924. package/dist/targets/cpp/property-store-claims.js +26 -0
  925. package/dist/targets/cpp/prototype/emit-prototype-array.d.ts +111 -0
  926. package/dist/targets/cpp/prototype/emit-prototype-array.js +602 -0
  927. package/dist/targets/cpp/prototype/emit-prototype-date.d.ts +133 -0
  928. package/dist/targets/cpp/prototype/emit-prototype-date.js +515 -0
  929. package/dist/targets/cpp/prototype/emit-prototype-error.d.ts +13 -0
  930. package/dist/targets/cpp/prototype/emit-prototype-error.js +54 -0
  931. package/dist/targets/cpp/prototype/emit-prototype-invoke.d.ts +34 -0
  932. package/dist/targets/cpp/prototype/emit-prototype-invoke.js +1003 -0
  933. package/dist/targets/cpp/prototype/emit-prototype-iterator.d.ts +47 -0
  934. package/dist/targets/cpp/prototype/emit-prototype-iterator.js +250 -0
  935. package/dist/targets/cpp/prototype/emit-prototype-promise.d.ts +46 -0
  936. package/dist/targets/cpp/prototype/emit-prototype-promise.js +143 -0
  937. package/dist/targets/cpp/prototype/emit-prototype-regexp.d.ts +335 -0
  938. package/dist/targets/cpp/prototype/emit-prototype-regexp.js +853 -0
  939. package/dist/targets/cpp/prototype/emit-prototype-string.d.ts +42 -0
  940. package/dist/targets/cpp/prototype/emit-prototype-string.js +252 -0
  941. package/dist/targets/cpp/prototype/prototype-method-reads.d.ts +25 -0
  942. package/dist/targets/cpp/prototype/prototype-method-reads.js +182 -0
  943. package/dist/targets/cpp/reactive-dependencies.d.ts +140 -0
  944. package/dist/targets/cpp/reactive-dependencies.js +432 -0
  945. package/dist/targets/cpp/reactive-origins.d.ts +44 -0
  946. package/dist/targets/cpp/reactive-origins.js +82 -0
  947. package/dist/targets/cpp/records.d.ts +277 -0
  948. package/dist/targets/cpp/records.js +2427 -0
  949. package/dist/targets/cpp/recursive-containers.d.ts +43 -0
  950. package/dist/targets/cpp/recursive-containers.js +144 -0
  951. package/dist/targets/cpp/regexp-types.d.ts +56 -0
  952. package/dist/targets/cpp/regexp-types.js +59 -0
  953. package/dist/targets/cpp/substrate.d.ts +125 -0
  954. package/dist/targets/cpp/substrate.js +12 -0
  955. package/dist/targets/cpp/translation-unit.d.ts +219 -0
  956. package/dist/targets/cpp/translation-unit.js +2063 -0
  957. package/dist/targets/cpp/types.d.ts +280 -0
  958. package/dist/targets/cpp/types.js +1135 -0
  959. package/dist/targets/cpp/virtual-methods.d.ts +95 -0
  960. package/dist/targets/cpp/virtual-methods.js +264 -0
  961. package/package.json +95 -0
  962. package/src/targets/cpp/runtime/gea_dynamic_proxy.h +824 -0
  963. package/src/targets/cpp/runtime/gea_eval.h +1376 -0
  964. package/src/targets/cpp/runtime/gea_native_class_prototype.h +44 -0
  965. package/src/targets/cpp/runtime/gea_runtime.h +29222 -0
  966. package/src/targets/cpp/runtime/gea_runtime_builtins.cpp +55 -0
@@ -0,0 +1,3967 @@
1
+ import { classRefTransportKind, constructorUpcastMember } from './class-ref-transport.js';
2
+ import { abiKey, arrayExtensionKey, containsUnresolved, isBooleanShapedMergeTarget, isCanonicalNumberPropertyKeyText, representationKey, walkRepresentation, carriesUndefined } from '../../representation/model.js';
3
+ import { transferOf } from '../../ir/transfer.js';
4
+ import { coercionText } from './emit-coercion.js';
5
+ import { structuralRecordViewText } from './emit-record-view.js';
6
+ import { createCppEmitBlockedError, cppConstructThunkName, cppThunkName, defineValue, isCppEmitBlockedError, isDeferredValue, operandText } from './emit-context.js';
7
+ import { booleanTestText } from './emit-presence.js';
8
+ import { recastedRecordToArrayText } from './emit-arrays.js';
9
+ import { cppAbiParameterType, cppAbiType, cppClassName, cppConstantLiteral, cppRecordFieldName, cppRecordFieldPresenceName, cppRecordStructName, cppResultTypeOf, cppStringLiteral, cppTypeOf, cppUndefinedIn, cppUndefinedValue } from './types.js';
10
+ import { cppRegExpNativeTypes, cppStringObjectNativeType } from './regexp-types.js';
11
+ import { widenedNativeSumText } from './emit-sum-widening.js';
12
+ import { nativeSelectionText } from './emit-native-selection.js';
13
+ import { narrowingReachesTarget } from '../../conversion/build.js';
14
+ /**
15
+ * `native-record-ref` (the String WRAPPER OBJECT, `new String(x)`) -> `string`,
16
+ * the one direction `convertedValueText` needs that neither `narrowedLoadText`
17
+ * nor `widenedStoreText` otherwise reaches: both read their `held`/`written`
18
+ * argument as a search through a union's arms or a dynamic box's tag, and a
19
+ * String object is neither.
20
+ *
21
+ * A short, deliberately duplicated twin of `emit-prototype-regexp.ts`'s
22
+ * `stringObjectToStringText` -- not imported, to avoid a cycle: that file
23
+ * already imports `convertedValueText` FROM here (for a property store's own
24
+ * threaded result, mirroring `emitNativeSidecarSet`), so this file cannot
25
+ * import back from it. Both read the identical one fact,
26
+ * ECMA-262 22.1.3.34/22.1.5.4's `[[StringData]]`, off the identical struct --
27
+ * see `gea::runtime::StringObject`'s doc comment in `runtime/gea_runtime.h`.
28
+ */
29
+ const stringObjectStringifyText = (source, text) => source.kind === 'native-record-ref' && source.native === cppStringObjectNativeType ? `${text}->value` : null;
30
+ /**
31
+ * Whether the C++ backend has a concrete reconciliation recipe for a promise
32
+ * payload. Promise-to-promise conversion is state adoption plus this payload
33
+ * conversion, so preflight and emission must ask the same authority rather
34
+ * than maintaining a second, inevitably drifting list of payload pairs.
35
+ */
36
+ export const promisePayloadConvertible = (source, target) => {
37
+ // A void TARGET payload has nowhere to put the source's value, and a void
38
+ // SOURCE payload has no `value()` to read -- except when the source payload
39
+ // is `never`, where the absence is the proof rather than the obstacle: a
40
+ // `Promise<never>` cannot fulfil, so the target's fulfilment channel is
41
+ // never written and only the rejection has to be carried across. This is
42
+ // the one authority both the registry's `recasting` entry and the
43
+ // `promise-payload` chain step ask, so the pair the census admits is the
44
+ // pair the printer renders.
45
+ //
46
+ // The second exception is `void` against `undefined`, which is one promise
47
+ // wearing two TypeScript types rather than two promises: ECMA-262 27.2.1.4
48
+ // fulfils a promise resolved with nothing with `undefined`, and this runtime
49
+ // elides that `undefined` entirely for `void` (`cppResultTypeOf`). So the
50
+ // unit pair reconciles by supplying or dropping the value, in either
51
+ // direction, while `void` against any richer payload stays a hole.
52
+ // `@hono/node-server`'s `listener.ts` needs exactly this: `responseViaCache`
53
+ // is declared `Promise<undefined | void>` and both callers `return` its
54
+ // promise from an async function whose own result is `Promise<void>`.
55
+ if (target.kind === 'void')
56
+ return isUnitPromisePayload(source);
57
+ if (source.kind === 'void')
58
+ return source.bottom === true || isUnitPromisePayload(target);
59
+ try {
60
+ return convertedValueText(source, target, 'gea_promise_payload') !== null;
61
+ }
62
+ catch (error) {
63
+ if (isCppEmitBlockedError(error))
64
+ return false;
65
+ throw error;
66
+ }
67
+ };
68
+ /** A promise payload that carries no information: `void`, and the `undefined` it is spelled as wherever a value is required. */
69
+ const isUnitPromisePayload = (payload) => payload.kind === 'void' || payload.kind === 'undefined';
70
+ /** `RegExpMatchArray` read through the `Array<string>` base it inherits. */
71
+ const regexpMatchArrayBaseText = (source, target, text) => source.kind === 'native-record-ref' &&
72
+ source.native === cppRegExpNativeTypes['match-result'] &&
73
+ source.ownership === 'shared-refcount' &&
74
+ target.kind === 'array-object' &&
75
+ target.ownership === 'shared-refcount' &&
76
+ target.element.kind === 'string'
77
+ ? `${cppTypeOf(target)}(${text})`
78
+ : null;
79
+ /** A failed candidate is not a failed search when another union arm can carry the value. */
80
+ const tryCandidateText = (build) => {
81
+ try {
82
+ return build();
83
+ }
84
+ catch (error) {
85
+ if (isCppEmitBlockedError(error))
86
+ return null;
87
+ throw error;
88
+ }
89
+ };
90
+ /** Whether `candidate` is `base` or descends from it in the emitted class layout. */
91
+ const isOrDescendsFrom = (ctx, candidate, base) => {
92
+ const seen = new Set();
93
+ for (let current = candidate; current !== null && !seen.has(current);) {
94
+ if (current === base)
95
+ return true;
96
+ seen.add(current);
97
+ current = ctx.classes.get(current)?.base ?? null;
98
+ }
99
+ return false;
100
+ };
101
+ /**
102
+ * A base-class carrier narrowed to a union of descendant classes.
103
+ *
104
+ * The physical carrier intentionally remains one `Ref<Base>`: allocation
105
+ * identity supplies the target union's discriminant, and checked class-family
106
+ * downcasts preserve the same native object. This is shared by cell and field
107
+ * reads because both are projections out of wider storage. An optional target
108
+ * preserves the source's absence around the projected descendant union.
109
+ */
110
+ export const classFamilyLoadText = (ctx, held, read, text) => {
111
+ const targetUnion = read.kind === 'optional' ? read.payload : read;
112
+ if (targetUnion.kind !== 'tagged-union' || targetUnion.arms.length === 0)
113
+ return null;
114
+ const heldClass = (() => {
115
+ if (held.kind === 'optional' && held.payload.kind === 'class-ref') {
116
+ return { source: held.payload, value: `(*${text})` };
117
+ }
118
+ if (held.kind === 'class-ref')
119
+ return { source: held, value: text };
120
+ if (read.kind === 'optional' || held.kind !== 'tagged-union')
121
+ return null;
122
+ const classes = held.arms.flatMap((arm, index) => (arm.value.kind === 'class-ref' ? [{ source: arm.value, index }] : []));
123
+ if (classes.length !== 1)
124
+ return null;
125
+ if (!held.arms.every((arm) => arm.value.kind === 'class-ref' || arm.value.kind === 'undefined' || arm.value.kind === 'null'))
126
+ return null;
127
+ const sole = classes[0];
128
+ return { source: sole.source, value: `${text}.get<${sole.index}>()` };
129
+ })();
130
+ if (heldClass === null)
131
+ return null;
132
+ const { source, value } = heldClass;
133
+ const arms = targetUnion.arms.map((arm) => arm.value);
134
+ if (!arms.every((arm) => arm.kind === 'class-ref' && arm.ownership === source.ownership && arm.ancestors.includes(source.declaration)))
135
+ return null;
136
+ const classes = arms;
137
+ // A target may retain both an ancestor and one of its descendants (Three's
138
+ // WebGLRenderTarget | WebGLCubeRenderTarget flow does). JS unions do not
139
+ // preserve which syntactic arm produced an object, so allocation identity
140
+ // supplies the canonical answer: test the most-specific family first and
141
+ // retain each arm's original target-union index when rebuilding it.
142
+ const ordered = classes
143
+ .map((arm, index) => ({ arm, index }))
144
+ .sort((left, right) => {
145
+ const leftBelowRight = isOrDescendsFrom(ctx, left.arm.declaration, right.arm.declaration);
146
+ const rightBelowLeft = isOrDescendsFrom(ctx, right.arm.declaration, left.arm.declaration);
147
+ if (leftBelowRight !== rightBelowLeft)
148
+ return leftBelowRight ? -1 : 1;
149
+ return left.index - right.index;
150
+ });
151
+ const unionType = cppTypeOf(targetUnion);
152
+ const armText = (arm, index) => `${unionType}::ofArm<${index}>(gea::host::downcastClassRef<${cppClassName(arm.declaration)}>(${value}))`;
153
+ const fallback = ordered[ordered.length - 1];
154
+ let projected = armText(fallback.arm, fallback.index);
155
+ for (let index = ordered.length - 2; index >= 0; index -= 1) {
156
+ const candidate = ordered[index];
157
+ const arm = candidate.arm;
158
+ const descendants = [...ctx.classes.keys()].filter((candidate) => isOrDescendsFrom(ctx, candidate, arm.declaration) && isOrDescendsFrom(ctx, candidate, source.declaration));
159
+ if (descendants.length === 0)
160
+ return null;
161
+ const test = `gea::host::hasNativeClassLayoutRef<${descendants.map(cppClassName).join(', ')}>(${value})`;
162
+ projected = `${test} ? ${armText(arm, candidate.index)} : (${projected})`;
163
+ }
164
+ if (classes.length > 1)
165
+ projected = `(${projected})`;
166
+ if (read.kind !== 'optional')
167
+ return projected;
168
+ const targetType = cppTypeOf(read);
169
+ if (held.kind === 'optional') {
170
+ if (held.absence !== read.absence)
171
+ return null;
172
+ return `(${text}.has_value() ? ${targetType}(${projected}) : ${targetType}())`;
173
+ }
174
+ if (held.kind !== 'class-ref')
175
+ return null;
176
+ if (read.absence === 'null')
177
+ return `(${text} ? ${targetType}(${projected}) : ${targetType}())`;
178
+ return `${targetType}(${projected})`;
179
+ };
180
+ /**
181
+ * The live arm of a tagged union, as an expression.
182
+ *
183
+ * More than one arm can carry the target's C++ type -- `A | B | C` where all
184
+ * three lower to `std::string` -- so the load dispatches on the discriminant
185
+ * rather than assuming a position. With one candidate it is a plain `get`, and
186
+ * the chain collapses to nothing; with several it tests each in turn and falls
187
+ * through to the last without testing it, because the narrowing already proved
188
+ * one of them is live and a test with no else has nothing to return.
189
+ */
190
+ const taggedUnionArmText = (union, target, text) => {
191
+ const targetKey = representationKey(target);
192
+ const exact = union.arms
193
+ .map((arm, index) => ({ arm, index }))
194
+ .filter((entry) => representationKey(entry.arm.value) === targetKey)
195
+ .map((entry) => ({ index: entry.index, text: `${text}.get<${entry.index}>()` }));
196
+ // An arm that CONVERTS to the target answers too. An exact arm is the
197
+ // cheapest answer for its own discriminant, but its existence cannot erase
198
+ // a different live arm which is structurally assignable to the same target.
199
+ // Hono's RegExpRouter exposed this: a truthy value is either `string[]` or
200
+ // `RegExpMatchArray`; both are `Array<string>`, yet preferring the exact arm
201
+ // emitted an unconditional `get<0>()` and crashed on a match-result arm.
202
+ //
203
+ // Hono's `Hono.fetch` is another converting case: the cell is
204
+ // `dynamic | optional(record)` and the read is a
205
+ // `native-record-ref`, so the live arm is the BOX and the load is the
206
+ // unboxing check `unboxedLoadText` already renders. The narrowing that
207
+ // licensed this load proved one arm is live; the discriminant chain below
208
+ // then picks whichever it is, exactly as it does for several exact arms.
209
+ // An arm the target lives INSIDE answers too, on the same terms as a
210
+ // converting arm: `undefined | null | (string|number)` narrowed to `number`
211
+ // reads the `present` arm and then narrows THAT, which is one more level than
212
+ // an arm-key search can see. `narrowedUnionSubsetText` already recurses this
213
+ // way for a sub-union target; this is the same step for a flat one, and it
214
+ // terminates because each call strips one level of nesting.
215
+ const candidates = union.arms.flatMap((arm, index) => {
216
+ const found = exact.find((entry) => entry.index === index);
217
+ if (found)
218
+ return [found];
219
+ const slot = `${text}.get<${index}>()`;
220
+ const converted = tryCandidateText(() => convertedValueText(arm.value, target, slot) ?? narrowedLoadText(arm.value, target, slot));
221
+ return converted === null ? [] : [{ index, text: converted }];
222
+ });
223
+ const last = candidates[candidates.length - 1];
224
+ // A union target that some arm only WIDENS into is a recast, not a
225
+ // narrowing: the narrowing proved nothing about which arm is live, so every
226
+ // arm needs its home or the read would rebuild a live arm as another.
227
+ const partialWidening = target.kind === 'tagged-union' &&
228
+ candidates.some((candidate) => !exact.includes(candidate)) &&
229
+ union.arms.some((arm, index) => arm.value.kind === 'record' && !candidates.some((candidate) => candidate.index === index));
230
+ if (!last || partialWidening) {
231
+ throw createCppEmitBlockedError(`conversion:${representationKey(union)}->${targetKey}`, `narrows tagged union "${representationKey(union)}" to "${targetKey}", which none of its arms carries`);
232
+ }
233
+ let result = last.text;
234
+ for (const entry of candidates.slice(0, -1).reverse()) {
235
+ result = `${text}.is<${entry.index}>() ? ${entry.text} : (${result})`;
236
+ }
237
+ return candidates.length > 1 ? `(${result})` : result;
238
+ };
239
+ /**
240
+ * A tagged union narrowed to a SUB-union -- fewer arms, not one flat arm.
241
+ *
242
+ * `taggedUnionArmText` above answers "narrowed to one specific representation";
243
+ * this answers the sibling question control flow proves just as often: `h
244
+ * instanceof Headers` inside `const h = init.headers` (a `Headers |
245
+ * Record<string,string> | [string,string][]` cell) leaves the `else` branch
246
+ * holding `Record<string,string> | [string,string][]` -- still a union, just
247
+ * one arm short. Neither `narrowedLoadText`'s optional-of-union case nor
248
+ * `taggedUnionArmText` covers it: the first is about presence, the second
249
+ * searches for arms sharing the target's WHOLE C++ type, which no single arm
250
+ * of a genuinely narrower union has.
251
+ *
252
+ * Every one of the narrower union's arms must have a same-shaped home among
253
+ * the wider union's arms (matched by `representationKey`, not by C++ type
254
+ * alone -- two structurally different arms can still share a C++ type, and
255
+ * conflating them would silently pick the wrong one) -- `null` otherwise,
256
+ * which is a real hole rather than a missing recipe. Each live arm rebuilds at
257
+ * its NEW index with `TaggedUnion<...>::ofArm<Index>`, the identical
258
+ * materializer a value narrowed into a fresh union already uses (`conversions.
259
+ * ts`'s widening/recasting entries) -- this is that same construction, tested
260
+ * over the WIDER union's own discriminant instead of over a single known
261
+ * value.
262
+ */
263
+ const narrowedUnionSubsetText = (inner, read, text) => {
264
+ const pairs = [];
265
+ for (const [targetIndex, arm] of read.arms.entries()) {
266
+ const heldIndex = inner.arms.findIndex((candidate) => representationKey(candidate.value) === representationKey(arm.value));
267
+ if (heldIndex >= 0) {
268
+ pairs.push({ heldIndex, targetIndex, load: null });
269
+ continue;
270
+ }
271
+ // An arm the held arm NARROWS INTO, rather than one it equals. A narrowing
272
+ // does not stop at the top: `typeof x === 'string'` in the else branch of
273
+ // `BodyInit | null | undefined` leaves the same three-arm outer shape with
274
+ // a smaller inner union in its `present` arm, and matching arms by key
275
+ // alone reads that as a union with no such arm at all. Asking this same
276
+ // function one level in is what makes the outer match, and it terminates
277
+ // because each step strips one level of nesting.
278
+ const nested = inner.arms
279
+ .map((candidate, index) => ({
280
+ index,
281
+ load: tryCandidateText(() => narrowedLoadText(candidate.value, arm.value, `${text}.get<${index}>()`))
282
+ }))
283
+ .find((entry) => entry.load !== null);
284
+ if (!nested?.load)
285
+ return null;
286
+ pairs.push({ heldIndex: nested.index, targetIndex, load: nested.load });
287
+ }
288
+ const last = pairs[pairs.length - 1];
289
+ if (!last)
290
+ return null;
291
+ // THE HELD INDEX IS THE DISCRIMINANT, so two target arms may not share one.
292
+ //
293
+ // The chain below tests `text.is<heldIndex>()` to pick a target arm, which is
294
+ // only a discriminant while each target arm comes from a DIFFERENT held arm.
295
+ // Exact homes always do -- two distinct arm keys cannot both equal one arm's
296
+ // -- but the nested search above can land several target arms on the same
297
+ // held arm, and then the first test wins every time: `string | number |
298
+ // boolean | null | undefined` narrowed past its two absences resolved all
299
+ // three of `string`, `number` and `boolean` to held arm 2 (the nested
300
+ // `present` union), and the emitted chain read `is<2>() ? ofArm<0>(...) :
301
+ // (is<2>() ? ofArm<1>(...) : ...)`, which always answered `string`. It
302
+ // compiled, certified, and printed `string:` for the number 7.
303
+ //
304
+ // When they collide, the read does not live across the arms at all -- it
305
+ // lives INSIDE one, and the whole narrowing is that one arm's own, one level
306
+ // down, where its own discriminant is in scope. That recursion is the answer;
307
+ // anything left over refuses rather than emitting a chain whose test cannot
308
+ // decide.
309
+ const collides = new Set(pairs.map((pair) => pair.heldIndex)).size !== pairs.length;
310
+ if (collides) {
311
+ for (const [index, candidate] of inner.arms.entries()) {
312
+ const slot = `${text}.get<${index}>()`;
313
+ // `narrowedLoadText` answers `null` for "nothing to load", which for an
314
+ // arm that already carries exactly the read IS the answer: the arm's own
315
+ // value. Asked as an equality first so that `null` is never mistaken for
316
+ // "this arm cannot".
317
+ if (representationKey(candidate.value) === representationKey(read))
318
+ return slot;
319
+ // Only an arm that is itself a sum can hold the whole read. A plain
320
+ // member "loads" into the read by widening, and taking it would rebuild
321
+ // every live arm as that one: `Promise<{done: true} | {done: false,
322
+ // value: Uint8Array}>` into its `value: unknown` twin read `.get<0>()`
323
+ // with the chunk arm live.
324
+ if (candidate.value.kind !== 'tagged-union' && candidate.value.kind !== 'optional')
325
+ continue;
326
+ const load = narrowedLoadText(candidate.value, read, slot);
327
+ if (load !== null)
328
+ return load;
329
+ }
330
+ return null;
331
+ }
332
+ // The same one-time binding `recastedUnionText` makes, for the same reason:
333
+ // this chain is what a NESTED union arm reaches through (`homeOf` there
334
+ // asks `convertedValueText`, which lands here for an inner union narrowed
335
+ // one level down), and it spelled the ~1.3KB target type and the source
336
+ // expression once per arm -- 928 inner spellings on one three.js line after
337
+ // the outer recast alone was bound. A single-pair chain keeps the direct
338
+ // spelling: there is nothing to share.
339
+ const multiPair = pairs.length > 1;
340
+ const boundText = multiPair ? recastUnionSourceName : text;
341
+ const targetType = multiPair ? recastUnionAliasName : cppTypeOf(read);
342
+ const armText = (pair) => `${targetType}::ofArm<${pair.targetIndex}>(${pair.load ?? `${boundText}.get<${pair.heldIndex}>()`})`;
343
+ let result = armText(last);
344
+ for (const pair of pairs.slice(0, -1).reverse()) {
345
+ result = `${boundText}.is<${pair.heldIndex}>() ? ${armText(pair)} : (${result})`;
346
+ }
347
+ if (!multiPair)
348
+ return result;
349
+ // The source arrives as the lambda's PARAMETER, spelled with its own type
350
+ // (an `auto` parameter makes every `.is<k>()` a dependent template name),
351
+ // never as a `const auto&` declared in its body: a nested chain's `text` is the outer chain's own
352
+ // `gea_recast_source.get<2>()`, and a body-local of the same name would be
353
+ // in scope from its own declarator, so the initializer would read the
354
+ // uninitialized inner binding instead of the outer one. An argument is
355
+ // evaluated in the caller's scope, where the outer name is the live one.
356
+ return (`([&](const ${cppTypeOf(inner)}& ${recastUnionSourceName}) -> ${cppTypeOf(read)} { using ${recastUnionAliasName} = ${cppTypeOf(read)}; ` +
357
+ `return ${result}; }(${text}))`);
358
+ };
359
+ /**
360
+ * The load a narrowing licenses, or `null` when the two carriers are the same
361
+ * and there is nothing to load.
362
+ *
363
+ * Returning `null` for "no narrowing needed" and throwing for "no narrowing
364
+ * possible" keeps the two apart: the first is the ordinary case, the second is
365
+ * a program this compiler already knows is wrong.
366
+ */
367
+ export const narrowedLoadText = (held, read, text) => {
368
+ if (representationKey(held) === representationKey(read))
369
+ return null;
370
+ const remappedSet = genericFunctionSetWideningText(held, read, text);
371
+ if (remappedSet !== null)
372
+ return remappedSet;
373
+ // Two structural views of one host facade still hold the same native value.
374
+ // `Buffer` and `Uint8Array` expose different TypeScript member sets while
375
+ // both map to the host's one BufferFacade C++ type. Equal native identity,
376
+ // ownership, and physical type prove this is an alias-preserving read.
377
+ if (held.kind === 'native-record-ref' &&
378
+ read.kind === 'native-record-ref' &&
379
+ held.native !== null &&
380
+ held.native === read.native &&
381
+ held.ownership === read.ownership &&
382
+ cppTypeOf(held) === cppTypeOf(read))
383
+ return text;
384
+ const unwrapped = held.kind === 'optional' ? `(*${text})` : text;
385
+ const inner = held.kind === 'optional' ? held.payload : held;
386
+ if (representationKey(inner) === representationKey(read))
387
+ return unwrapped;
388
+ // The same array read as the interface that extends it (`elements` of
389
+ // `readonly T[] | undefined`, proven present and then `isNodeArray`): one
390
+ // C++ type either way -- the fields an extension adds live beside the
391
+ // elements -- so the load is the payload itself, exactly as
392
+ // `conversions.ts`'s `gea::ArrayObject::identity` recast states for the
393
+ // bare pair.
394
+ if (sameArrayUpToExtension(inner, read))
395
+ return unwrapped;
396
+ // The guard proved the cell ABSENT: `if ( dstArray === null )` and every read
397
+ // inside that branch. There is nothing in the cell to load -- the read's
398
+ // carrier has one inhabitant -- so this produces the constant, and must be
399
+ // asked BEFORE the arm search below, which answers `null` ("nothing to
400
+ // narrow") for this pair and would let the caller emit the whole
401
+ // `gea::Optional<T>` where a `std::nullptr_t` is declared.
402
+ if (held.kind === 'optional' && read.kind === held.absence)
403
+ return read.kind === 'null' ? 'nullptr' : cppUndefinedValue;
404
+ // Reading a cell as `dynamic` is not a narrowing at all -- it is the box, and
405
+ // the box accepts every carrier. Answered by the widening this file already
406
+ // owns rather than by searching the arms for a `dynamic` one, which no union
407
+ // has: a sum's arms are the declared types, and `any` is not one of them.
408
+ // Without this, a `string | symbol` cell read into an `unknown` parameter
409
+ // refused with "narrows a tagged union to dynamic, which none of its arms
410
+ // carries" -- an accurate sentence about the wrong question.
411
+ //
412
+ // The HELD carrier goes in whole, never `inner`/`unwrapped`: an optional
413
+ // read as `any` still has its presence to state, and `widenedStoreText`'s
414
+ // dynamic branch owns that (`has_value() ? box(payload) : box(absence)`).
415
+ // Handing it the unwrapped payload boxed `(*cell)` unconditionally, so a
416
+ // `number | null` holding `null` reached an `...args: any[]` listener as
417
+ // `Tag::Number` over whatever the dereference found -- node-compat's
418
+ // cluster 'exit' event reported `code=0 signal=` for a worker Node reports
419
+ // as `code=null signal=SIGTERM`. Certified, compiled, wrong.
420
+ if (read.kind === 'dynamic')
421
+ return widenedStoreText(read, held, text);
422
+ // And the mirror: a cell the program declared `any` read at a place the
423
+ // checker narrowed. `if (a instanceof Error) throw a` reads the same cell as
424
+ // an Error record, and that read is the box's tag-and-payload-type check --
425
+ // the narrowing's claim ENFORCED, not assumed. A union's arm search below
426
+ // cannot answer it, because a box has no arms.
427
+ if (inner.kind === 'dynamic')
428
+ return unboxedLoadText(read, unwrapped);
429
+ // Presence narrowing and class widening can happen in the same read. A
430
+ // guarded `Derived | undefined` passed to a `Base` parameter is physically
431
+ // the present `Derived` handle followed by Ref's ordinary upcast. Keeping
432
+ // those as one recipe matters because the exact-payload check above only
433
+ // answers `T | undefined -> T`; it cannot answer a different base carrier.
434
+ //
435
+ // This direction is a store-style widening, so it is proved from the
436
+ // source's ancestry. The downcast immediately below proves the inverse
437
+ // direction from the target's ancestry and remains the checked narrowing.
438
+ if (inner.kind === 'class-ref' &&
439
+ read.kind === 'class-ref' &&
440
+ inner.ownership === read.ownership &&
441
+ inner.ancestors.includes(read.declaration)) {
442
+ return `${cppTypeOf(read)}(${unwrapped})`;
443
+ }
444
+ // A program-class handle read at a DERIVED class -- `part instanceof Mesh`
445
+ // then `part.castShadow = true` on a `traverse` callback's `Object3D`.
446
+ // `gea::Ref<Base>` and `gea::Ref<Derived>` are different C++ types, so the
447
+ // narrowed read needs a cast even though nothing about the object changes:
448
+ // `downcastClassRef` (gea_runtime.h) is `staticCast` plus the one thing this
449
+ // call site can be wrong about on its own, a `static_assert` that `Derived`
450
+ // really descends from `Base`.
451
+ //
452
+ // Only ever the DOWNCAST direction, and it is CHECKED here rather than left
453
+ // to the `static_assert`: this function is also the arm search
454
+ // `narrowedUnionSubsetText` runs over EVERY arm of a union looking for a
455
+ // home, so answering a downcast for two unrelated classes would put a text
456
+ // that cannot compile into a branch the dispatch never takes. The carrier
457
+ // states its own ancestry (`Representation`'s `class-ref`), so the question
458
+ // is answerable without a policy to thread. A read at an ANCESTOR is a
459
+ // widening and `widenedStoreText` answers it with `Ref`'s own converting
460
+ // constructor. Ownership must agree -- a handle and a by-value struct of the
461
+ // same class are different physical things, not a heritage question.
462
+ if (inner.kind === 'class-ref' &&
463
+ read.kind === 'class-ref' &&
464
+ inner.ownership === read.ownership &&
465
+ read.ancestors.includes(inner.declaration)) {
466
+ return `gea::host::downcastClassRef<${cppClassName(read.declaration)}>(${unwrapped})`;
467
+ }
468
+ // A cell that provably holds nothing but absence, read at a place declared
469
+ // wider. hono's `#dispatch` binds `env: E['Bindings']` -- an indexed access
470
+ // through a type parameter's constraint that this program never instantiates
471
+ // -- so its cell is placed `undefined` while every read of it is the declared
472
+ // optional. A read cannot be narrower than the cell here; it is a widening,
473
+ // and the store direction already knows the answer is the empty optional.
474
+ if ((inner.kind === 'undefined' || inner.kind === 'null') && read.kind === 'optional' && read.absence === inner.kind) {
475
+ return widenedStoreText(read, inner, unwrapped);
476
+ }
477
+ if (inner.kind !== 'tagged-union')
478
+ return null;
479
+ // A join may preserve another join (or an optional) as one physical arm.
480
+ // Search that nested carrier before trying to compare the outer arm list
481
+ // with the read's list. Otherwise a read of the preserved inner union is
482
+ // mistaken for a sub-union assembled from several OUTER arms, and a read of
483
+ // an absence carried inside an optional arm is reported as having no arm at
484
+ // all. Control-flow narrowing already proves the selected leaf is live; the
485
+ // recursion below only renders the required sequence of `get`/`*` loads.
486
+ const nested = nestedArmLoadText(inner, read, unwrapped);
487
+ if (nested !== null)
488
+ return nested;
489
+ // A read that is still optional narrowed the arm without proving presence, so
490
+ // the presence test survives into the load: absent stays absent, and present
491
+ // reads the arm the narrowing named. The empty branch is a default-constructed
492
+ // optional rather than a copy of the cell, because the cell's optional holds
493
+ // the wrong payload type by construction -- that is the whole difference this
494
+ // load exists to express.
495
+ if (held.kind === 'optional' && read.kind === 'optional') {
496
+ const narrowed = cppTypeOf(read);
497
+ // The payload itself can still be a sub-union -- `if (x !== undefined &&
498
+ // typeof x !== 'number')` proves both presence and one excluded arm in a
499
+ // single guard, leaving `T | U | undefined` narrowed to `T | U`, still
500
+ // wrapped in the same optional. `taggedUnionArmText` searches for one arm
501
+ // whose WHOLE representation matches the target and has nothing to find
502
+ // when the target is a sub-union rather than a flat arm.
503
+ const armText = read.payload.kind === 'tagged-union'
504
+ ? (narrowedUnionSubsetText(inner, read.payload, unwrapped) ?? recastedUnionText(inner, read.payload, unwrapped))
505
+ : taggedUnionArmText(inner, read.payload, unwrapped);
506
+ if (armText === null) {
507
+ throw createCppEmitBlockedError(`conversion:${representationKey(inner)}->${representationKey(read.payload)}`, `narrows an optional tagged union carrying "${representationKey(inner)}" to "${representationKey(read.payload)}", ` +
508
+ 'which none of its arms carries');
509
+ }
510
+ return `(${text}.has_value() ? ${narrowed}(${armText}) : ${narrowed}())`;
511
+ }
512
+ // A union read as an OPTIONAL: the read spends absence on one flag where the
513
+ // union spends it on an arm. `f(x: T | null = null)`'s body binds `T | null`
514
+ // while its slot is `T | null | undefined`, so every such body does this
515
+ // once, at the merge that chose between the argument and the default.
516
+ //
517
+ // The arms not named here are proven dead by the narrowing that licensed
518
+ // this load -- the same proof every other branch of this function reads
519
+ // rather than re-derives.
520
+ if (read.kind === 'optional') {
521
+ const absentIndex = inner.arms.findIndex((arm) => arm.value.kind === read.absence);
522
+ if (absentIndex >= 0) {
523
+ const narrowed = cppTypeOf(read);
524
+ return `(${unwrapped}.is<${absentIndex}>() ? ${narrowed}() : ${narrowed}(${taggedUnionArmText(inner, read.payload, unwrapped)}))`;
525
+ }
526
+ // No arm is the bare absent literal, because absence lives INSIDE another
527
+ // arm's own optional. hono's `compose` binds `let handler` to a
528
+ // `Function | (Next | undefined)` cell and reads it back as `Function |
529
+ // undefined`: the union's arms are the record and an
530
+ // `optional(callable)`, and the read's payload is the record alone.
531
+ //
532
+ // So the payload's own arm is the whole test: live means present, and
533
+ // every other arm is absence -- the arms not named here being proven dead
534
+ // by the narrowing that licensed this load, the same proof the branch
535
+ // above reads rather than re-derives.
536
+ const payloadIndex = inner.arms.findIndex((arm) => representationKey(arm.value) === representationKey(read.payload));
537
+ if (payloadIndex >= 0) {
538
+ const narrowed = cppTypeOf(read);
539
+ return `(${unwrapped}.is<${payloadIndex}>() ? ${narrowed}(${unwrapped}.get<${payloadIndex}>()) : ${narrowed}())`;
540
+ }
541
+ }
542
+ // The narrower-union case: `read` is still a union, just missing one or more
543
+ // of `inner`'s arms (proven dead by the narrowing that licensed this load),
544
+ // rather than one flat representation `taggedUnionArmText` below can search
545
+ // for. `narrowedUnionSubsetText` covers both the plain case (`inner` held no
546
+ // optional to begin with) and the presence-plus-arm-narrowing case just
547
+ // handled above falling through here when `read` itself is not `optional`
548
+ // (a destructured element proven both present and narrowed in one step).
549
+ if (read.kind === 'tagged-union') {
550
+ const subset = narrowedUnionSubsetText(inner, read, unwrapped);
551
+ if (subset !== null)
552
+ return subset;
553
+ // And the other direction: a read WIDER than the cell. A narrowing does not
554
+ // only ever shrink a union -- the cell's placement is the union of what the
555
+ // program writes to it, and a read's carrier is the declared type at that
556
+ // site, so a cell every writer narrows still gets read at its full declared
557
+ // width. hono's `#newResponse` is the case: the cell holds
558
+ // `Response | ResponseInit` and the read wants
559
+ // `StatusCode | Response | ResponseInit`. Every arm the cell can hold is an
560
+ // arm of the read, which is exactly what `recastedUnionText` proves, and
561
+ // the arms the read has and the cell does not are simply never live.
562
+ const widened = recastedUnionText(inner, read, unwrapped);
563
+ if (widened !== null)
564
+ return widened;
565
+ }
566
+ return taggedUnionArmText(inner, read, unwrapped);
567
+ };
568
+ /**
569
+ * The load for a target that is an arm of an ARM.
570
+ *
571
+ * A join nests: `string | Blob | URLSearchParams | null | undefined` derives as
572
+ * an outer union over the two absence arms and an inner union over the three
573
+ * value arms, so narrowing it to `string` reads through two `get`s and not one.
574
+ * `widenedStoreText` already walks exactly this nesting in the store direction
575
+ * ("A join can nest", below); this is its mirror, and without it the load
576
+ * refused with "narrows a tagged union to string, which none of its arms
577
+ * carries" -- an accurate sentence about the outer union only.
578
+ *
579
+ * `node-compat`'s `new Request(url, init)` is the case: `typeof init.body ===
580
+ * 'string'` narrows `BodyInit | null | undefined` to `string`.
581
+ *
582
+ * The discriminant is tested whenever more than one arm can answer, exactly as
583
+ * `taggedUnionArmText` does for the flat case, and for the same reason: with
584
+ * one candidate the narrowing already proved which arm is live, and a test with
585
+ * no else has nothing to return.
586
+ */
587
+ const nestedArmLoadText = (inner, read, text) => {
588
+ const readKey = representationKey(read);
589
+ const nestedLoad = (held, at) => {
590
+ if (representationKey(held) === readKey)
591
+ return at;
592
+ // A leaf can satisfy a wider nested read through a real carrier
593
+ // conversion. Hono's RegExpRouter reads an optional `Array<string>` from
594
+ // an optional union whose leaves are `string[]` and `RegExpMatchArray`:
595
+ // neither leaf equals the OPTIONAL target, but both convert to it. Looking
596
+ // only for exact nested keys found the first leaf and emitted its `get`
597
+ // unconditionally, crashing when the second discriminant was live.
598
+ if (held.kind !== 'optional' && held.kind !== 'tagged-union') {
599
+ const converted = tryCandidateText(() => convertedValueText(held, read, at));
600
+ if (converted !== null)
601
+ return converted;
602
+ }
603
+ if (held.kind === 'optional') {
604
+ if (read.kind === held.absence)
605
+ return read.kind === 'null' ? 'nullptr' : cppUndefinedValue;
606
+ if (read.kind === 'optional' && read.absence === held.absence) {
607
+ const payload = nestedLoad(held.payload, `(*${at})`);
608
+ if (payload === null)
609
+ return null;
610
+ const targetType = cppTypeOf(read);
611
+ return `(${at}.has_value() ? ${targetType}(${payload}) : ${targetType}())`;
612
+ }
613
+ return nestedLoad(held.payload, `(*${at})`);
614
+ }
615
+ if (held.kind !== 'tagged-union')
616
+ return null;
617
+ if (read.kind === 'tagged-union') {
618
+ const narrowed = tryCandidateText(() => narrowedUnionSubsetText(held, read, at));
619
+ if (narrowed !== null)
620
+ return narrowed;
621
+ }
622
+ const candidates = held.arms.flatMap((arm, index) => {
623
+ const loaded = nestedLoad(arm.value, `${at}.get<${index}>()`);
624
+ return loaded === null ? [] : [{ index, loaded }];
625
+ });
626
+ const last = candidates[candidates.length - 1];
627
+ if (!last)
628
+ return null;
629
+ let dispatched = last.loaded;
630
+ for (const candidate of candidates.slice(0, -1).reverse()) {
631
+ dispatched = `${at}.is<${candidate.index}>() ? ${candidate.loaded} : (${dispatched})`;
632
+ }
633
+ return candidates.length > 1 ? `(${dispatched})` : dispatched;
634
+ };
635
+ const candidates = [];
636
+ let converted = false;
637
+ for (const [index, arm] of inner.arms.entries()) {
638
+ const armText = `${text}.get<${index}>()`;
639
+ const load = nestedLoad(arm.value, armText);
640
+ if (load === null)
641
+ continue;
642
+ candidates.push({ index, text: load });
643
+ if (arm.value.kind !== 'optional' && arm.value.kind !== 'tagged-union' && representationKey(arm.value) !== readKey)
644
+ converted = true;
645
+ }
646
+ const last = candidates[candidates.length - 1];
647
+ if (!last)
648
+ return null;
649
+ // A flat arm that only CONVERTS into the read proves nothing about which arm
650
+ // is live, so dispatching on it is sound only when every arm has a home: a
651
+ // `{done: true} | {done: false, value: Uint8Array}` cell read as its
652
+ // `value: unknown` twin found a home for the first arm alone and read it
653
+ // unconditionally, with the chunk arm live.
654
+ if (converted && candidates.length !== inner.arms.length)
655
+ return null;
656
+ let result = last.text;
657
+ for (const candidate of candidates.slice(0, -1).reverse()) {
658
+ result = `${text}.is<${candidate.index}>() ? ${candidate.text} : (${result})`;
659
+ }
660
+ return candidates.length > 1 ? `(${result})` : result;
661
+ };
662
+ /**
663
+ * The store a widening needs, or `null` when the value can be assigned as it
664
+ * stands.
665
+ *
666
+ * This is the mirror of `narrowedLoadText` and it exists for the same reason:
667
+ * `const x: string | number = 3` puts a `double` into a `TaggedUnion`, and the
668
+ * language really does widen there -- the union is the declared carrier and the
669
+ * initializer is one arm of it.
670
+ *
671
+ * `gea::Optional<T>` needs nothing: it declares a converting assignment from
672
+ * `T`, so a present value assigns straight in and the presence flag follows.
673
+ * `gea::TaggedUnion` deliberately declares no such thing, because there is no
674
+ * one answer -- two arms can share a C++ type, and an implicit conversion would
675
+ * have to pick one silently. `ofArm<Index>` is where that choice is stated, and
676
+ * the index is the arm's position in the checker's own order, the same order
677
+ * the load reads back.
678
+ *
679
+ * The first arm carrying the written carrier is the one chosen. When several
680
+ * do, they are indistinguishable at runtime by construction -- the load
681
+ * dispatches over exactly that set and reads the same C++ type from any of them
682
+ * -- so the choice is not observable. What would be observable is choosing an
683
+ * arm that does *not* carry it, which is what returning `null` prevents.
684
+ */
685
+ /**
686
+ * The `gea::Value::Tag` a concrete representation boxes under when it is
687
+ * written into a `dynamic` cell -- the widening direction `conversion/
688
+ * derive.ts`'s narrowing-only algebra has no answer for (see citations.md
689
+ * finding 1). `null` for a carrier this compiler does not yet box (a
690
+ * container, a proxy, an existing `dynamic` itself, which the
691
+ * `representationKey` equality check above already short-circuits): an
692
+ * omission to extend deliberately later, not to guess at here.
693
+ *
694
+ * Exported so `targets/cpp/conversions.ts`'s `widening` registry entry can
695
+ * ask the identical question when the conversion graph -- not just a
696
+ * `convert` this function already renders -- needs to know whether boxing a
697
+ * given carrier is a real, installed capability. One answer, asked from both
698
+ * the preflight authority and the emission authority, rather than a second
699
+ * table naming the same tags that could drift from this one.
700
+ */
701
+ export const dynamicTagFor = (representation) => {
702
+ switch (representation.kind) {
703
+ case 'null':
704
+ return 'Null';
705
+ case 'undefined':
706
+ return 'Undefined';
707
+ case 'string':
708
+ return 'String';
709
+ // `gea::Symbol` is an id, and `Value::Tag` has a state for it. Missing here
710
+ // rather than deliberately absent: every `symbol`-carrying union refused to
711
+ // box at all, which is what `EventName = string | symbol` -- node's own
712
+ // event-name type -- is made of.
713
+ case 'symbol':
714
+ return 'Symbol';
715
+ case 'scalar':
716
+ return representation.domain === 'boolean' ? 'Boolean' : representation.domain === 'bigint' ? 'BigInt' : 'Number';
717
+ case 'class-ref':
718
+ case 'record':
719
+ case 'record-with-index':
720
+ case 'native-record-ref':
721
+ case 'native-handle':
722
+ case 'array-object':
723
+ case 'dictionary':
724
+ // A source-declared any/unknown boundary retains the collection's native
725
+ // handle, including its key/value types and object identity.
726
+ case 'keyed-collection':
727
+ // ECMA-262 25.1's `ArrayBuffer` is a JS Object exactly like the typed
728
+ // array below it -- `typeof` answers `"object"`, `cppTypeOf` already
729
+ // spells it `gea::Ref<gea::ArrayBuffer>` (`gea::ArrayBuffer` being the
730
+ // one monomorphic `std::vector<std::uint8_t>` alias, so there is only
731
+ // ever one payload address to record, never one per element domain the
732
+ // way `typed-array` needs). `Value::box` records that address as
733
+ // `payloadType()` the identical way it does for every other `Ref<...>`
734
+ // in this list. Missing here meant an `ArrayBuffer` value -- hono's
735
+ // `Context.body`'s own `Data` union carries one -- could never widen
736
+ // into a `dynamic` cell, the same silent gap `typed-array`'s own
737
+ // addition just above closed for typed arrays, blocking both this
738
+ // widen and the reverse read (`unboxedLoadText`, below) for the
739
+ // identical reason.
740
+ case 'array-buffer':
741
+ case 'shared-array-buffer':
742
+ // A `gea::TypedArray<T>` is a JS Object like the rest of this list --
743
+ // ECMA-262 typeof answers `"object"` for it too (`emit-typeof.ts`'s
744
+ // `objectLike`) -- and it boxes the identical way: `cppTypeOf` already
745
+ // spells it `gea::Ref<gea::TypedArray<T>>`, so `Value::box` records that
746
+ // exact C++ type's address as `payloadType()`, one program-wide address
747
+ // per element domain. That per-type address is also what
748
+ // `gea::host::instanceOfTypedArray<T>` (gea_runtime.h) reads back to
749
+ // answer `x instanceof Uint16Array` on a boxed value -- the SAME
750
+ // primitive `instanceOfError` above already uses for the error family,
751
+ // asked of a typed array's own payload type instead of a registration
752
+ // table, because a typed array's C++ type already IS its constructor
753
+ // identity with no subclass to reconcile.
754
+ //
755
+ // Missing here meant a typed-array value could never widen into a
756
+ // `dynamic` cell at all: `emit-instanceof.ts`'s boxed
757
+ // `x instanceof Uint16Array` test has a real box to read only once a
758
+ // typed array can actually get INTO one, and this is the one place that
759
+ // decides that. The same gap silently blocked the reverse read
760
+ // (`unboxedLoadText`, below) and the "typed-array -> dynamic" merge
761
+ // widenings `targets/cpp/conversions.ts`'s `widening` registry asks this
762
+ // exact function about.
763
+ case 'typed-array':
764
+ return 'Object';
765
+ // A `gea::Promise<T>` is a JS Object too -- `typeof new Promise(...) ===
766
+ // 'object'` -- and `Value::box` needs nothing case-specific to hold one:
767
+ // the template already records `payloadTypeTagFor<gea::Promise<T>>()` for
768
+ // whichever `T` this instantiation closes over, one program-wide address
769
+ // per payload type, exactly as it does for every other kind sharing this
770
+ // tag. Missing here meant a value returned from a plain (non-async)
771
+ // function -- `#cachedBody`'s `return (bodyCache[k] as Promise<BodyInit>)
772
+ // .then(...)` in hono's `request.ts`, whose OWN inferred return type is a
773
+ // real `Promise<unknown>` folding to `dynamic` only because its sibling
774
+ // return arm reads a program-declared `any` -- could never widen into a
775
+ // `dynamic` cell at all: `emit-narrowing.ts`'s own generic `held.kind ===
776
+ // 'dynamic'` store (below) and `targets/cpp/conversions.ts`'s `widening`
777
+ // registry both ask this exact function, and both got `null` for a
778
+ // pairing this runtime already has every primitive to satisfy.
779
+ case 'promise':
780
+ return 'Object';
781
+ case 'function':
782
+ case 'function-family':
783
+ case 'function-value-family':
784
+ case 'function-value-dispatch':
785
+ case 'generic-function-set':
786
+ case 'constructor-family':
787
+ case 'constructor-value-dispatch':
788
+ case 'function-and-constructor':
789
+ return 'Function';
790
+ default:
791
+ return null;
792
+ }
793
+ };
794
+ /**
795
+ * The empty optional, when what is being stored is the absence the cell's own
796
+ * presence flag stands for.
797
+ *
798
+ * This is the one store the payload cannot state, which is why the recursion
799
+ * below has a second half. `gea::Optional<T>` is a presence flag plus a `T`;
800
+ * an absence is neither a `T` nor an arm of one, so asking the payload how to
801
+ * hold it gets no answer -- and the answer that comes back, `null`, is read by
802
+ * every caller as "assign as it stands" and renders `cell = nullptr`. For
803
+ * `Optional<double>` that is a clang error. For `Optional<std::string>` it is
804
+ * worse: `std::string`'s own `const char*` constructor makes
805
+ * `Optional::operator=(T&&)` viable, so the cell comes out *present*, holding
806
+ * a string built from a null pointer, and every later presence test answers the
807
+ * opposite of the truth with nothing to say it did.
808
+ *
809
+ * Which absence the cell holds is checked rather than assumed -- an optional
810
+ * tagged `null` handed an `undefined` would answer a later `=== null` with
811
+ * `true`, and that is a wrong answer, not a spelling gap -- which is the rule
812
+ * `convertedValueText` used to keep as its own private copy, now stated once
813
+ * here, where every store site already asks.
814
+ */
815
+ const emptyOptionalText = (held, written) => {
816
+ if (written.kind !== 'null' && written.kind !== 'undefined')
817
+ return null;
818
+ return written.kind === held.absence ? `${cppTypeOf(held)}()` : null;
819
+ };
820
+ /**
821
+ * The empty handle, when what is being stored is the absence the handle's own
822
+ * invalid state stands for.
823
+ *
824
+ * The same store `emptyOptionalText` describes, for the carrier that took the
825
+ * `Optional`'s place. `representation/optional.ts` collapses `T | null` onto a
826
+ * host handle because a handle already carries its absence -- `NativeHandle`
827
+ * default-constructs to `id_ = -1` and `valid()` is `id_ >= 0` -- so the cell
828
+ * that used to be an `Optional<Element>` holding nothing is now an `Element`
829
+ * naming nothing. Without this the `null` assigns as it stands and renders
830
+ * `handle = nullptr`, which is the clang error the optional case above exists
831
+ * to prevent, one carrier along.
832
+ *
833
+ * `null` only, matching the collapse exactly: `optionalOf` keeps the presence
834
+ * flag for `undefined`, so a handle carrier is never the destination of one.
835
+ */
836
+ const emptyHandleText = (held, written) => written.kind === 'null' ? `${cppTypeOf(held)}()` : null;
837
+ /**
838
+ * The parameter index a callable's trailing arguments pack into, or `null` for
839
+ * a fixed-arity convention or a carrier that is not a callable at all.
840
+ */
841
+ const restFromOf = (representation) => {
842
+ if (representation.kind === 'function' || representation.kind === 'function-value-dispatch')
843
+ return representation.abi.restFrom;
844
+ if (representation.kind === 'function-family' || representation.kind === 'function-value-family')
845
+ return representation.abi.restFrom;
846
+ if (representation.kind === 'function-and-constructor')
847
+ return representation.call.restFrom;
848
+ return null;
849
+ };
850
+ /**
851
+ * One value, boxed.
852
+ *
853
+ * A CALLABLE with a rest parameter takes the ABI-stating form: which formal
854
+ * absorbs the trailing arguments is invisible in the C++ type -- `(a, xs: T[])`
855
+ * and `(a, ...xs: T[])` are the same `CallableObject` -- so a dynamic call
856
+ * through the box would hand the first trailing argument to a slot expecting
857
+ * the packed array (10.2.11 binds a rest parameter to an Array of ALL the
858
+ * remaining ones). The emitter is the only reader of the ABI, so it states the
859
+ * position here and `gea::Value::boxCallable` records the matching thunk.
860
+ * hono's `this[method] = (args1, ...args) => ...` is every `app.get(path,
861
+ * handler)` in the program.
862
+ */
863
+ export const boxedText = (representation, tag, text) => {
864
+ const restFrom = restFromOf(representation);
865
+ const value = `static_cast<${cppTypeOf(representation)}>(${text})`;
866
+ const abi = representation.kind === 'function-and-constructor' ? representation.call : 'abi' in representation ? representation.abi : null;
867
+ if (abi?.receiver)
868
+ return `gea::Value::boxMethod<${restFrom === null ? -1 : restFrom + 1}>(${value})`;
869
+ if (restFrom === null)
870
+ return `gea::Value::box(gea::Value::Tag::${tag}, ${value})`;
871
+ return `gea::Value::boxCallable<${restFrom}>(${value})`;
872
+ };
873
+ /**
874
+ * How a native carrier boxes into a dynamic cell, rendered over an arbitrary
875
+ * text naming the storage.
876
+ *
877
+ * The dynamic reason is stated HERE rather than at each caller because it is
878
+ * not a choice: `widenedStoreText` records below that no `DynamicReason` is
879
+ * consulted on this edge -- every boundary a reason names admits any concrete
880
+ * value the checker lets reach it -- so a caller asking "box this carrier"
881
+ * asks this one question and must not be free to spell a different reason for
882
+ * it.
883
+ *
884
+ * `storage` is a TEXT, not a member. The recipe is a function of the
885
+ * representation and that text alone: no field key, no struct name, no
886
+ * surrounding statement. So two fields spelling the same carrier spell the
887
+ * same recipe, and the native field table's read hook can render an optional
888
+ * or union carrier's arms ONCE over a slot its key chain selects into
889
+ * (`records.ts`) instead of once per field. `null` is the renderer's own
890
+ * answer that this carrier needs no widening at all.
891
+ */
892
+ export const dynamicCarrierBoxText = (carrier, storage) => widenedStoreText({ kind: 'dynamic', reason: 'declared-any-never-narrowed' }, carrier, storage);
893
+ /**
894
+ * A `dynamic` value an async body returns, settled into its `Promise<V>`
895
+ * result. The value may itself be a promise, which the return ADOPTS rather
896
+ * than fulfils with (ECMA-262 27.2.1.3.2), so the choice is made at run time
897
+ * by `gea::detail::promiseFromDynamic` -- the store-direction recipe below.
898
+ */
899
+ export const dynamicPromiseAdoptionText = (result, text) => widenedStoreText(result, { kind: 'dynamic', reason: 'declared-any-never-narrowed' }, text);
900
+ export const widenedStoreText = (held, written, text) => {
901
+ if (representationKey(held) === representationKey(written))
902
+ return null;
903
+ const remappedSet = genericFunctionSetWideningText(written, held, text);
904
+ if (remappedSet !== null)
905
+ return remappedSet;
906
+ // An optional over a union widens twice: the arm becomes the union, and the
907
+ // union assigns into the optional through its converting assignment. Asking
908
+ // recursively is what keeps the two steps from being two separate rules.
909
+ //
910
+ // The payload is asked first and the absence answers only what it leaves
911
+ // unanswered. A payload that really does carry an arm of the written carrier
912
+ // -- an absence arm included -- is that value's own home, and storing the
913
+ // empty optional instead would drop a live value rather than record it.
914
+ if (held.kind === 'optional')
915
+ return widenedStoreText(held.payload, written, text) ?? emptyOptionalText(held, written);
916
+ if (held.kind === 'native-handle')
917
+ return emptyHandleText(held, written);
918
+ // The same store once more, for the program's own refcounted instances:
919
+ // `optional.ts` collapses `TreeNode | null` onto `gea::Ref<TreeNode>`, whose
920
+ // default construction IS that `null`. Without this the literal assigns as it
921
+ // stands and renders `left = nullptr`, which `Ref` does not accept.
922
+ if (held.kind === 'class-ref' && held.ownership === 'shared-refcount' && written.kind === 'null')
923
+ return `${cppTypeOf(held)}()`;
924
+ // A dynamic value returned where `Promise<T>` is declared may itself be a
925
+ // promise -- hono's `formData()` returns its `any`-typed `#cachedBody(...)`
926
+ // -- and an async function's return adopts it (ECMA-262 27.2.1.3.2) rather
927
+ // than fulfilling with the promise object.
928
+ if (held.kind === 'promise' && written.kind === 'dynamic') {
929
+ if (held.value.kind === 'void')
930
+ return `gea::detail::promiseFromDynamic<void>(${text})`;
931
+ const settled = tryCandidateText(() => convertedValueText(written, held.value, 'gea_settled'));
932
+ if (settled === null)
933
+ return null;
934
+ return `gea::detail::promiseFromDynamic<${cppTypeOf(held.value)}>(${text}, [](const gea::Value& gea_settled) { return ${cppTypeOf(held.value)}(${settled}); })`;
935
+ }
936
+ // A held `Promise<T>` is the one case ECMAScript itself widens this way: an
937
+ // `async` function's body returns `T` at a `return` terminator whose ABI
938
+ // result is `Promise<T>` -- the checker accepts this because `T` is
939
+ // assignable to `Promise<T>`, and no ordinary binding or field can ever
940
+ // hold `T` where a `Promise<T>` is declared, so this path is reached only
941
+ // from the return terminator (`emit.ts`). Recursing (rather than a direct
942
+ // `gea::Promise<...>(text)` wrap) lets a payload that itself needs
943
+ // widening -- e.g. a tagged-union arm -- widen first; either way the
944
+ // result relies on `gea::Promise<T>`'s own non-explicit converting
945
+ // constructor, the same mechanism `Optional<T>` already uses above.
946
+ if (held.kind === 'promise')
947
+ return widenedStoreText(held.value, written, text);
948
+ // A `dynamic` cell is the one case this reconciliation widens *into* rather
949
+ // than narrows *out of* -- see citations.md finding 1 for why nothing else
950
+ // in the compiler boxes a concrete value on write. `held.reason` is not
951
+ // consulted: every `DynamicReason` (model.ts) names a boundary that admits
952
+ // any concrete value the checker allows to reach it (declared
953
+ // `any`/`unknown`, an unresolved `JSON.parse`, a thrown carrier,
954
+ // `ToString`'s operand), so the box is unconditional once the cell itself
955
+ // is `dynamic`.
956
+ if (held.kind === 'dynamic') {
957
+ // A value that is ALREADY a box needs no box: a `gea::Value` reaching a
958
+ // `gea::Value` cell is a copy. `dynamicTagFor` answers `null` for a
959
+ // `dynamic` not because it cannot be boxed but because it already is one,
960
+ // so without this the generic tail below read that `null` as a refusal.
961
+ // Two boxes whose carriers state different REASONS are one C++ type, which
962
+ // is why this is a kind test and not a key test -- `dynamic(untyped-
963
+ // callable)` and `dynamic(declared-any-never-narrowed)` are both
964
+ // `gea::Value`, and only the identical pair is caught by this function's
965
+ // own identity gate above.
966
+ if (written.kind === 'dynamic')
967
+ return text;
968
+ // A sum has no single tag: which JavaScript type it is depends on the arm
969
+ // that is live, so the box is built by the same discriminant chain
970
+ // `emit-equality.ts` compares one with. Written out per arm rather than
971
+ // deferred to a runtime helper because `Value::box` needs the payload's
972
+ // STATIC type to record its dispatchers (gea_runtime.h's own note on
973
+ // `box`), and only an arm-indexed `get<I>()` has one.
974
+ //
975
+ // `text` is read once per arm, which is safe for the same reason
976
+ // `absenceComparisonText` reads its operand twice: `operandText` answers an
977
+ // already-materialized SSA name, never an expression with effects.
978
+ //
979
+ // The value is force-cast to the carrier's own declared C++ type
980
+ // (`cppTypeOf`) before it reaches `box`, never left to deduce `box`'s
981
+ // template argument from `text`'s own C++ expression type: `box<T>`
982
+ // records `std::decay_t<T>` as the payload's static type (`gea_runtime.h`'s
983
+ // own comment on `box`), and a checked unboxer downstream (`unboxAs`)
984
+ // compares THAT recorded type against `cppTypeOf` again -- so the two must
985
+ // be the same authority. Deduction gives the wrong one for exactly the
986
+ // case this "already-materialized SSA name" assumption misses: a constant
987
+ // operand's `text` can be the bare literal itself (`emit.ts`'s
988
+ // `emitConstant`/`types.ts`'s `cppConstantLiteral` inline a `dynamic`
989
+ // constant's literal text directly rather than routing it through a
990
+ // variable first), and a bare `41` is a C++ `int`, a bare `"9"` a `const
991
+ // char*` -- neither matches the `double`/`std::string`
992
+ // `Tag::Number`/`Tag::String` promise. `static_cast<double>(41)` converts
993
+ // the literal to the declared type before boxing it -- a plain explicit
994
+ // `box<T>(...)` template argument was tried first and rejected: an
995
+ // ALREADY-typed source read through a `const` accessor (a field read
996
+ // inside a `const` member function, e.g.) is a `const` lvalue, which
997
+ // cannot bind to `box`'s `T&&` when `T` is forced non-const, where
998
+ // `static_cast<T>` copy-constructs a plain prvalue either way and binds
999
+ // cleanly regardless of what qualifiers the source expression carried.
1000
+ // An optional value has no single tag either, for a different reason than
1001
+ // a union does: which JS value it boxes to depends on whether it is
1002
+ // present at all, not on which arm is live. Absent boxes to the exact
1003
+ // value its own absence spells (`null` or `undefined`, `written.absence`
1004
+ // names which) -- reusing `cppConstantLiteral`'s own spelling for that
1005
+ // literal and this SAME function's generic tail below to box it, rather
1006
+ // than hand-writing a second `Tag::Null`/`Tag::Undefined` construction
1007
+ // that could drift from the one the plain bare-`null`/bare-`undefined`
1008
+ // widening already renders. Present recurses one payload down, which
1009
+ // reaches the tagged-union case just above when the payload is one.
1010
+ if (written.kind === 'optional') {
1011
+ const absentRepresentation = { kind: written.absence };
1012
+ const absentText = cppConstantLiteral(written.absence, written.absence, absentRepresentation);
1013
+ const boxedAbsent = widenedStoreText(held, absentRepresentation, absentText);
1014
+ const boxedPresent = widenedStoreText(held, written.payload, `(*${text})`);
1015
+ if (boxedAbsent === null || boxedPresent === null)
1016
+ return null;
1017
+ return `(${text}.has_value() ? ${boxedPresent} : ${boxedAbsent})`;
1018
+ }
1019
+ if (written.kind === 'tagged-union') {
1020
+ const arms = [];
1021
+ for (const [index, arm] of written.arms.entries()) {
1022
+ const armText = `${text}.get<${index}>()`;
1023
+ // An arm that is itself a box is stored as-is, and cannot go through
1024
+ // the recursion below: this function's identity gate answers `null`
1025
+ // for a same-carrier pair -- "no widening needed", which the loop
1026
+ // would read as a refusal. hono's `compose` carries its `handler` as
1027
+ // `tagged-union(dynamic(untyped-callable) | optional(Next))`, and
1028
+ // reading that cell as `dynamic` asks exactly this of arm 0.
1029
+ if (arm.value.kind === 'dynamic') {
1030
+ // A broad Function arm is stored as FunctionValue so the tagged
1031
+ // union retains its executable Function-tag discriminator. Both
1032
+ // FunctionValue -> Value (base conversion) and Value ->
1033
+ // FunctionValue (checked constructor) exist, which makes the two
1034
+ // arms of a C++ conditional expression ambiguous unless the live
1035
+ // FunctionValue is explicitly viewed as its Value base.
1036
+ const dynamicArmText = arm.runtimeDiscriminator.kind === 'callable-tag' ? `static_cast<gea::Value>(${armText})` : armText;
1037
+ arms.push(`${text}.is<${index}>() ? ${dynamicArmText} : `);
1038
+ continue;
1039
+ }
1040
+ const armTag = dynamicTagFor(arm.value);
1041
+ // An arm with no tag is not a carrier this box cannot hold -- it is one
1042
+ // whose JavaScript type depends on something a level further in.
1043
+ // `BodyInit | null | undefined` (hono's `createResponseInstance`) has a
1044
+ // `present` arm that is ITSELF a union of seven, every one tagged. So
1045
+ // the arm recurses here -- this case and the optional one above answer
1046
+ // exactly the shapes `dynamicTagFor` returns `null` for -- and an arm
1047
+ // neither reaches still refuses, one level deeper, for the real reason.
1048
+ const boxed = armTag === null ? widenedStoreText(held, arm.value, armText) : boxedText(arm.value, armTag, armText);
1049
+ if (boxed === null)
1050
+ return null;
1051
+ arms.push(`${text}.is<${index}>() ? ${boxed} : `);
1052
+ }
1053
+ return `(${arms.join('')}gea::Value())`;
1054
+ }
1055
+ const tag = dynamicTagFor(written);
1056
+ return tag === null ? null : boxedText(written, tag, text);
1057
+ }
1058
+ // A CALLABLE CELL whose held convention differs from the written one only in
1059
+ // its RESULT -- `const slot: (n: number) => any = concrete`, and hono's
1060
+ // `#addRoute(handler: H)` one union layer up. Asked before the tagged-union
1061
+ // gate below because a bare callable cell never reaches the arm loop, and
1062
+ // `emitBindingWrite` renders a store through this function rather than
1063
+ // through `convertedValueText`, so the pair has no other way in.
1064
+ const adaptedStore = resultAdaptedCallableText(written, held, text);
1065
+ if (adaptedStore !== null)
1066
+ return adaptedStore;
1067
+ if (held.kind !== 'tagged-union')
1068
+ return null;
1069
+ // A bare tagged union has no `has_value()` of its own: it cannot keep an
1070
+ // absence flag the way a `held.kind === 'optional'` above does. So a
1071
+ // `written` that still arrives optional here -- `a && a.b`'s kept side
1072
+ // still wearing its own optional while the merge's own carrier collapsed
1073
+ // to a bare union, or `a || b`/`a ?? b`'s kept-present side one absence
1074
+ // state narrower than `a` itself -- is read unconditionally rather than
1075
+ // refused. That unconditional read is sound, not a risk taken here: the
1076
+ // checker never lets a possibly-absent value reach a non-optional carrier
1077
+ // unless it has already proven presence at this exact point (the same
1078
+ // proof `narrowing`'s own unwrap trusts rather than re-derives,
1079
+ // `targets/cpp/conversions.ts`), one level up, at a store instead of a
1080
+ // load. The arm search below runs over the optional's *payload* -- no arm
1081
+ // an optional target ever selected wraps another optional, so searching
1082
+ // the wrapper itself would never match.
1083
+ if (written.kind === 'optional') {
1084
+ // An arm that IS this optional, whole. The deref below spends the
1085
+ // optional's own absence on the grounds that no arm ever wraps one -- and
1086
+ // hono's `compose` disproves that: `let handler` is a `Function | (Next |
1087
+ // undefined)` cell whose arms are the record and `optional(callable)`, so
1088
+ // writing `next || undefined` into it stores the optional AS the arm.
1089
+ // Dereferencing there would drop the absence the arm exists to hold.
1090
+ const wholeIndex = held.arms.findIndex((arm) => representationKey(arm.value) === representationKey(written));
1091
+ if (wholeIndex >= 0)
1092
+ return `${cppTypeOf(held)}::ofArm<${wholeIndex}>(${text})`;
1093
+ const derefText = `(*${text})`;
1094
+ const payloadKey = representationKey(written.payload);
1095
+ const index = held.arms.findIndex((arm) => representationKey(arm.value) === payloadKey);
1096
+ if (index >= 0)
1097
+ return `${cppTypeOf(held)}::ofArm<${index}>(${derefText})`;
1098
+ // The optional's live payload can reach a union arm by the same nominal
1099
+ // upcast as a bare class value below. This arises when a mutable local is
1100
+ // filled from an optional derived-class property after a presence guard,
1101
+ // while its cell holds a union of base classes. The guard has already
1102
+ // discharged absence at this store; preserve the live object and select
1103
+ // the unique ancestor arm rather than refusing merely because the source
1104
+ // still carries its flow-view Optional wrapper.
1105
+ if (written.payload.kind === 'class-ref') {
1106
+ for (const [armIndex, arm] of held.arms.entries()) {
1107
+ if (arm.value.kind === 'class-ref' &&
1108
+ written.payload.ownership === arm.value.ownership &&
1109
+ written.payload.ancestors.includes(arm.value.declaration)) {
1110
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${cppTypeOf(arm.value)}(${derefText}))`;
1111
+ }
1112
+ }
1113
+ }
1114
+ for (const [armIndex, arm] of held.arms.entries()) {
1115
+ if (arm.value.kind !== 'tagged-union')
1116
+ continue;
1117
+ const inner = widenedStoreText(arm.value, written.payload, derefText);
1118
+ if (inner !== null)
1119
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${inner})`;
1120
+ }
1121
+ return null;
1122
+ }
1123
+ const writtenKey = representationKey(written);
1124
+ const index = held.arms.findIndex((arm) => representationKey(arm.value) === writtenKey);
1125
+ if (index >= 0)
1126
+ return `${cppTypeOf(held)}::ofArm<${index}>(${text})`;
1127
+ // A join can nest: `string | number | boolean | null | undefined` derives as
1128
+ // an outer union over the two absence arms and an *inner* union over the
1129
+ // three value arms, so a written `bool` is an arm of an arm and reaches its
1130
+ // home in two wraps rather than one. Only a `tagged-union` arm is searched.
1131
+ // Every other widening this function performs is lossy about which value
1132
+ // arrived -- an `optional` answers the empty optional and a `native-handle`
1133
+ // the empty handle when the payload does not match -- and choosing one of
1134
+ // those from inside a union would silently store an absence where the
1135
+ // program wrote a value.
1136
+ for (const [armIndex, arm] of held.arms.entries()) {
1137
+ if (arm.value.kind === 'tagged-union') {
1138
+ const inner = widenedStoreText(arm.value, written, text);
1139
+ if (inner !== null)
1140
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${inner})`;
1141
+ continue;
1142
+ }
1143
+ // A CALLABLE reaches an arm the same way it reaches a plain slot: through
1144
+ // one of `gea::CallableObject`'s own implicit converting constructors,
1145
+ // which take the value as it stands and are what `convertedValueText`
1146
+ // already answers with for the identical pair when the target is not a
1147
+ // union. hono's `H = Handler | MiddlewareHandler` is the case -- a
1148
+ // middleware `(c, next) => Promise<void>` is assignable to one arm and the
1149
+ // exact-key search above cannot see it, because assignability here is not
1150
+ // carrier equality.
1151
+ //
1152
+ // Only these four, and only because each one renders as the value itself:
1153
+ // an arm chosen through a LOSSY widening would store something other than
1154
+ // what the program wrote, which is the same line the record recasts below
1155
+ // stay on.
1156
+ if (cppTypeOf(written) === cppTypeOf(arm.value) ||
1157
+ dropsUnboundParameters(written, arm.value) ||
1158
+ dropsAllParametersIntoResultArm(written, arm.value) ||
1159
+ widensResultIntoArm(written, arm.value) ||
1160
+ discardsResultIntoVoid(written, arm.value)) {
1161
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${text})`;
1162
+ }
1163
+ if (written.kind === 'class-ref' &&
1164
+ arm.value.kind === 'class-ref' &&
1165
+ written.ownership === arm.value.ownership &&
1166
+ written.ancestors.includes(arm.value.declaration)) {
1167
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${cppTypeOf(arm.value)}(${text}))`;
1168
+ }
1169
+ // The fifth pair is not an implicit constructor, so it wraps the text
1170
+ // rather than passing it through -- see `resultAdapterOf`. hono's `H =
1171
+ // Handler | MiddlewareHandler` reaches its arm exactly here: same frame,
1172
+ // a result the arm declares `any`.
1173
+ const adaptedArm = resultAdaptedCallableText(written, arm.value, text);
1174
+ if (adaptedArm !== null)
1175
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${adaptedArm})`;
1176
+ // A record recasts into a dictionary/array-object ARM the same way
1177
+ // `convertedValueText`'s own top level recasts it directly -- identity above never matches an object literal against either.
1178
+ // A dictionary reaches a WIDER dictionary arm the same way (`dictionaryCastableToDictionary`);
1179
+ // `conversions.ts` admits only a union with ONE such arm, so the first match is the match.
1180
+ const recast = written.kind === 'record'
1181
+ ? arm.value.kind === 'dictionary'
1182
+ ? recastedRecordToDictionaryText(written, arm.value, text)
1183
+ : arm.value.kind === 'array-object'
1184
+ ? recastedRecordToArrayText(written, arm.value, text)
1185
+ : arm.value.kind === 'record'
1186
+ ? recastedRecordText(written, arm.value, text)
1187
+ : null
1188
+ : written.kind === 'dictionary' && arm.value.kind === 'dictionary'
1189
+ ? recastedDictionaryText(written, arm.value, text)
1190
+ : null;
1191
+ if (recast !== null)
1192
+ return `${cppTypeOf(held)}::ofArm<${armIndex}>(${recast})`;
1193
+ }
1194
+ return null;
1195
+ };
1196
+ /**
1197
+ * Rebuilding a tagged union at a different arm ordering than the one it was
1198
+ * built with.
1199
+ *
1200
+ * Two declared unions with the same member set do not have to agree on tag
1201
+ * order -- `boolean | number` and `number | boolean` are two unrelated C++
1202
+ * template instantiations (`gea::TaggedUnion<bool, double>` vs
1203
+ * `gea::TaggedUnion<double, bool>`), which is exactly what a control-flow merge
1204
+ * produces when its two branches arrive through independently-declared unions.
1205
+ * Neither `narrowedLoadText` nor `widenedStoreText` covers it: the target is
1206
+ * not one arm of the source, and the source is not one arm of the target. What
1207
+ * it needs is the same discriminant `narrowedLoadText` reads, dispatched into
1208
+ * the same constructor `widenedStoreText` calls -- read the live arm by
1209
+ * whichever tag the source actually holds, and rebuild it at the target's tag
1210
+ * for that same value type. `null` when some source arm has no same-typed home
1211
+ * in the target: that is a real hole in the target's arm set, not a spelling
1212
+ * gap, and inventing a fallback there would silently drop a live value.
1213
+ */
1214
+ /**
1215
+ * A sum converted ARM BY ARM into one payload, or `null` when an arm has no
1216
+ * way there.
1217
+ *
1218
+ * tsc's `visitEachChild<T>` returns `fn === undefined ? node : fn(node, ...)`
1219
+ * where `fn` is a `VisitEachChildFunction<any>`: the conditional merges `T`
1220
+ * with a declared `any` into `tagged-union(native-record-ref | dynamic)`, and
1221
+ * the function returns `T | undefined`. The typed arm IS the payload; the
1222
+ * dynamic arm is the checked unbox every read of a declared-`any` value into
1223
+ * a typed cell already performs (`unboxedLoadText`, which aborts naming the
1224
+ * carrier on a mismatch). So the load dispatches on the live arm, exactly as
1225
+ * `recastedUnionText` does for a sum whose home is another sum. Only a sum
1226
+ * WITH a dynamic arm is answered: without one, a same-keyed arm beside
1227
+ * others is a narrowing (`narrowedLoadText`), which selects an arm and
1228
+ * proves nothing about the rest, and this must not stand in for it.
1229
+ * `targets/cpp/conversions.ts`'s `recasting` admits the pair on the same
1230
+ * predicate -- through this function -- so the admission and the render
1231
+ * cannot drift.
1232
+ */
1233
+ export const sumIntoPayloadText = (source, payload, text, wrap) => {
1234
+ if (payload.kind === 'dynamic' || payload.kind === 'optional' || payload.kind === 'tagged-union')
1235
+ return null;
1236
+ if (!source.arms.some((arm) => arm.value.kind === 'dynamic'))
1237
+ return null;
1238
+ const key = representationKey(payload);
1239
+ const homes = [];
1240
+ for (const [index, arm] of source.arms.entries()) {
1241
+ const armText = `${text}.get<${index}>()`;
1242
+ const loaded = representationKey(arm.value) === key ? armText : arm.value.kind === 'dynamic' ? unboxedLoadText(payload, armText) : null;
1243
+ if (loaded === null)
1244
+ return null;
1245
+ homes.push(wrap(loaded));
1246
+ }
1247
+ let result = homes[homes.length - 1] ?? null;
1248
+ if (result === null)
1249
+ return null;
1250
+ for (let index = homes.length - 2; index >= 0; index--)
1251
+ result = `${text}.is<${index}>() ? ${homes[index]} : (${result})`;
1252
+ return `(${result})`;
1253
+ };
1254
+ export const recastedUnionText = (source, target, text,
1255
+ // A third resolver for an arm pair this module cannot answer alone. A
1256
+ // `native-record-ref` names a layout instead of carrying one, so the field
1257
+ // lists a record recast needs are a fact of the EMITTER's context, not of
1258
+ // the representation -- `emit-callable.ts`'s `structuralRecordViewText` is
1259
+ // where both are in hand. `conversions.ts` already ADMITS that pair, so
1260
+ // without this the admission and the render disagree, which is the one
1261
+ // failure mode this compiler exists to remove: hono's `c.json()` passes
1262
+ // `ResponseOrInit<ContentfulStatusCode>` into `ResponseOrInit<StatusCode>`
1263
+ // -- two shape ids over identical field carriers -- and the arm had no home
1264
+ // here even though the conversion graph said it did.
1265
+ viaLayout) => {
1266
+ // A union with N arms otherwise interpolates BOTH the full target spelling
1267
+ // and the source expression at every arm -- and a nested inner-union arm
1268
+ // repeats that again inside every outer arm. The three.js app's `NativeUniformValue
1269
+ // | null | undefined` is a 3-arm outer union over a 32-arm inner one, and
1270
+ // one recast of it -- reached from several sibling call sites glued by the
1271
+ // caller's own `&&`/`||` -- emitted a single line over 1.29MB: 957
1272
+ // `::ofArm<` sites each re-spelling a ~1.3KB template type, because neither
1273
+ // the source nor the target type was ever bound to a short name. Binding
1274
+ // both once, in an IIFE, is `promiseSourceName`'s own reasoning applied
1275
+ // here: `text` is an arbitrary expression that must be evaluated once, and
1276
+ // the target's spelling is exactly as repeatable. Skipped for a single-arm
1277
+ // source -- there is only one spelling of `text` and one of the target type
1278
+ // either way, so the IIFE would add bytes without removing any.
1279
+ const multiArm = source.arms.length > 1;
1280
+ const boundSource = multiArm ? recastUnionSourceName : text;
1281
+ const homeOf = source.arms.map((arm, index) => {
1282
+ const armText = `${boundSource}.get<${index}>()`;
1283
+ const key = representationKey(arm.value);
1284
+ const exact = target.arms.findIndex((candidate) => representationKey(candidate.value) === key);
1285
+ if (exact >= 0)
1286
+ return { index: exact, text: armText };
1287
+ // An arm that is ITSELF a union homes as a whole, never candidate by
1288
+ // candidate. The candidate probe below asks `convertedValueText(arm,
1289
+ // candidate)`, and for a union arm against one of its own members that
1290
+ // question is answered by `narrowedLoadText` -- an UNCHECKED `.get<k>()`
1291
+ // that assumes the member is live. Taken as a home it reads whichever
1292
+ // member sits at that slot regardless of the inner discriminant: the
1293
+ // runtime `Request` constructor's `ArrayBuffer.isView(init.body)`, over
1294
+ // `undefined | null | (string | Uint8Array | ArrayBuffer | ... |
1295
+ // ReadableStream)` narrowed to the four arms the preceding guards left,
1296
+ // recast every inner arm as the `Uint8Array` one, answered `true` for a
1297
+ // `ReadableStream` body, and read `.buffer`/`.byteOffset` out of it.
1298
+ // Certified, compiled, RangeError. The inner union's own discriminant is
1299
+ // what decides the target arm, so the whole inner union narrows (or
1300
+ // recasts) into the whole target, one level down where that discriminant
1301
+ // is in scope -- `narrowedUnionSubsetText`'s collision rule, applied here.
1302
+ if (arm.value.kind === 'tagged-union') {
1303
+ const inner = arm.value;
1304
+ const whole = tryCandidateText(() => narrowedLoadText(inner, target, armText)) ?? recastedUnionText(inner, target, armText, viaLayout);
1305
+ return whole === null ? null : { index: -1, text: whole, whole: true };
1306
+ }
1307
+ const homes = [];
1308
+ for (const [candidateIndex, candidate] of target.arms.entries()) {
1309
+ // `convertedValueText` and not `widenedStoreText` alone: an arm can
1310
+ // reach its home by a recast rather than a widening -- two records that
1311
+ // declare the same fields under two interned shapes, which is what
1312
+ // hono's `Result<T>` is made of -- and only the superset tries all of
1313
+ // them.
1314
+ const stored = tryCandidateText(() => convertedValueText(arm.value, candidate.value, armText));
1315
+ const home = stored ?? viaLayout?.(arm.value, candidate.value, armText) ?? null;
1316
+ if (home !== null)
1317
+ homes.push({ index: candidateIndex, text: home });
1318
+ }
1319
+ return homes.length > 1 ? (undefinedFieldChoice(arm.value, target, homes, armText) ?? homes[0]) : (homes[0] ?? null);
1320
+ });
1321
+ if (homeOf.some((home) => home === null))
1322
+ return null;
1323
+ return recastedUnionFromHomes(source, target, text, homeOf);
1324
+ };
1325
+ /**
1326
+ * Two target arms a record arm converts into, told apart by a dynamic field.
1327
+ *
1328
+ * A record whose field is dynamic converts into every target arm whose field
1329
+ * a checked unbox can reach -- `ReadableStreamReadResult<unknown>`'s
1330
+ * `{ done, value }` into both `{ done: true, value: undefined }` and
1331
+ * `{ done: false, value: Uint8Array }` -- and taking the first one unboxed a
1332
+ * live chunk as `undefined`. Which arm holds is the field's own run-time tag.
1333
+ */
1334
+ const undefinedFieldChoice = (source, target, homes, armText) => {
1335
+ if (source.kind !== 'record' || homes.length !== 2)
1336
+ return null;
1337
+ const arrow = source.ownership === 'shared-refcount' ? '->' : '.';
1338
+ for (const field of source.fields) {
1339
+ if (field.value.kind !== 'dynamic' || !field.required)
1340
+ continue;
1341
+ const fieldKinds = homes.map((home) => {
1342
+ const arm = target.arms[home.index]?.value;
1343
+ return arm?.kind === 'record' ? arm.fields.find((candidate) => candidate.key === field.key)?.value.kind : undefined;
1344
+ });
1345
+ const absent = fieldKinds.findIndex((kind) => kind === 'undefined');
1346
+ const present = fieldKinds.findIndex((kind) => kind !== undefined && kind !== 'undefined' && kind !== 'dynamic');
1347
+ if (absent < 0 || present < 0)
1348
+ continue;
1349
+ const type = cppTypeOf(target);
1350
+ const wrap = (home) => `${type}::ofArm<${home.index}>(${home.text})`;
1351
+ const test = `${armText}${arrow}${cppRecordFieldName(field.key)}.tag() == gea::Value::Tag::Undefined`;
1352
+ return { index: -1, whole: true, text: `(${test} ? ${wrap(homes[absent])} : ${wrap(homes[present])})` };
1353
+ }
1354
+ return null;
1355
+ };
1356
+ /** The text an arm of a multi-arm source is read through, bound by `recastedUnionFromHomes`; a single-arm source reads the value itself. */
1357
+ export const recastUnionArmText = (source, text, index) => `${source.arms.length > 1 ? recastUnionSourceName : text}.get<${index}>()`;
1358
+ /**
1359
+ * The dispatch over a sum's arms, given each arm's home in the target: the
1360
+ * discriminant read and the `ofArm` rebuild are one answer, whoever decided
1361
+ * the homes -- the chain probe above or a plan the census owns
1362
+ * (`conversion/record-view.ts`).
1363
+ */
1364
+ export const recastedUnionFromHomes = (source, target, text, homes) => {
1365
+ const multiArm = source.arms.length > 1;
1366
+ const boundSource = multiArm ? recastUnionSourceName : text;
1367
+ const targetType = multiArm ? recastUnionAliasName : cppTypeOf(target);
1368
+ const last = homes.length - 1;
1369
+ const lastHome = homes[last];
1370
+ if (!lastHome)
1371
+ return null;
1372
+ const homeText = (home) => (home.whole ? home.text : `${targetType}::ofArm<${home.index}>(${home.text})`);
1373
+ let result = homeText(lastHome);
1374
+ for (let index = last - 1; index >= 0; index--) {
1375
+ const home = homes[index];
1376
+ if (!home)
1377
+ return null;
1378
+ result = `${boundSource}.is<${index}>() ? ${homeText(home)} : (${result})`;
1379
+ }
1380
+ if (!multiArm)
1381
+ return result;
1382
+ // Parameter, not body-local -- see the nested chain above for why a
1383
+ // same-named `const auto&` in the body reads itself.
1384
+ return (`([&](const ${cppTypeOf(source)}& ${recastUnionSourceName}) -> ${cppTypeOf(target)} { using ${recastUnionAliasName} = ${cppTypeOf(target)}; ` +
1385
+ `return ${result}; }(${text}))`);
1386
+ };
1387
+ /**
1388
+ * One record rebuilt as another whose declared shape it satisfies.
1389
+ *
1390
+ * `type A = Named & { age: number }` and `({ ...base, ...extra })` describe one
1391
+ * shape and intern as two -- an `intersection` and an `object` -- so they derive
1392
+ * two `record` carriers under two shape ids, which this backend spells as two
1393
+ * structs. A value of one is not a value of the other in C++ even though every
1394
+ * member matches, and `return ({ ...base, ...extra })` out of a function
1395
+ * declared to return the intersection is exactly that meeting.
1396
+ *
1397
+ * This is `recastedUnionText`'s structural twin, and it is written for the same
1398
+ * reason: neither a narrowing nor a widening covers a pair where each side is
1399
+ * the whole of the other, just carried under a different identity.
1400
+ *
1401
+ * The relation admitted is TypeScript's own structural one, not shape equality,
1402
+ * because that equality was never the question a return conversion asks.
1403
+ * `return this.options` out of a method declared to return a narrower options
1404
+ * type is legal TypeScript and lowers to exactly this pair, and mongodb's
1405
+ * driver is built out of it: every `CommandOptions` producer hands a record
1406
+ * carrying dozens of fields to a slot declaring an overlapping, differently
1407
+ * ordered, differently optional subset. So:
1408
+ *
1409
+ * - A source field the target does not declare is DROPPED. TypeScript already
1410
+ * erased it at the boundary -- the declared type is what any later read goes
1411
+ * through -- so the rebuild loses nothing the program could still see. (An
1412
+ * `any`-typed re-read is not a counter-example: it reads the target carrier,
1413
+ * which is what the declaration says the value is.)
1414
+ * - A target field the source lacks is filled with the carrier's own absent
1415
+ * value, and only when the carrier HAS one -- `gea::Optional<T>{}` and
1416
+ * `gea::Undefined{}`. A missing field whose carrier is a bare `T` is a real
1417
+ * hole and still refuses.
1418
+ * - Requiredness may WIDEN (source required, target optional) and never
1419
+ * narrow. There is no physical presence bit -- `records.ts` stores exactly
1420
+ * `cppTypeOf(field.value)` -- so `in` answers from `staticKeyPresenceOf`'s
1421
+ * reading of the field list, which proves presence from `required`. Going
1422
+ * the other way would turn a runtime presence flag into a constant `true`:
1423
+ * a silent wrong answer, so it stays refused.
1424
+ *
1425
+ * A field's own carrier is matched by key, by being a bare `T` poured into the
1426
+ * `Optional<T>` the target declares (`gea::Optional`'s converting constructor
1427
+ * performs it, and two optionals over one payload are one C++ type since
1428
+ * `cppTypeOf` never reads `.absence`), or by being the same recast one level
1429
+ * down. The nominal-identity split this exists for does not stop at the top:
1430
+ * hono's pattern router hands `[Pattern, string, [Handler, ParamIndexMap]]` to
1431
+ * a `Route<T>` slot whose innermost tuple interned under a second shape id with
1432
+ * a byte-identical field list, so the OUTER pair matched everywhere but there.
1433
+ * Refusing would refuse the identical question one level in.
1434
+ *
1435
+ * A lambda, not a repeated expression: `text` is evaluated once however many
1436
+ * fields the record has, which a comma-separated aggregate would not guarantee
1437
+ * for a source that is a call. `depth` names the parameter, so a nested rebuild
1438
+ * does not shadow its parent's.
1439
+ */
1440
+ const absentFieldTexts = new WeakMap();
1441
+ const absentFieldText = (value) => {
1442
+ // Remembered per carrier: an unspellable carrier answers by CATCHING a
1443
+ // thrown refusal, and the recast admission below asks about the same
1444
+ // absent target field once per candidate source -- thousands of throws
1445
+ // for one field on TypeScript's own compiler.
1446
+ const remembered = absentFieldTexts.get(value);
1447
+ if (remembered !== undefined)
1448
+ return remembered;
1449
+ const built = absentFieldTextOf(value);
1450
+ absentFieldTexts.set(value, built);
1451
+ return built;
1452
+ };
1453
+ const absentFieldTextOf = (value) => {
1454
+ // A carrier the one type mapping cannot spell has no default it could be
1455
+ // absent AS, so the field is not recastable -- a refusal, not a throw. This
1456
+ // is asked from the conversion registry's ADMISSION (`conversions.ts`'s
1457
+ // `recasting`), whose own `cppTypeOf(target)` guard spells the record by
1458
+ // shape id and never looks inside it: TypeScript's own compiler put an
1459
+ // `optional(function(...))` whose ABI still carried an unmonomorphized type
1460
+ // parameter in a field here, and the throw took the whole compile down
1461
+ // instead of costing this one pair. Same idiom as `manifest.ts`'s
1462
+ // `isSpellable`: the mapping's throw IS the answer, not a second predicate.
1463
+ const spelled = spelledOrNull(value);
1464
+ if (spelled === null)
1465
+ return null;
1466
+ if (value.kind === 'optional' || value.kind === 'undefined')
1467
+ return `${spelled}{}`;
1468
+ // A property that is not there reads back as `undefined`, and a
1469
+ // default-constructed box is exactly that -- `gea::Value`'s own doc says so
1470
+ // ("a hoisted cell holds *some* value from the moment it exists").
1471
+ if (value.kind === 'dynamic')
1472
+ return `${spelled}{}`;
1473
+ // A union declaring an `undefined` arm can state absence too, but its
1474
+ // default constructor builds ARM 0 whichever arm that is, so the arm is
1475
+ // named rather than assumed.
1476
+ if (value.kind === 'tagged-union') {
1477
+ const index = value.arms.findIndex((arm) => arm.value.kind === 'undefined');
1478
+ const arm = value.arms[index];
1479
+ if (arm !== undefined)
1480
+ return `${spelled}::ofArm<${index}>(${cppTypeOf(arm.value)}{})`;
1481
+ }
1482
+ return null;
1483
+ };
1484
+ const spelledOrNull = (value) => {
1485
+ try {
1486
+ return cppTypeOf(value);
1487
+ }
1488
+ catch {
1489
+ return null;
1490
+ }
1491
+ };
1492
+ /**
1493
+ * The arm of `union` that carries exactly `from`, or `null` when none or
1494
+ * several do.
1495
+ *
1496
+ * Several is a refusal rather than a pick: `A | B` where both lower to one C++
1497
+ * type has two right answers and the discriminant records which, so choosing
1498
+ * one here would decide a question the source value does not answer.
1499
+ */
1500
+ const soleArmIndexFor = (union, from) => {
1501
+ const key = representationKey(from);
1502
+ const matches = union.arms.flatMap((arm, index) => (representationKey(arm.value) === key ? [index] : []));
1503
+ return matches.length === 1 ? (matches[0] ?? null) : null;
1504
+ };
1505
+ /** Two array carriers of one element and ownership whose only difference is the extension an interface adds -- one C++ type. */
1506
+ export const sameArrayUpToExtension = (left, right) => left.kind === 'array-object' &&
1507
+ right.kind === 'array-object' &&
1508
+ left.ownership === right.ownership &&
1509
+ representationKey(left.element) === representationKey(right.element) &&
1510
+ arrayExtensionKey(left.extension) !== arrayExtensionKey(right.extension);
1511
+ const recastableFieldValue = (from, to, seen) => {
1512
+ if (representationKey(from) === representationKey(to))
1513
+ return true;
1514
+ if (to.kind === 'optional') {
1515
+ const payload = representationKey(to.payload);
1516
+ if (payload === representationKey(from))
1517
+ return true;
1518
+ if (from.kind === 'optional' && payload === representationKey(from.payload))
1519
+ return true;
1520
+ // An `undefined` source field IS the optional's own absent value: the copy
1521
+ // `EvaluatorResult<undefined>` returned into a slot declared as the
1522
+ // default `EvaluatorResult` writes `value: undefined` into `value?: string
1523
+ // | number`. There is no payload to convert; `recastFieldText` renders the
1524
+ // empty optional.
1525
+ if (from.kind === 'undefined')
1526
+ return true;
1527
+ const fromPayload = from.kind === 'optional' ? from.payload : from;
1528
+ // A payload that is one ARM of the optional's tagged-union payload is the
1529
+ // same admission the bare tagged-union branch below makes, one presence
1530
+ // flag deeper -- and the same render, `recastFieldText`'s optional branch
1531
+ // already descends into its payload before spelling the arm. Without it
1532
+ // tsc's `return evaluatorResult(0)` -- the copy `EvaluatorResult<number>`
1533
+ // with `value: number`, returned where `EvaluatorResult` (default `T`,
1534
+ // `value: string | number | undefined`) is declared -- had the renderer
1535
+ // but not the admission: 19 rows across `checker.ts` and `utilities.ts`.
1536
+ if (to.payload.kind === 'tagged-union' && soleArmIndexFor(to.payload, fromPayload) !== null)
1537
+ return true;
1538
+ // A structural conversion does not stop at a presence flag. A required
1539
+ // source record can fill an optional target record of another interned
1540
+ // shape, and an optional source does the same conversion only on its live
1541
+ // payload. ColorManagement's two concrete color-space records flowing
1542
+ // into its common dictionary value are this exact shape.
1543
+ return recordsRecastableIn(fromPayload, to.payload, seen);
1544
+ }
1545
+ if (to.kind === 'tagged-union' && soleArmIndexFor(to, from) !== null)
1546
+ return true;
1547
+ // A field declared `unknown`/`any` holds any value boxed: the chunk of a
1548
+ // `ReadableStreamReadResult<Uint8Array>` read as `ReadableStreamReadResult<unknown>`.
1549
+ if (to.kind === 'dynamic')
1550
+ return tryCandidateText(() => dynamicCarrierBoxText(from, 'gea_probe')) !== null;
1551
+ // A callable field whose convention differs from the declared one's is
1552
+ // copied through the same adapter a direct store of it takes.
1553
+ if (from.kind === 'function-value-dispatch' && to.kind === 'function-value-dispatch')
1554
+ return resultAdapterOf(from, to) !== null;
1555
+ // Containers copy element by element (`array-copy-recast`,
1556
+ // `dictionary-to-dictionary`), so a field holding one recasts when its
1557
+ // elements convert.
1558
+ // A dynamic field is read out of its box the way any assertion reads one.
1559
+ if ((from.kind === 'array-object' && to.kind === 'array-object') ||
1560
+ (from.kind === 'dictionary' && to.kind === 'dictionary') ||
1561
+ from.kind === 'dynamic')
1562
+ return (tryCandidateText(() => convertedValueText(from, to, 'gea_field')) !== null || emptyContainerRecastText(from, to, 'gea_field') !== null);
1563
+ return recordsRecastableIn(from, to, seen);
1564
+ };
1565
+ /** A record's fields by key, built once per carrier object. */
1566
+ const fieldIndexes = new WeakMap();
1567
+ const fieldIndexOf = (record) => {
1568
+ const remembered = fieldIndexes.get(record);
1569
+ if (remembered !== undefined)
1570
+ return remembered;
1571
+ const built = new Map();
1572
+ for (const field of record.fields)
1573
+ built.set(field.key, field);
1574
+ fieldIndexes.set(record, built);
1575
+ return built;
1576
+ };
1577
+ /** The top-level pairs whose answer is being computed. */
1578
+ const recordRecastPending = new WeakMap();
1579
+ /** Top-level `recordsRecastable` answers, per (source, target) carrier pair. */
1580
+ const recordRecastAnswers = new WeakMap();
1581
+ /**
1582
+ * `seen` is the coinductive cycle guard: `null` at the top level, and the set
1583
+ * of every pair on the current descent once a field's own carrier makes the
1584
+ * walk recurse. A record with a field that cites the record again would recur
1585
+ * forever; the pair is admitted on the assumption under test, which can only
1586
+ * ever admit a cycle, never a pair that fails for a reason the walk would
1587
+ * otherwise have reached.
1588
+ *
1589
+ * The guard's key and set exist only once a descent happens. The conversion
1590
+ * registry asks this about every ordered pair of record carriers a program
1591
+ * has (`conversion/build.ts`), and TypeScript's own compiler has enough of
1592
+ * them, with enough fields each, that a key string, a fresh set and a linear
1593
+ * field scan per target field, per pair, WAS the representations stage (the
1594
+ * main thread sat at 100% for an hour past the previous run's total). Fields
1595
+ * are matched through a per-record index, and a top-level answer is
1596
+ * remembered per pair, which `recastedRecordText` reads back for the pairs
1597
+ * the graph kept.
1598
+ */
1599
+ const recordsRecastableIn = (source, target, seen) => {
1600
+ if (source.kind !== 'record' || target.kind !== 'record')
1601
+ return false;
1602
+ if (source.accessors.length > 0 || target.accessors.length > 0)
1603
+ return false;
1604
+ if (seen !== null) {
1605
+ const pairKey = `${representationKey(source)}->${representationKey(target)}`;
1606
+ if (seen.has(pairKey))
1607
+ return true;
1608
+ seen.add(pairKey);
1609
+ return recordFieldsRecastable(source, target, seen);
1610
+ }
1611
+ const remembered = recordRecastAnswers.get(source)?.get(target);
1612
+ if (remembered !== undefined)
1613
+ return remembered;
1614
+ // A container field re-enters here through `convertedValueText`, outside
1615
+ // the `seen` descent; the pair already under test is admitted on the same
1616
+ // coinductive assumption.
1617
+ const pending = recordRecastPending.get(source);
1618
+ if (pending?.has(target))
1619
+ return true;
1620
+ if (pending === undefined)
1621
+ recordRecastPending.set(source, new WeakSet([target]));
1622
+ else
1623
+ pending.add(target);
1624
+ let answer;
1625
+ try {
1626
+ answer = recordFieldsRecastable(source, target, null);
1627
+ }
1628
+ finally {
1629
+ recordRecastPending.get(source)?.delete(target);
1630
+ }
1631
+ let byTarget = recordRecastAnswers.get(source);
1632
+ if (byTarget === undefined) {
1633
+ byTarget = new WeakMap();
1634
+ recordRecastAnswers.set(source, byTarget);
1635
+ }
1636
+ byTarget.set(target, answer);
1637
+ return answer;
1638
+ };
1639
+ const recordFieldsRecastable = (source, target, seen) => {
1640
+ const sourceFields = fieldIndexOf(source);
1641
+ let descent = seen;
1642
+ for (const field of target.fields) {
1643
+ const match = sourceFields.get(field.key);
1644
+ if (match === undefined) {
1645
+ if (field.required || absentFieldText(field.value) === null)
1646
+ return false;
1647
+ continue;
1648
+ }
1649
+ if (!match.required && field.required)
1650
+ return false;
1651
+ if (match.value === field.value || representationKey(match.value) === representationKey(field.value))
1652
+ continue;
1653
+ // The first descent starts the guard with this pair on it, exactly as the
1654
+ // eager form recorded the pair before walking any field.
1655
+ descent ??= new Set([`${representationKey(source)}->${representationKey(target)}`]);
1656
+ if (!recastableFieldValue(match.value, field.value, descent))
1657
+ return false;
1658
+ }
1659
+ return true;
1660
+ };
1661
+ export const recordsRecastable = (source, target) => recordsRecastableIn(source, target, null);
1662
+ /**
1663
+ * Whether an admitted record recast moves any field across the dynamic
1664
+ * boundary: boxes a native field into a `dynamic` destination (the
1665
+ * `to.kind === 'dynamic'` admission of `recastableFieldValue`) or unboxes a
1666
+ * `dynamic` source field into a native one -- through an optional's payload,
1667
+ * a nested record, or a container's element.
1668
+ *
1669
+ * `targets/cpp/conversions.ts` states `nativeFieldProtocol: 'unused'` for a
1670
+ * record recast, which `ir/reflection-demand.ts` reads as "this copy asks no
1671
+ * dynamic field table, so the fields it copies keep their sealed keys-only
1672
+ * protocol". That is true of a recast that only copies native storage. A
1673
+ * recast that boxes a field hands the boxed payload to every later dynamic
1674
+ * read, and those reads DO go through the payload's field protocol -- so the
1675
+ * claim must not be made for it, or the boxed child is emitted without the
1676
+ * protocol the box needs (`reflection-demand.test.ts`, "a boxed destination
1677
+ * still exposes its payload").
1678
+ */
1679
+ export const recordRecastCrossesDynamic = (source, target) => recastCrossesDynamicIn(source, target, new Set());
1680
+ const recastCrossesDynamicIn = (source, target, seen) => {
1681
+ if (source.kind !== 'record' || target.kind !== 'record')
1682
+ return false;
1683
+ const pairKey = `${representationKey(source)}->${representationKey(target)}`;
1684
+ if (seen.has(pairKey))
1685
+ return false;
1686
+ seen.add(pairKey);
1687
+ for (const field of target.fields) {
1688
+ const match = source.fields.find((candidate) => candidate.key === field.key);
1689
+ if (match !== undefined && recastFieldCrossesDynamic(match.value, field.value, seen))
1690
+ return true;
1691
+ }
1692
+ return false;
1693
+ };
1694
+ const recastFieldCrossesDynamic = (from, to, seen) => {
1695
+ const source = from.kind === 'optional' ? from.payload : from;
1696
+ const target = to.kind === 'optional' ? to.payload : to;
1697
+ if (representationKey(source) === representationKey(target))
1698
+ return false;
1699
+ if ((source.kind === 'dynamic') !== (target.kind === 'dynamic'))
1700
+ return true;
1701
+ if (source.kind === 'array-object' && target.kind === 'array-object')
1702
+ return recastFieldCrossesDynamic(source.element, target.element, seen);
1703
+ if (source.kind === 'dictionary' && target.kind === 'dictionary')
1704
+ return recastFieldCrossesDynamic(source.value, target.value, seen);
1705
+ return recastCrossesDynamicIn(source, target, seen);
1706
+ };
1707
+ const recastedRecordText = (source, target, text, depth = 0) => {
1708
+ if (!recordsRecastable(source, target))
1709
+ return null;
1710
+ const arrow = source.ownership === 'shared-refcount' ? '->' : '.';
1711
+ const holder = `gea_from${depth === 0 ? '' : `_${depth}`}`;
1712
+ const recastFieldText = (from, to, read, nestedDepth) => {
1713
+ if (representationKey(from) === representationKey(to)) {
1714
+ const arithmetic = to.kind === 'scalar' && to.domain !== 'bigint';
1715
+ return arithmetic ? `static_cast<${cppTypeOf(to)}>(${read})` : read;
1716
+ }
1717
+ if (to.kind === 'optional') {
1718
+ // The absent value, admitted by `recastableFieldValue` for an
1719
+ // `undefined` source; the source field is storage-free and is not read.
1720
+ if (from.kind === 'undefined')
1721
+ return `${cppTypeOf(to)}()`;
1722
+ const fromPayload = from.kind === 'optional' ? from.payload : from;
1723
+ const payloadRead = from.kind === 'optional' ? `(*${read})` : read;
1724
+ const converted = recastFieldText(fromPayload, to.payload, payloadRead, nestedDepth);
1725
+ if (converted === null)
1726
+ return null;
1727
+ const targetType = cppTypeOf(to);
1728
+ return from.kind === 'optional'
1729
+ ? `(${read}.has_value() ? ${targetType}(${converted}) : ${targetType}())`
1730
+ : `${targetType}(${converted})`;
1731
+ }
1732
+ if (to.kind === 'tagged-union') {
1733
+ const index = soleArmIndexFor(to, from);
1734
+ return index === null ? null : `${cppTypeOf(to)}::ofArm<${index}>(${read})`;
1735
+ }
1736
+ if (to.kind === 'dynamic')
1737
+ return tryCandidateText(() => dynamicCarrierBoxText(from, read));
1738
+ if (from.kind === 'function-value-dispatch' && to.kind === 'function-value-dispatch')
1739
+ return resultAdaptedCallableText(from, to, read);
1740
+ if ((from.kind === 'array-object' && to.kind === 'array-object') ||
1741
+ (from.kind === 'dictionary' && to.kind === 'dictionary') ||
1742
+ from.kind === 'dynamic')
1743
+ return tryCandidateText(() => convertedValueText(from, to, read)) ?? emptyContainerRecastText(from, to, read);
1744
+ if (from.kind !== 'record' || to.kind !== 'record')
1745
+ return null;
1746
+ return recastedRecordText(from, to, read, nestedDepth + 1);
1747
+ };
1748
+ const reads = target.fields.map((field) => {
1749
+ const read = `${holder}${arrow}${cppRecordFieldName(field.key)}`;
1750
+ const match = source.fields.find((candidate) => candidate.key === field.key);
1751
+ // A field the target declares and the source does not: the carrier's own
1752
+ // absent value, which `recordsRecastable` already proved exists.
1753
+ if (match === undefined)
1754
+ return absentFieldText(field.value);
1755
+ // Inside braces [dcl.init.list]/7 forbids a narrowing every other argument
1756
+ // position allows: a field the integer census holds in a `long long` is a
1757
+ // hard error written into a `double` member, though both carriers say
1758
+ // `scalar(number)`. `static_cast` spells that same conversion explicitly --
1759
+ // `emit-arrays.ts`'s `packElementText` answers the identical rule this way,
1760
+ // `bigint` excluded on its terms too, and it is a no-op where both agree.
1761
+ const converted = recastFieldText(match.value, field.value, read, depth);
1762
+ if (converted === null || field.value.kind !== 'scalar' || field.value.domain === 'bigint')
1763
+ return converted;
1764
+ // `cppTypeOf` answers what the CARRIER is; the struct answers what the
1765
+ // MEMBER is, and the integer-storage census makes those two disagree --
1766
+ // a `scalar(number)` field the census proved integral is declared
1767
+ // `long long`. Inside braces that disagreement is a hard error
1768
+ // ([dcl.init.list]/7), not a silent conversion. Naming the member's own
1769
+ // declared type is the only spelling that cannot drift from it; the
1770
+ // census is not reachable from here and a second opinion about which
1771
+ // fields it narrowed is exactly the drift that keeps producing defects.
1772
+ return `static_cast<decltype(${cppRecordStructName(target.shapeId)}::${cppRecordFieldName(field.key)})>(${converted})`;
1773
+ });
1774
+ if (reads.some((read) => read === null))
1775
+ return null;
1776
+ const presences = target.fields.flatMap((field) => {
1777
+ if (field.required)
1778
+ return [];
1779
+ const match = source.fields.find((candidate) => candidate.key === field.key);
1780
+ if (match === undefined)
1781
+ return ['false'];
1782
+ return [match.required ? 'true' : `${holder}${arrow}${cppRecordFieldPresenceName(field.key)}`];
1783
+ });
1784
+ const structure = `${cppRecordStructName(target.shapeId)}{${[...reads, ...presences].join(', ')}}`;
1785
+ const built = target.ownership === 'shared-refcount' ? `gea::makeRef<${cppRecordStructName(target.shapeId)}>(${structure})` : structure;
1786
+ return `[](const ${cppTypeOf(source)}& ${holder}) { return ${built}; }(${text})`;
1787
+ };
1788
+ /**
1789
+ * A record recast into a dictionary, one level out from `recordsRecastable`
1790
+ * above.
1791
+ *
1792
+ * A `record`'s field set is CLOSED and known at compile time; a `dictionary`'s
1793
+ * is open and string/number-keyed. Going from the closed shape to the open one
1794
+ * loses no information -- every field the record has, the dictionary can hold
1795
+ * -- so the direction is always sound, unlike the reverse (`derive.ts`'s own
1796
+ * `dictionary` case: "Product needs a complete frozen field list", never
1797
+ * derivable from an open one). Concretely this is `return {}` reaching a
1798
+ * function whose declared result is `Record<string, V>`: TypeScript types the
1799
+ * empty literal as the record with zero fields it is, not as the dictionary
1800
+ * its ABI slot wants, and the two carriers stay genuinely different even
1801
+ * though the empty case rebuilds to zero inserts.
1802
+ *
1803
+ * Every field's own value must have an installed conversion into the
1804
+ * dictionary's declared value carrier. This is the same store each field
1805
+ * would perform if the source literal had been contextualized as the index
1806
+ * signature in the first place: a scalar into a declared `any` slot boxes at
1807
+ * that explicit dynamic boundary, and a typed slot keeps its ordinary native
1808
+ * conversion. A field with no such conversion refuses the whole recast before
1809
+ * any output is rendered.
1810
+ */
1811
+ export const recordCastableToDictionary = (source, target) => {
1812
+ if (source.accessors.length > 0)
1813
+ return false;
1814
+ // A record field names a compile-time key. A symbol-indexed dictionary is
1815
+ // keyed by the runtime Symbol value, and a field's `sym(<declaration>)`
1816
+ // identity cannot manufacture that value at this conversion site. Admitting
1817
+ // the cast would either stringify the symbol (wrong identity) or box it.
1818
+ if (target.key === 'symbol')
1819
+ return source.fields.length === 0;
1820
+ if (target.key === 'number' && source.fields.some((field) => !isCanonicalNumberPropertyKeyText(field.key)))
1821
+ return false;
1822
+ if (containsUnresolved(source) || containsUnresolved(target))
1823
+ return false;
1824
+ return source.fields.every((field) => {
1825
+ try {
1826
+ return convertedValueText(field.value, target.value, 'gea_record_field') !== null;
1827
+ }
1828
+ catch (error) {
1829
+ // This function is queried while the conversion graph is being built.
1830
+ // A nested narrowing recipe can reject its own source/target pair by
1831
+ // throwing the backend's named refusal; in this predicate that means the
1832
+ // field is not convertible, not that graph construction itself failed.
1833
+ // Unexpected errors still escape so internal defects remain visible.
1834
+ if (isCppEmitBlockedError(error))
1835
+ return false;
1836
+ throw error;
1837
+ }
1838
+ });
1839
+ };
1840
+ const recastedRecordToDictionaryText = (source, target, text) => {
1841
+ if (!recordCastableToDictionary(source, target))
1842
+ return null;
1843
+ const fieldArrow = source.ownership === 'shared-refcount' ? '->' : '.';
1844
+ const dictArrow = target.ownership === 'shared-refcount' ? '->' : '.';
1845
+ const container = target.key === 'number' ? 'gea::NumericDictionary' : target.key === 'symbol' ? 'gea::SymbolDictionary' : 'gea::Dictionary';
1846
+ const storage = `${container}<${cppTypeOf(target.value)}>`;
1847
+ const inserts = source.fields.map((field) => {
1848
+ if (target.key === 'symbol')
1849
+ return null;
1850
+ const key = cppStringLiteral(field.key);
1851
+ const read = `gea_from${fieldArrow}${cppRecordFieldName(field.key)}`;
1852
+ const converted = convertedValueText(field.value, target.value, read);
1853
+ if (converted === null)
1854
+ return null;
1855
+ return `gea_dict${dictArrow}operator[](${key}) = ${converted};`;
1856
+ });
1857
+ if (inserts.some((insert) => insert === null))
1858
+ return null;
1859
+ const statements = inserts.join(' ');
1860
+ const construct = target.ownership === 'shared-refcount' ? `auto gea_dict = gea::makeRef<${storage}>();` : `${storage} gea_dict{};`;
1861
+ return `[](const ${cppTypeOf(source)}& gea_from) { ${construct} ${statements} return gea_dict; }(${text})`;
1862
+ };
1863
+ /**
1864
+ * An open dictionary recast into another open dictionary over the same key
1865
+ * domain whose VALUES it widens into -- `Record<string, string>` reaching a
1866
+ * slot declared `OutgoingHttpHeaders` (`{ [name: string]: string | number |
1867
+ * boolean | readonly string[] | undefined }`). TypeScript's `||` typing
1868
+ * subtype-reduces the two into the wider one, so `@hono/node-server`'s `get
1869
+ * headers()` -- `cache[2] || { 'content-type': defaultContentType }` where
1870
+ * the slot is `Record<string, string> | [string, string][] |
1871
+ * OutgoingHttpHeaders | undefined` -- publishes ONLY the wider dictionary as
1872
+ * the merge's arm, and both the literal and the slot's own string-dictionary
1873
+ * arm have to reach it. Nothing between the two carriers is a view: a
1874
+ * `gea::Dictionary<std::string>` and a `gea::Dictionary<gea::Optional<...>>`
1875
+ * are two template instantiations.
1876
+ *
1877
+ * One level out from `recordCastableToDictionary` above and admitted on the
1878
+ * same terms: every value the source holds converts into the target's value
1879
+ * carrier by the store it would have performed had the entry been written
1880
+ * against the wider index signature in the first place, and the rebuild is a
1881
+ * fresh table -- the same fresh table the record pour builds, with the same
1882
+ * consequence, that a program which keeps writing the SOURCE afterwards and
1883
+ * reads the widened copy does not see the write. The record pour already
1884
+ * stands on that ground for the identical pair one shape narrower; this does
1885
+ * not extend it further.
1886
+ *
1887
+ * String keys only. `gea::NumericDictionary` and `gea::SymbolDictionary` are
1888
+ * different containers with their own iteration, and nothing has asked for
1889
+ * them; a pair over either refuses by name rather than guessing a spelling.
1890
+ */
1891
+ export const dictionaryCastableToDictionary = (source, target) => {
1892
+ if (source.key !== 'string' || target.key !== 'string')
1893
+ return false;
1894
+ if (representationKey(source.value) === representationKey(target.value))
1895
+ return false;
1896
+ if (containsUnresolved(source) || containsUnresolved(target))
1897
+ return false;
1898
+ try {
1899
+ return convertedValueText(source.value, target.value, 'gea_entry_value') !== null;
1900
+ }
1901
+ catch (error) {
1902
+ // Queried while the conversion graph is being built, exactly as
1903
+ // `recordCastableToDictionary` is: a nested recipe's named refusal means
1904
+ // the value pair does not convert, not that graph construction failed.
1905
+ if (isCppEmitBlockedError(error))
1906
+ return false;
1907
+ throw error;
1908
+ }
1909
+ };
1910
+ const recastedDictionaryText = (source, target, text) => {
1911
+ if (!dictionaryCastableToDictionary(source, target))
1912
+ return null;
1913
+ const converted = convertedValueText(source.value, target.value, 'gea_entry.second');
1914
+ if (converted === null)
1915
+ return null;
1916
+ const storage = `gea::Dictionary<${cppTypeOf(target.value)}>`;
1917
+ const construct = target.ownership === 'shared-refcount' ? `auto gea_dict = gea::makeRef<${storage}>();` : `${storage} gea_dict{};`;
1918
+ const dictArrow = target.ownership === 'shared-refcount' ? '->' : '.';
1919
+ // `gea::Dictionary`'s own `const_iterator` walks entries in creation order,
1920
+ // which is the order the language enumerates them in and the order the copy
1921
+ // must therefore insert them in.
1922
+ const range = source.ownership === 'shared-refcount' ? '*gea_from' : 'gea_from';
1923
+ return (`[](const ${cppTypeOf(source)}& gea_from) { ${construct} ` +
1924
+ `for (const auto& gea_entry : ${range}) { gea_dict${dictArrow}operator[](gea_entry.first) = ${converted}; } ` +
1925
+ `return gea_dict; }(${text})`);
1926
+ };
1927
+ /**
1928
+ * The text a `convert` renders to, or `null` when nothing installed performs it.
1929
+ *
1930
+ * A convert is a carrier change the graph asked for by name -- a merge that
1931
+ * keeps one side, an optional chain's absent arm -- so unlike a narrowing read
1932
+ * it may widen, and unlike a widening store it may be handed an absence rather
1933
+ * than a payload.
1934
+ *
1935
+ * The absence cases are not spelled here. They used to be, and that private
1936
+ * copy was the defect: `widenedStoreText` -- which every ordinary store site
1937
+ * asks, and which this function's own tail already delegates to -- carried no
1938
+ * absence rule, so a `null` reaching a binding, a field or a return came out
1939
+ * unconverted while the same `null` reaching an explicit `convert` came out
1940
+ * right. One question, two answers, and only the quieter one was wrong. The
1941
+ * rule now lives once, on the store side, and this function reaches it through
1942
+ * the same tail it already used for every other widening.
1943
+ */
1944
+ /**
1945
+ * The calling convention of a carrier the backend spells
1946
+ * `gea::CallableObject<...>`, or `null` for anything else.
1947
+ *
1948
+ * The constructor carriers are deliberately absent even though they also state
1949
+ * one convention: they spell `gea::ConstructorObject<...>`, which declares no
1950
+ * converting constructor, so the rule below does not hold for them.
1951
+ * `function-and-constructor` is absent for a different reason -- it states two
1952
+ * conventions, and picking one here would answer a question the value does not.
1953
+ */
1954
+ export const callableObjectAbi = (representation) => {
1955
+ switch (representation.kind) {
1956
+ case 'function':
1957
+ case 'function-family':
1958
+ case 'function-value-family':
1959
+ case 'function-value-dispatch':
1960
+ return representation.abi;
1961
+ default:
1962
+ return null;
1963
+ }
1964
+ };
1965
+ /**
1966
+ * The callable half of a value that also carries `[[Construct]]`.
1967
+ *
1968
+ * The target's callable ABI is the authority that makes selecting this half
1969
+ * unambiguous: its exact key must equal the source's published `call` ABI.
1970
+ * The new `CallableObject` keeps the same function pointer, environment and
1971
+ * owning reference, so extracting the view preserves both behavior and
1972
+ * lifetime while dropping only the construction entry the target cannot use.
1973
+ */
1974
+ const callablePartText = (source, target, text) => {
1975
+ if (source.kind !== 'function-and-constructor')
1976
+ return null;
1977
+ const targetAbi = callableObjectAbi(target);
1978
+ if (targetAbi === null || abiKey(source.call) !== abiKey(targetAbi))
1979
+ return null;
1980
+ const sourceType = cppTypeOf(source);
1981
+ const targetType = cppTypeOf(target);
1982
+ return (`[](const ${sourceType}& gea_from) { ${targetType} gea_callable{}; ` +
1983
+ `gea_callable.invoke = gea_from.invoke; gea_callable.environment = gea_from.environment; ` +
1984
+ `gea_callable.environmentOwner = gea_from.environmentOwner; gea_callable.functionObject = gea_from.functionObject; return gea_callable; }(${text})`);
1985
+ };
1986
+ /**
1987
+ * The same positional read, for a rest slot carried as a closed TUPLE rather
1988
+ * than an open Array.
1989
+ *
1990
+ * `Function.prototype.call` is where that shape comes from. Under
1991
+ * `strictBindCallApply` its signature is `call<T, A extends any[], R>(this:
1992
+ * (this: T, ...args: A) => R, thisArg: T, ...args: A): R`, and the checker
1993
+ * infers `A` from the call's OWN arguments -- so
1994
+ * `callback.call(thisArg, value, key, parent)` states the `this` slot as
1995
+ * `(this: unknown, ...args: [string, string, Headers]) => void`, whose rest
1996
+ * parameter derives to a `record` of numeric keys and not to the
1997
+ * `array-object` the open `(...args: any[])` idiom produces. The positions are
1998
+ * the same positions; only the carrier differs.
1999
+ *
2000
+ * A source parameter past the tuple's end is refused rather than defaulted:
2001
+ * the open-array case has an element TYPE to default, while a tuple that does
2002
+ * not declare the position states nothing to stand in for it. An optional
2003
+ * field is refused for the same reason -- its absence is a presence flag the
2004
+ * caller must read, not a value.
2005
+ *
2006
+ * `@hono/node-server`'s `RequestHeaders.forEach` is the measured case: it
2007
+ * forwards to the caller's `(value, key, parent) => void` through
2008
+ * `callback.call(thisArg, value, key, this)`.
2009
+ */
2010
+ const restTupleFieldActual = (slot, restOrdinal, position, parameter) => {
2011
+ const field = slot.fields[position];
2012
+ if (field === undefined || !field.required)
2013
+ return null;
2014
+ const arrow = slot.ownership === 'shared-refcount' ? '->' : '.';
2015
+ const access = `${adapterFormalName(restOrdinal)}${arrow}${cppRecordFieldName(field.key)}`;
2016
+ if (cppTypeOf(field.value) === cppTypeOf(parameter.value))
2017
+ return access;
2018
+ try {
2019
+ return convertedValueText(field.value, parameter.value, access);
2020
+ }
2021
+ catch (error) {
2022
+ if (isCppEmitBlockedError(error))
2023
+ return null;
2024
+ throw error;
2025
+ }
2026
+ };
2027
+ /**
2028
+ * One source parameter read out of the slot's own rest Array -- the positional
2029
+ * read ECMA-262 performs when a function is called with an argument list the
2030
+ * caller assembled. A position the array does not reach is `undefined`, which
2031
+ * is exactly what a JavaScript call passes there, so the element's own default
2032
+ * stands in rather than an invented value.
2033
+ */
2034
+ const restElementActual = (to, restOrdinal, position, parameter) => {
2035
+ const slot = to.parameters[restOrdinal];
2036
+ if (slot === undefined)
2037
+ return null;
2038
+ if (slot.value.kind === 'record')
2039
+ return restTupleFieldActual(slot.value, restOrdinal, position, parameter);
2040
+ if (slot.value.kind !== 'array-object')
2041
+ return null;
2042
+ const element = slot.value.element;
2043
+ const rest = adapterFormalName(restOrdinal);
2044
+ const elementType = cppTypeOf(element);
2045
+ const access = `(${rest}.get() == nullptr || ${rest}->size() <= static_cast<std::size_t>(${position}) ` +
2046
+ `? ${elementType}() : ${rest}->at(${position}))`;
2047
+ if (elementType === cppTypeOf(parameter.value))
2048
+ return access;
2049
+ try {
2050
+ return convertedValueText(element, parameter.value, access);
2051
+ }
2052
+ catch (error) {
2053
+ if (isCppEmitBlockedError(error))
2054
+ return null;
2055
+ throw error;
2056
+ }
2057
+ };
2058
+ /** `gea::detail::emptyContainerAs` for a shared array or dictionary field whose elements do not convert. */
2059
+ const emptyContainerRecastText = (from, to, read) => {
2060
+ const container = (carrier) => ((carrier.kind === 'array-object' && !carrier.recursive && !carrier.extension) ||
2061
+ (carrier.kind === 'dictionary' && carrier.key === 'string')) &&
2062
+ carrier.ownership === 'shared-refcount';
2063
+ if (from.kind !== to.kind || !container(from) || !container(to))
2064
+ return null;
2065
+ return `gea::detail::emptyContainerAs<${cppTypeOf(to, 'owned')}>(${read}, "a container recast")`;
2066
+ };
2067
+ /**
2068
+ * An object the program asserts (`env as unknown as Bindings`) or narrows
2069
+ * (`body instanceof Uint8Array` over a declared `string | ReadableStream`)
2070
+ * into a union none of whose arms it can become, even as a structural view. The
2071
+ * assertion went through `unknown`, so it is carried as that: boxed, then
2072
+ * read back out with the exact-type check every box read makes, which aborts
2073
+ * by name when the value is not what the program claimed rather than
2074
+ * inventing a layout. The last resort after `structuralRecordViewText`, and
2075
+ * asked only there and in the census's matching fallback.
2076
+ */
2077
+ export const boxedAssertionText = (source, target, text) => {
2078
+ const objectLike = (carrier) => (carrier.kind === 'record' || carrier.kind === 'native-record-ref' || carrier.kind === 'class-ref') &&
2079
+ carrier.ownership === 'shared-refcount';
2080
+ const union = target.kind === 'optional' ? target.payload : target;
2081
+ if (!objectLike(source) || union.kind !== 'tagged-union')
2082
+ return null;
2083
+ const boxed = dynamicCarrierBoxText(source, text);
2084
+ if (boxed === null)
2085
+ return null;
2086
+ return tryCandidateText(() => convertedValueText({ kind: 'dynamic', reason: 'declared-any-never-narrowed' }, target, boxed));
2087
+ };
2088
+ /** The slot parameters from `first` on, packed into the source's rest Array -- see `restPack` in `resultAdapterOf`. */
2089
+ const restPackedActual = (slots, first, element) => {
2090
+ const elementType = cppTypeOf(element);
2091
+ const elements = [];
2092
+ for (const [offset, slot] of slots.entries()) {
2093
+ const formal = adapterFormalName(first + offset);
2094
+ if (cppTypeOf(slot.value) === elementType) {
2095
+ elements.push(formal);
2096
+ continue;
2097
+ }
2098
+ try {
2099
+ const converted = convertedValueText(slot.value, element, formal);
2100
+ if (converted === null)
2101
+ return null;
2102
+ elements.push(converted);
2103
+ }
2104
+ catch (error) {
2105
+ if (isCppEmitBlockedError(error))
2106
+ return null;
2107
+ throw error;
2108
+ }
2109
+ }
2110
+ return `gea::arrayOf<${elementType}>({${elements.join(', ')}})`;
2111
+ };
2112
+ /** The lambda's own names, kept out of every body-local namespace this text can be spliced into. */
2113
+ const adapterEnvironmentName = 'gea_adapt_environment';
2114
+ const adapterReceiverName = 'gea_adapt_receiver';
2115
+ const adapterResultName = 'gea_adapt_result';
2116
+ const adapterFormalName = (ordinal) => `gea_adapt_arg_${ordinal}`;
2117
+ /** The local a promise-to-promise conversion binds its source to, for the same reason the adapter names above are what they are. */
2118
+ const promiseSourceName = 'gea_promise_source';
2119
+ /** `promiseSourceName`'s twins: the promise a payload reconciliation settles, and the rejection it forwards. */
2120
+ const promiseTargetName = 'gea_promise_target';
2121
+ const promiseRejectionName = 'gea_promise_rejection';
2122
+ /** The fulfilment value a pending source hands the payload reconciliation, once it exists. */
2123
+ const promiseValueName = 'gea_promise_value';
2124
+ /**
2125
+ * The local `recastedUnionText` binds its source to, and the alias it binds
2126
+ * the target's spelling to, once per multi-arm recast -- not per arm.
2127
+ *
2128
+ * A fixed name rather than a generated one, same as `promiseSourceName`
2129
+ * above: every use sits inside its own IIFE's block scope (or, for a nested
2130
+ * inner-union arm, inside an IIFE nested textually inside this one), and a
2131
+ * C++ inner scope shadowing an outer one by the same name is ordinary,
2132
+ * unambiguous re-declaration -- not a collision -- because the argument that
2133
+ * feeds the inner IIFE is evaluated in the OUTER scope before the inner
2134
+ * parameter's own binding takes over.
2135
+ */
2136
+ const recastUnionSourceName = 'gea_recast_source';
2137
+ const recastUnionAliasName = 'GeaRecastArm';
2138
+ /**
2139
+ * A callable whose declared parameters or result convert into its slot's ABI.
2140
+ *
2141
+ * `gea::CallableObject` declares four converting constructors and every one of
2142
+ * them hands the source's result through untouched -- they change the ARITY
2143
+ * (`dropArguments`, `dropTrailingArguments`, `spreadRestOverLeading`) or read
2144
+ * a result the runtime can already reach by arm index (`widenResultIntoArm`,
2145
+ * whose `ResultWidensIntoArm` trait needs a `TaggedUnion` target whose arm IS
2146
+ * the source result). None of them can BOX one, and that is not an omission
2147
+ * to fix in the header: which `Value::Tag` a carrier boxes to is
2148
+ * `dynamicTagFor`'s table, it lives in this emitter, and a second copy of it
2149
+ * in `gea_runtime.h` is precisely the two-authorities defect this compiler
2150
+ * exists to avoid.
2151
+ *
2152
+ * hono's `H = Handler | MiddlewareHandler` is the case that needs it:
2153
+ * `Handler<E, P, I, R = any>` defaults its result parameter to `any`, so both
2154
+ * arms take `(Context, Next)` and return `any`/`Promise<any>` while every
2155
+ * handler a program writes returns something concrete. Same frame, boxed
2156
+ * result.
2157
+ *
2158
+ * So the adapter is rendered HERE, where the tag table is, through
2159
+ * `CallableObject`'s own public `(Invoke, void*)` constructor: a CAPTURELESS
2160
+ * lambda (`+[]` decays it to the `Result (*)(void*, Arguments...)` pointer
2161
+ * that constructor takes) over a heap copy of the source, which is the exact
2162
+ * environment-carrying shape the header's own four constructors use.
2163
+ *
2164
+ * What it refuses, and why each is not a case to widen into later:
2165
+ *
2166
+ * - A source receiver that the target does not supply. A receiver-free
2167
+ * source, however, can fill a method slot: its ABI proves the body consumes
2168
+ * no dynamic receiver, so the adapter ignores the target's receiver. This
2169
+ * is how an arrow assigned to an instance method keeps its lexical `this`.
2170
+ * Two receiver-bearing conventions adapt the
2171
+ * target receiver into the source receiver exactly like an ordinary
2172
+ * parameter; it is the first physical formal in both ABIs.
2173
+ * - A source frame wider than its slot, or a different rest placement. The
2174
+ * adapter may ignore target arguments after the source's declared prefix,
2175
+ * just as an ordinary JavaScript call does; it never invents arguments the
2176
+ * source declared but the target does not supply.
2177
+ * - A void source result flowing anywhere except void or dynamic. Calling a
2178
+ * JavaScript function that returns no value produces `undefined`, so a
2179
+ * genuinely dynamic result receives the runtime's default Undefined box;
2180
+ * no other concrete carrier may be invented from it.
2181
+ * - A pair whose parameters and result already share C++ spellings. It needs
2182
+ * no adapter and must not get one, or an identity store would allocate.
2183
+ */
2184
+ const resultAdapterOf = (source, target) => {
2185
+ const from = callableObjectAbi(source);
2186
+ const to = callableObjectAbi(target);
2187
+ if (!from || !to)
2188
+ return null;
2189
+ if (from.receiver !== null && to.receiver === null)
2190
+ return null;
2191
+ const ignoresReceiver = from.receiver === null && to.receiver !== null;
2192
+ // A fixed-arity source filling a slot whose trailing frame is ONE rest array:
2193
+ // `(...args: any[]) => void`, JavaScript's "some function, arguments not my
2194
+ // business" idiom. tsc's whole `Debug` family passes itself through it --
2195
+ // `assertIsDefined(value, message, stackCrawlMark || checkDefined)` -- and
2196
+ // every such argument refused to lower, 265 rows in `debug.ts` alone, because
2197
+ // the rest placements did not match. They do not have to: the language
2198
+ // defines the call exactly, the slot binds one Array and the source reads its
2199
+ // own parameters positionally out of it. The rest ELEMENT is what makes this
2200
+ // sound rather than a guess -- a slot declared `any[]` is a genuine dynamic
2201
+ // boundary, so each source parameter is filled through the same conversion an
2202
+ // ordinary dynamic argument already takes, and a slot whose element cannot
2203
+ // convert leaves `null` here and keeps the refusal.
2204
+ // The slot's trailing frame is one rest parameter whichever way the checker
2205
+ // spelled it: an open `any[]` Array, or the closed TUPLE
2206
+ // `Function.prototype.call`'s `A extends any[]` infers from the call's own
2207
+ // arguments -- see `restTupleFieldActual` for why those are the same
2208
+ // positions in two carriers.
2209
+ const restSlotKind = to.restFrom === null ? undefined : to.parameters[to.restFrom]?.value.kind;
2210
+ const restSpread = from.restFrom === null &&
2211
+ to.restFrom !== null &&
2212
+ to.parameters.length === to.restFrom + 1 &&
2213
+ from.parameters.length >= to.restFrom &&
2214
+ (restSlotKind === 'array-object' || restSlotKind === 'record');
2215
+ // A source formal past the end of the slot's frame is OMITTED at every call
2216
+ // the slot admits, and ECMA-262 10.2.1 binds an omitted parameter to
2217
+ // `undefined` -- a value the language supplies, not one the adapter invents.
2218
+ // So the refusal is not "the source frame is wider" but "a source formal
2219
+ // past the slot's frame cannot hold the absence", which `cppUndefinedIn`
2220
+ // (the one authority `paddedArguments` already asks at a direct call that
2221
+ // omits a trailing argument) answers. hono's `utils/url.ts` is the measured
2222
+ // case: `export const getQueryParam: (url, key?) => ... = _getQueryParam as
2223
+ // (...)`, where `_getQueryParam` declares a third `multiple?: boolean`; the
2224
+ // slot is the only contract any caller has, so the third formal is
2225
+ // `Optional<bool>()` at every one of them.
2226
+ //
2227
+ // A rest parameter is not an omitted argument -- what a caller owes one is
2228
+ // an allocated array -- so a source with a rest keeps the arity refusal.
2229
+ const omitted = !restSpread && from.restFrom === null && from.parameters.length > to.parameters.length;
2230
+ // The mirror: a source whose trailing frame is one rest Array filling a
2231
+ // slot of fixed parameters. The slot's parameters from the rest position on
2232
+ // are the arguments the source's Array binds, so the adapter packs them.
2233
+ // An optional slot parameter is packed even when a caller omitted it, so
2234
+ // the Array's length counts every slot position.
2235
+ const restParameter = from.restFrom === null ? undefined : from.parameters[from.restFrom];
2236
+ const restPack = !restSpread &&
2237
+ from.restFrom !== null &&
2238
+ to.restFrom === null &&
2239
+ from.parameters.length === from.restFrom + 1 &&
2240
+ to.parameters.length >= from.restFrom &&
2241
+ restParameter?.value.kind === 'array-object' &&
2242
+ restParameter.value.ownership === 'shared-refcount' &&
2243
+ !restParameter.value.recursive;
2244
+ if (!restSpread && !restPack) {
2245
+ if (from.restFrom !== to.restFrom)
2246
+ return null;
2247
+ if (from.parameters.length > to.parameters.length && !omitted)
2248
+ return null;
2249
+ if (omitted && from.parameters.slice(to.parameters.length).some((parameter) => cppUndefinedIn(parameter.value) === null))
2250
+ return null;
2251
+ }
2252
+ let receiverActual = null;
2253
+ if (from.receiver !== null && to.receiver !== null) {
2254
+ if (cppTypeOf(from.receiver) === cppTypeOf(to.receiver)) {
2255
+ receiverActual = adapterReceiverName;
2256
+ }
2257
+ else {
2258
+ try {
2259
+ receiverActual = convertedValueText(to.receiver, from.receiver, adapterReceiverName);
2260
+ }
2261
+ catch (error) {
2262
+ if (isCppEmitBlockedError(error))
2263
+ return null;
2264
+ throw error;
2265
+ }
2266
+ if (receiverActual === null)
2267
+ return null;
2268
+ }
2269
+ }
2270
+ const actuals = [];
2271
+ const restOrdinal = to.restFrom;
2272
+ for (const [ordinal, parameter] of from.parameters.entries()) {
2273
+ if (restSpread && restOrdinal !== null && ordinal >= restOrdinal) {
2274
+ const spread = restElementActual(to, restOrdinal, ordinal - restOrdinal, parameter);
2275
+ if (spread === null)
2276
+ return null;
2277
+ actuals.push(spread);
2278
+ continue;
2279
+ }
2280
+ if (restPack && ordinal === from.restFrom && parameter.value.kind === 'array-object') {
2281
+ const packed = restPackedActual(to.parameters.slice(ordinal), ordinal, parameter.value.element);
2282
+ if (packed === null)
2283
+ return null;
2284
+ actuals.push(packed);
2285
+ continue;
2286
+ }
2287
+ const slot = to.parameters[ordinal];
2288
+ if (slot === undefined) {
2289
+ const absent = omitted ? cppUndefinedIn(parameter.value) : null;
2290
+ if (absent === null)
2291
+ return null;
2292
+ actuals.push(absent);
2293
+ continue;
2294
+ }
2295
+ const formal = adapterFormalName(ordinal);
2296
+ if (cppAbiParameterType(parameter) === cppAbiParameterType(slot)) {
2297
+ actuals.push(formal);
2298
+ continue;
2299
+ }
2300
+ try {
2301
+ const converted = convertedValueText(slot.value, parameter.value, formal);
2302
+ if (converted === null)
2303
+ return null;
2304
+ actuals.push(converted);
2305
+ }
2306
+ catch (error) {
2307
+ if (isCppEmitBlockedError(error))
2308
+ return null;
2309
+ throw error;
2310
+ }
2311
+ }
2312
+ let convertedResult;
2313
+ if (to.result.kind === 'void') {
2314
+ convertedResult = null;
2315
+ }
2316
+ else if (from.result.kind === 'void') {
2317
+ if (to.result.kind !== 'dynamic')
2318
+ return null;
2319
+ convertedResult = `${cppTypeOf(to.result)}()`;
2320
+ }
2321
+ else if (cppTypeOf(from.result) === cppTypeOf(to.result)) {
2322
+ convertedResult = adapterResultName;
2323
+ }
2324
+ else {
2325
+ try {
2326
+ convertedResult = convertedValueText(from.result, to.result, adapterResultName);
2327
+ }
2328
+ catch (error) {
2329
+ if (isCppEmitBlockedError(error))
2330
+ return null;
2331
+ throw error;
2332
+ }
2333
+ if (convertedResult === null)
2334
+ return null;
2335
+ }
2336
+ const receiverChanges = receiverActual !== null && receiverActual !== adapterReceiverName;
2337
+ const nativeReceiverTransport = from.receiver !== null && to.receiver !== null && classRefTransportKind(to.receiver, from.receiver) !== null;
2338
+ const ignoresParameters = from.parameters.length < to.parameters.length;
2339
+ const parametersChange = actuals.some((actual, ordinal) => actual !== adapterFormalName(ordinal));
2340
+ const resultChanges = from.result.kind !== to.result.kind || (from.result.kind !== 'void' && cppTypeOf(from.result) !== cppTypeOf(to.result));
2341
+ const nativeFieldProtocolUnused = !restSpread &&
2342
+ !restPack &&
2343
+ (!receiverChanges || nativeReceiverTransport) &&
2344
+ !parametersChange &&
2345
+ (from.result.kind === 'void' || to.result.kind === 'void' || cppTypeOf(from.result) === cppTypeOf(to.result));
2346
+ return ignoresReceiver || ignoresParameters || receiverChanges || parametersChange || resultChanges
2347
+ ? { from, to, receiverActual, actuals, convertedResult, nativeFieldProtocolUnused }
2348
+ : null;
2349
+ };
2350
+ /** Whether `source` reaches `target` through a result adapter -- see `resultAdapterOf`. Exported so `conversions.ts` asks the identical question the render below answers. */
2351
+ export const adaptsResultIntoTarget = (source, target) => resultAdapterOf(source, target) !== null;
2352
+ /** The same adapter selection used by the printer publishes its transport facts to the conversion census. */
2353
+ export const resultAdapterTransportOf = (source, target) => {
2354
+ const adapter = resultAdapterOf(source, target);
2355
+ return adapter === null ? null : { nativeConventions: adapter.nativeFieldProtocolUnused ? { from: adapter.from, to: adapter.to } : null };
2356
+ };
2357
+ /** The adapter itself, or `null` for a pair that needs none -- see `resultAdapterOf`. */
2358
+ export const resultAdaptedCallableText = (source, target, text) => {
2359
+ const adapter = resultAdapterOf(source, target);
2360
+ if (adapter === null)
2361
+ return null;
2362
+ const sourceType = cppTypeOf(source);
2363
+ const formals = [
2364
+ `void* ${adapterEnvironmentName}`,
2365
+ ...(adapter.to.receiver === null ? [] : [`${cppTypeOf(adapter.to.receiver)} ${adapterReceiverName}`]),
2366
+ ...adapter.to.parameters.map((parameter, ordinal) => `${cppAbiParameterType(parameter)} ${adapterFormalName(ordinal)}`)
2367
+ ];
2368
+ const sourceActuals = [...(adapter.receiverActual === null ? [] : [adapter.receiverActual]), ...adapter.actuals];
2369
+ const call = `static_cast<${sourceType}*>(${adapterEnvironmentName})->call(${sourceActuals.join(', ')})`;
2370
+ const body = adapter.from.result.kind === 'void'
2371
+ ? `${call};${adapter.convertedResult === null ? '' : ` return ${adapter.convertedResult};`}`
2372
+ : adapter.to.result.kind === 'void'
2373
+ ? `${call};`
2374
+ : `${cppTypeOf(adapter.from.result)} ${adapterResultName} = ${call}; return ${adapter.convertedResult};`;
2375
+ return `${cppTypeOf(target)}::adaptSource(${sourceType}{${text}}, [](${formals.join(', ')}) -> ${cppResultTypeOf(adapter.to.result)} { ${body} })`;
2376
+ };
2377
+ /**
2378
+ * A callable filling a slot that declares MORE parameters than it does, the
2379
+ * extra ones landing after every one the source itself names./**
2380
+ * A callable filling a slot that declares MORE parameters than it does, the
2381
+ * extra ones landing after every one the source itself names.
2382
+ *
2383
+ * ECMA-262 does not bind the extra arguments, so `() => void` really is a
2384
+ * `(time: number) => void` and TypeScript accepts it everywhere, and the same
2385
+ * is true one prefix over: hono's `Hono.getPath` merges two
2386
+ * `(request: Request) => string` values into a field declared
2387
+ * `(request: Request, options?: {env?}) => string` -- one shared leading
2388
+ * parameter, one trailing parameter neither physical function looks at.
2389
+ * `gea::CallableObject`'s own converting constructor (gea_runtime.h) performs
2390
+ * both shapes, so the value converts by being written where the wider one is
2391
+ * expected and needs no text of its own.
2392
+ *
2393
+ * The source's own parameters must be a TYPE-MATCHING PREFIX of the target's,
2394
+ * not merely a shorter count: converting an arbitrary prefix without proving
2395
+ * each shared position's type matches would let a wrong guess pass a value
2396
+ * into a slot expecting something else, which is what the runtime
2397
+ * constructor's own `IsTypePrefix` proves at compile time rather than
2398
+ * assumes, and this predicate re-asks the identical question so the two
2399
+ * cannot disagree about which pairs the constructor actually accepts. Every
2400
+ * position through the source's own last one has to agree, not only the
2401
+ * first -- `(a: string) => void` is not a sound stand-in for
2402
+ * `(a: string, b: number, c: string) => void` merely because position 0
2403
+ * matches; every position the source declares has to. The result and the
2404
+ * receiver must still agree exactly -- neither is dropped by a call, so a
2405
+ * difference in either is a real mismatch, not a binding the language elides.
2406
+ */
2407
+ /**
2408
+ * A method value -- its receiver first in its physical convention -- filling
2409
+ * a slot that declares the same convention WITHOUT one: `gea_runtime.h`'s
2410
+ * `CallableObject::bindReceiver`, which packs the receiver the property read
2411
+ * went through in with the value. Everything but the receiver has to agree
2412
+ * exactly, in the C++ spelling the runtime's own signature matching sees
2413
+ * (`cppAbiType`, the one authority on that spelling), so the adapter's
2414
+ * `Result(Receiver, Arguments...)` is the source's own type and nothing is
2415
+ * coerced on the way through.
2416
+ *
2417
+ * Only the SHAPE is decided here; whether the pair can actually be rendered
2418
+ * -- the read's receiver has to be recoverable at the site, and the body must
2419
+ * never read `this` -- is `emit-callable.ts`'s `receiverBoundCallableText`'s
2420
+ * question, a fact of the emitter's context and not of the two carriers.
2421
+ */
2422
+ /**
2423
+ * A `this`-TYPED function value, stored in a field and read through the object
2424
+ * whose `this` it names: `gea_runtime.h`'s `CallableObject::bindReceiver`
2425
+ * again, but reached from a fact about the two DECLARATIONS rather than from a
2426
+ * method body.
2427
+ *
2428
+ * hono's `RegExpRouter` writes `match: typeof match<Router<T>, T> = match`
2429
+ * over `function match<R extends Router<T>, T>(this: R, method, path)`, while
2430
+ * `TrieRouter` and `SmartRouter` declare `match` as an ordinary method -- and
2431
+ * `Router<T>` itself declares it as a method, with no `this` at all. So the
2432
+ * field's carrier names a receiver and every read through the interface (or
2433
+ * through the union of the three implementors) publishes one that does not,
2434
+ * and nothing converted between them.
2435
+ *
2436
+ * Binding the read's own receiver is the only call the language admits here,
2437
+ * which is what makes this sound where the detached-METHOD case next door
2438
+ * needs `readsReceiver` to hold: a `this` parameter is a parameter, and
2439
+ * TypeScript refuses a call through the value that does not supply one
2440
+ * (`The 'this' context of type 'void' is not assignable...`). A method has no
2441
+ * such annotation, so a detached call really can reach it with `this`
2442
+ * undefined, and binding there would answer where the program would have
2443
+ * thrown.
2444
+ */
2445
+ export const receiverBoundFieldText = (ctx, stored, published, storedText, receiver, receiverText) => {
2446
+ if (!bindsReceiver(stored, published))
2447
+ return null;
2448
+ const abi = callableObjectAbi(stored);
2449
+ if (abi === null || abi.receiver === null)
2450
+ return null;
2451
+ const bound = alignedValueText(ctx, 'emit-narrowing.ts:receiver-bound-field', receiver, abi.receiver, receiverText);
2452
+ if (bound === null)
2453
+ return null;
2454
+ return `${cppTypeOf(published)}::bindReceiver(${storedText}, ${bound})`;
2455
+ };
2456
+ export const bindsReceiver = (source, target) => {
2457
+ const from = callableObjectAbi(source);
2458
+ const to = callableObjectAbi(target);
2459
+ if (!from || !to || from.receiver === null || to.receiver !== null)
2460
+ return false;
2461
+ return cppAbiType({ ...from, receiver: null }) === cppAbiType(to);
2462
+ };
2463
+ export const dropsUnboundParameters = (source, target) => {
2464
+ const from = callableObjectAbi(source);
2465
+ const to = callableObjectAbi(target);
2466
+ if (!from || !to)
2467
+ return false;
2468
+ // Receivers are not part of the runtime constructor's own proof (it
2469
+ // matches on `Arguments...` alone), so a callable wearing one on either
2470
+ // side is not this shape.
2471
+ if (from.receiver !== null || to.receiver !== null)
2472
+ return false;
2473
+ if (from.parameters.length >= to.parameters.length)
2474
+ return false;
2475
+ if (representationKey(from.result) !== representationKey(to.result))
2476
+ return false;
2477
+ return from.parameters.every((parameter, index) => representationKey(parameter.value) === representationKey(to.parameters[index].value));
2478
+ };
2479
+ /**
2480
+ * A zero-parameter callable filling a slot that declares at least one
2481
+ * parameter AND whose result is a tagged union naming the source's own
2482
+ * result as one of its arms -- `dropsUnboundParameters`'s zero-arity special
2483
+ * case (`gea_runtime.h`'s `dropArguments`) composed with
2484
+ * `gea::TaggedUnion::ofArm`, for a source neither alone accepts:
2485
+ * `dropsUnboundParameters` requires an IDENTICAL result, and a bare `ofArm`
2486
+ * conversion has no arity adaptation of its own.
2487
+ *
2488
+ * hono's `Context.notFound`: `this.#notFoundHandler ??=
2489
+ * () => createResponseInstance()` merges a `() => Response` into a field
2490
+ * declared `(c: Context) => Response | Promise<Response>`.
2491
+ * `gea_runtime.h`'s `ResultWidensIntoArm` proves the identical pairing at
2492
+ * the type level; this re-asks the same two-part question so the two cannot
2493
+ * disagree about which pairs the runtime constructor actually accepts.
2494
+ *
2495
+ * Deliberately narrower than `dropsUnboundParameters`: a nonzero source
2496
+ * arity is left entirely to that function (its PREFIX proof does not carry
2497
+ * over once the result itself also changes), and a target result that is
2498
+ * not a tagged union, or a source result that is not EXACTLY one of its
2499
+ * arms, is refused -- the same fail-closed discipline `ResultWidensIntoArm`
2500
+ * documents in the runtime.
2501
+ */
2502
+ export const dropsAllParametersIntoResultArm = (source, target) => {
2503
+ const from = callableObjectAbi(source);
2504
+ const to = callableObjectAbi(target);
2505
+ if (!from || !to)
2506
+ return false;
2507
+ if (from.receiver !== null || to.receiver !== null)
2508
+ return false;
2509
+ if (from.parameters.length !== 0 || to.parameters.length === 0)
2510
+ return false;
2511
+ if (to.result.kind !== 'tagged-union')
2512
+ return false;
2513
+ const resultKey = representationKey(from.result);
2514
+ return to.result.arms.some((arm) => representationKey(arm.value) === resultKey);
2515
+ };
2516
+ /**
2517
+ * A callable filling a slot whose declared result is `void`, its own result
2518
+ * being anything else -- same parameters, in the same order, with the same
2519
+ * types; only the RESULT differs, and only in the direction of being
2520
+ * discarded.
2521
+ *
2522
+ * ECMA-262 makes this sound for a reason distinct from the other two
2523
+ * predicates above: a call's result is simply not read unless something
2524
+ * reads it, so a function returning `T` really is a valid `() => void` (or
2525
+ * any arity's `void`-returning form) wherever the caller never names the
2526
+ * result -- `Array.prototype.forEach`'s callback contract is the textbook
2527
+ * case, and this port's own is `node:http`'s `RequestListener = (req, res)
2528
+ * => void`: `createServer(async (req, res) => { ... })` is an everyday Node
2529
+ * idiom (`apps/hono-hello/server.ts` is one), and TypeScript accepts a
2530
+ * `Promise<void>`-returning callback there for the identical reason -- a
2531
+ * `void`-returning function TYPE is satisfied by ANY return type, precisely
2532
+ * so a caller who ignores the result is never forced to wrap it.
2533
+ *
2534
+ * `gea_runtime.h`'s `CallableObject` composed converting constructor this
2535
+ * asks for discards whatever the source returns; see that constructor's own
2536
+ * comment for why doing so is never lossy for an async body specifically --
2537
+ * this runtime's `Promise<V>` is a settled-value box, never a coroutine
2538
+ * frame, so by the time a call returns here its entire body, `await`s
2539
+ * included, has already run to completion.
2540
+ *
2541
+ * Gated the mirror of `dropsAllParametersIntoResultArm` above: parameters
2542
+ * must agree in COUNT and in every position's TYPE (this is not an arity
2543
+ * change, `dropsUnboundParameters`'s own axis -- it is a pure result
2544
+ * change), the receiver must agree exactly on both sides (a call's receiver
2545
+ * is never dropped), and the target's result must actually BE `void` --
2546
+ * anything else stays a hard compile error, this file's shared fail-closed
2547
+ * rule. Source and target already sharing `void` needs no constructor at
2548
+ * all: `cppTypeOf` equality above already renders that as the identity
2549
+ * text, so this predicate is never even asked.
2550
+ *
2551
+ * Zero parameters is excluded on the runtime constructor's own account, not
2552
+ * this predicate's: `gea_runtime.h`'s matching constructor collapses to the
2553
+ * textually identical signature the `ResultWidensIntoArm`-gated one above it
2554
+ * already declares once `Arguments...` is empty, which fails to COMPILE
2555
+ * (every zero-arity `CallableObject<Result()>` in the header, `Result=void`
2556
+ * or not) rather than merely fails to be selected -- see that constructor's
2557
+ * own comment. Asking this predicate to answer `false` there keeps the two
2558
+ * files agreeing about which pairs the runtime actually accepts, the same
2559
+ * discipline `dropsUnboundParameters` and `dropsAllParametersIntoResultArm`
2560
+ * both already state for their own exclusions.
2561
+ */
2562
+ /**
2563
+ * A callable whose parameters already agree with the slot's, position for
2564
+ * position, and whose RESULT is exactly one arm of the tagged union the slot
2565
+ * declares -- `dropsAllParametersIntoResultArm` with the arity change taken
2566
+ * back out, leaving only the widening half.
2567
+ *
2568
+ * That predicate requires a ZERO-parameter source and `dropsUnboundParameters`
2569
+ * an IDENTICAL result, so a pair moving along only the result axis at a nonzero
2570
+ * arity satisfies neither -- hono's `Hono.fetch` is one, a `(Context) =>
2571
+ * Response` assigned into a field declared `(Context) => Response |
2572
+ * Promise<Response>`. Sound for exactly the reason `gea_runtime.h`'s
2573
+ * `ResultWidensIntoArm` states, and asked in the same terms so the two cannot
2574
+ * disagree: the result must equal one arm EXACTLY and every other position --
2575
+ * receiver, arity, each parameter's carrier, the rest slot -- exactly, none
2576
+ * being adapted here. `widenResultIntoArm` is the constructor performing it.
2577
+ */
2578
+ export const widensResultIntoArm = (source, target) => {
2579
+ const from = callableObjectAbi(source);
2580
+ const to = callableObjectAbi(target);
2581
+ if (!from || !to)
2582
+ return false;
2583
+ if (from.receiver !== null || to.receiver !== null)
2584
+ return false;
2585
+ if (from.parameters.length === 0 || from.parameters.length !== to.parameters.length)
2586
+ return false;
2587
+ if (from.restFrom !== to.restFrom || to.result.kind !== 'tagged-union')
2588
+ return false;
2589
+ if (!from.parameters.every((parameter, index) => representationKey(parameter.value) === representationKey(to.parameters[index].value)))
2590
+ return false;
2591
+ const resultKey = representationKey(from.result);
2592
+ return to.result.arms.some((arm) => representationKey(arm.value) === resultKey);
2593
+ };
2594
+ /**
2595
+ * A source whose rest parameter sits at a LATER position than the target's.
2596
+ *
2597
+ * ECMAScript has one calling convention, so `(base?: string, sub?: string,
2598
+ * ...rest: string[]) => string` and `(...paths: string[]) => string` are the
2599
+ * same function -- hono's `utils/url.ts` declares exactly that pair on one
2600
+ * `const mergePath`, the annotation stating the second and the initializer
2601
+ * being the first. C++ sees two unrelated function types, and the store
2602
+ * refused.
2603
+ *
2604
+ * The target has to be a single rest parameter, because that array is the
2605
+ * whole of what the thunk has to unpack. Every leading source parameter has
2606
+ * to be `optional(E, undefined)` over the same element the rest carries: a
2607
+ * position the call never reached has to be ABSENT, and a leading slot must
2608
+ * never be handed an element of a type it did not declare. The source's own
2609
+ * rest must sit exactly after its leading run, since the tail is what is
2610
+ * left when those are taken.
2611
+ *
2612
+ * `gea_runtime.h`'s `RestRebaseAdmits` states the identical conditions over
2613
+ * the C++ types, so a pair this admits and that one does not is a compile
2614
+ * error rather than a wrong call -- the same fail-closed pairing every other
2615
+ * predicate here has with its constructor.
2616
+ */
2617
+ export const rebasesRestOverLeadingParameters = (source, target) => {
2618
+ const from = callableObjectAbi(source);
2619
+ const to = callableObjectAbi(target);
2620
+ if (!from || !to)
2621
+ return false;
2622
+ if (from.receiver !== null || to.receiver !== null)
2623
+ return false;
2624
+ if (representationKey(from.result) !== representationKey(to.result))
2625
+ return false;
2626
+ if (to.restFrom !== 0 || to.parameters.length !== 1)
2627
+ return false;
2628
+ const leading = from.parameters.length - 1;
2629
+ if (leading < 1 || from.restFrom !== leading)
2630
+ return false;
2631
+ const slot = to.parameters[0]?.value;
2632
+ const tail = from.parameters[leading]?.value;
2633
+ if (!slot || !tail || slot.kind !== 'array-object')
2634
+ return false;
2635
+ if (representationKey(slot) !== representationKey(tail))
2636
+ return false;
2637
+ const elementKey = representationKey(slot.element);
2638
+ return from.parameters
2639
+ .slice(0, leading)
2640
+ .every((parameter) => parameter.value.kind === 'optional' &&
2641
+ parameter.value.absence === 'undefined' &&
2642
+ representationKey(parameter.value.payload) === elementKey);
2643
+ };
2644
+ export const discardsResultIntoVoid = (source, target) => {
2645
+ const from = callableObjectAbi(source);
2646
+ const to = callableObjectAbi(target);
2647
+ if (!from || !to)
2648
+ return false;
2649
+ if (from.receiver !== null || to.receiver !== null)
2650
+ return false;
2651
+ if (to.result.kind !== 'void')
2652
+ return false;
2653
+ if (to.parameters.length === 0)
2654
+ return false;
2655
+ if (from.parameters.length !== to.parameters.length)
2656
+ return false;
2657
+ return from.parameters.every((parameter, index) => representationKey(parameter.value) === representationKey(to.parameters[index].value));
2658
+ };
2659
+ const callableMembershipOf = (carrier) => {
2660
+ switch (carrier.kind) {
2661
+ case 'function':
2662
+ return [carrier.functionId];
2663
+ case 'function-family':
2664
+ case 'function-value-family':
2665
+ return carrier.members.length > 0 ? [...new Set(carrier.members)].sort() : null;
2666
+ case 'function-value-dispatch':
2667
+ case 'function-and-constructor':
2668
+ case 'constructor-family':
2669
+ case 'constructor-value-dispatch':
2670
+ case 'generic-function-set':
2671
+ return null;
2672
+ case 'dynamic':
2673
+ return carrier.reason === 'untyped-callable' ? null : undefined;
2674
+ default:
2675
+ return undefined;
2676
+ }
2677
+ };
2678
+ export const boxDiscriminantsOf = (carrier) => {
2679
+ if (carrier.kind === 'tagged-union') {
2680
+ const parts = carrier.arms.map(boxDiscriminantsOfArm);
2681
+ return parts.some((part) => part === null) ? null : parts.flat();
2682
+ }
2683
+ if (carrier.kind === 'optional') {
2684
+ const payload = boxDiscriminantsOf(carrier.payload);
2685
+ if (payload === null)
2686
+ return null;
2687
+ return [{ tag: carrier.absence === 'null' ? 'Null' : 'Undefined', payload: null, nominal: null, callableMembers: null }, ...payload];
2688
+ }
2689
+ const callableMembers = callableMembershipOf(carrier);
2690
+ // A Function tag and a C++ signature authenticate only callability and an
2691
+ // ABI. They cannot select a source union member. Generic/evaluated callable
2692
+ // arms therefore fail closed; exact functions and closed families carry the
2693
+ // FunctionId tokens installed on their shared Function object.
2694
+ if (callableMembers === null)
2695
+ return null;
2696
+ // The static Ref wrapper recorded in payloadType() is not an object brand:
2697
+ // a Derived allocation can cross the dynamic boundary through Ref<Base>.
2698
+ // Nominal union selection therefore reads the authenticated allocation
2699
+ // family retained by Value, which is the same fact class projection uses.
2700
+ if (carrier.kind === 'class-ref' && carrier.ownership === 'shared-refcount')
2701
+ return [{ tag: 'Object', payload: null, nominal: cppClassName(carrier.declaration), callableMembers: null }];
2702
+ const tag = dynamicTagFor(carrier);
2703
+ if (tag === null)
2704
+ return null;
2705
+ return [
2706
+ {
2707
+ tag,
2708
+ payload: tag === 'Undefined' || tag === 'Null' ? null : cppTypeOf(carrier),
2709
+ nominal: null,
2710
+ callableMembers: callableMembers ?? null
2711
+ }
2712
+ ];
2713
+ };
2714
+ /** The representation arm's carried discriminator and the executable classifier must agree. */
2715
+ export const boxDiscriminantsOfArm = (arm) => {
2716
+ // Broad `Function` is an already-dynamic boundary. It has no static ABI or
2717
+ // declaration identity to recover, but its Value tag is executable evidence
2718
+ // against a non-callable arm. A second callable arm shares this exact tag;
2719
+ // the pairwise collision check in unboxedLoadText then rejects the overlap
2720
+ // because this entry deliberately carries no invented FunctionId set.
2721
+ if (arm.runtimeDiscriminator.kind === 'callable-tag') {
2722
+ return arm.value.kind === 'dynamic' && arm.value.reason === 'untyped-callable'
2723
+ ? [{ tag: 'Function', payload: null, nominal: null, callableMembers: null }]
2724
+ : null;
2725
+ }
2726
+ const discriminants = boxDiscriminantsOf(arm.value);
2727
+ if (arm.runtimeDiscriminator.kind === 'unverifiable-callable')
2728
+ return null;
2729
+ if (discriminants === null)
2730
+ return null;
2731
+ if (arm.runtimeDiscriminator.kind === 'carrier') {
2732
+ return discriminants.some((entry) => entry.callableMembers !== null) ? null : discriminants;
2733
+ }
2734
+ const expected = [...arm.runtimeDiscriminator.members].sort();
2735
+ if (expected.length === 0)
2736
+ return null;
2737
+ return discriminants.every((entry) => entry.callableMembers !== null && [...entry.callableMembers].sort().join('\0') === expected.join('\0'))
2738
+ ? discriminants
2739
+ : null;
2740
+ };
2741
+ /**
2742
+ * The site string a failed unbox aborts with.
2743
+ *
2744
+ * `gea::detail::refusePayloadMismatch` prints this verbatim, and in a release
2745
+ * build it is the ONLY evidence the abort carries: the generated C++ ships with
2746
+ * no debug info, so the backtrace names an enclosing function and nothing more.
2747
+ * A function that unboxes twice then has two indistinguishable aborts, and
2748
+ * finding which one fired costs a rebuild. Naming the carrier the read demanded
2749
+ * makes the message itself the answer.
2750
+ */
2751
+ const assertionSite = (target) => `an assertion out of a dynamic value to ${cppTypeOf(target)}`;
2752
+ export const unboxedLoadText = (target, text) => {
2753
+ // A `Function` arm remains a Value because its ABI is unknown. Loading it
2754
+ // out of a broader dynamic boundary is an identity-preserving copy guarded
2755
+ // by the Function tag, not an adaptation to an invented signature.
2756
+ if (target.kind === 'dynamic' && target.reason === 'untyped-callable') {
2757
+ return `gea::detail::unboxFunctionValue(${text}, ${cppStringLiteral(assertionSite(target))})`;
2758
+ }
2759
+ // A dynamic Function does not carry one statically recoverable declaration
2760
+ // identity, but an evaluated dispatch needs only callability plus its own
2761
+ // ABI. `DynamicCarrier<CallableObject<...>>::in` checks the Function tag and
2762
+ // either recovers an exact payload or builds the checked argument/result
2763
+ // adapter described in gea_runtime.h. This is deliberately separate from
2764
+ // the generic `unboxValue<T>` below: that exact-payload load cannot adapt a
2765
+ // callable whose source and reader ABIs differ.
2766
+ const callableAbi = callableObjectAbi(target);
2767
+ if (callableAbi !== null) {
2768
+ const type = cppTypeOf(target);
2769
+ const value = 'gea_callable_value';
2770
+ const restFrom = callableAbi.restFrom;
2771
+ const load = restFrom === null
2772
+ ? callableAbi.receiver === null
2773
+ ? `gea::detail::DynamicCarrier<${type}>::in(${value}, 0)`
2774
+ : `gea::detail::DynamicCarrier<${type}>::inWithReceiver(${value}, 0)`
2775
+ : callableAbi.receiver === null
2776
+ ? `gea::detail::DynamicCarrier<${type}>::template inWithRest<${restFrom}>(${value}, 0)`
2777
+ : `gea::detail::DynamicCarrier<${type}>::template inWithReceiverAndRest<${restFrom + 1}>(${value}, 0)`;
2778
+ const members = target.kind === 'function'
2779
+ ? [target.functionId]
2780
+ : target.kind === 'function-family' || target.kind === 'function-value-family'
2781
+ ? target.members
2782
+ : null;
2783
+ if (members === null)
2784
+ return `[](const gea::Value& ${value}) -> ${type} { return ${load}; }(${text})`;
2785
+ if (members.length === 0)
2786
+ return null;
2787
+ const membership = [...new Set(members)]
2788
+ .sort()
2789
+ .map((member) => `${value}.callableDeclarationIdentity() == gea::detail::callableDeclarationTagFor<&${cppThunkName(member)}>()`)
2790
+ .join(' || ');
2791
+ return (`[](const gea::Value& ${value}) -> ${type} { ` +
2792
+ `if (${value}.tag() != gea::Value::Tag::Function || !(${membership})) ` +
2793
+ `gea::detail::refusePayloadMismatch("an assertion to an authenticated callable"); return ${load}; }(${text})`);
2794
+ }
2795
+ // A class assertion is a checked projection of the authenticated allocation
2796
+ // the box retained, not an exact match against the writer's static Ref<T>
2797
+ // wrapper. That distinction admits a Derived instance carried through an
2798
+ // `any` Base boundary while still refusing an arbitrary object-shaped Value.
2799
+ // Borrowed and owned class carriers never had a box-safe lifetime contract,
2800
+ // so keep them outside this path rather than rendering a plausible cast.
2801
+ if (target.kind === 'class-ref') {
2802
+ if (target.ownership !== 'shared-refcount')
2803
+ return null;
2804
+ // `T | null` never reaches here as an `optional` target at all --
2805
+ // `representation/optional.ts` folds it onto this same bare `class-ref`,
2806
+ // since `gea::Ref<T>` already default-constructs to the JS `null` state
2807
+ // (the identical fact `widenedStoreText`'s own `written.kind === 'null'`
2808
+ // branch relies on for the store direction). A dynamic boundary must
2809
+ // honor that folding on the READ side too: a box tagged `Null` is this
2810
+ // carrier's own absence, not a payload mismatch, so it is read first and
2811
+ // produces the empty `Ref`, exactly as the `optional` branch above reads
2812
+ // its stated absence tag before falling through to the payload load.
2813
+ // Skipping this let `unknown` values that were legitimately `null`
2814
+ // (`x as SomeClass | null`) abort at `unboxClassRef`'s `Tag::Object`
2815
+ // check instead of producing the empty handle the type says they can be.
2816
+ const type = cppTypeOf(target);
2817
+ return `(${text}.tag() == gea::Value::Tag::Null ? ${type}() : gea::detail::unboxClassRef<${cppClassName(target.declaration)}>(${text}, ${cppStringLiteral(assertionSite(target))}))`;
2818
+ }
2819
+ // A sum has no single tag -- which JavaScript type the box holds depends on
2820
+ // the arm that is live -- so `dynamicTagFor` answers `null` for it and this
2821
+ // used to refuse. Refusing here was not a refusal anywhere the caller could
2822
+ // see: `emit-return.ts` fell through to the bare operand text and emitted
2823
+ // `return <gea::Value>;` from a function declared to return
2824
+ // `gea::TaggedUnion<...>`, which certifies, passes preflight, and is
2825
+ // rejected by clang. A JSDoc `@returns {number|string}` on a JS function
2826
+ // whose parameter's call sites disagree is enough to reach it.
2827
+ //
2828
+ // The recipe is the exact mirror of the per-arm boxing chain
2829
+ // `widenedStoreText` renders in the other direction: discriminate on the
2830
+ // BOX's own tag and build the matching arm. Written as a chain rather than a
2831
+ // runtime helper for the same reason boxing is -- each arm needs its
2832
+ // payload's STATIC C++ type, which only an arm-indexed `ofArm<I>` has.
2833
+ if (target.kind === 'tagged-union') {
2834
+ // A union with exactly one boxed arm is a typed sum with a dynamic
2835
+ // REMAINDER: the descriptor `objectDescriptorReturnTypeAt` mints for a
2836
+ // record read through a runtime key is the measured shape -- `number`
2837
+ // for the declared field, and whatever the object's dynamic-property
2838
+ // sidecar holds for any other key. A box whose tag names a typed arm
2839
+ // lands in that arm (a number never lives boxed beside the number arm,
2840
+ // so the union's own equality keeps working), and every other box is
2841
+ // the remainder. Two boxed arms would leave the remainder ambiguous.
2842
+ const boxedArms = target.arms.filter((arm) => arm.value.kind === 'dynamic' && arm.value.reason !== 'untyped-callable');
2843
+ if (boxedArms.length > 1)
2844
+ return null;
2845
+ const catchAll = target.arms.findIndex((arm) => arm.value.kind === 'dynamic' && arm.value.reason !== 'untyped-callable');
2846
+ const arms = target.arms.map((arm, index) => (index === catchAll ? [] : boxDiscriminantsOfArm(arm)));
2847
+ // One arm this backend cannot name a tag for: the box cannot say which
2848
+ // arm is live, and guessing would reinterpret bytes. Refuse, and let the
2849
+ // obligation say so out loud.
2850
+ if (arms.some((arm) => arm === null))
2851
+ return null;
2852
+ const sets = arms;
2853
+ // Two arms sharing a JS tag -- `Response` and `Promise<Response>` both
2854
+ // read `typeof x === 'object'` -- are still distinguishable if they
2855
+ // record DIFFERENT C++ payload types: `Value::payloadType()`, the second
2856
+ // half of the exact check `gea::detail::unboxValue` performs on every
2857
+ // read below. Only when two arms agree on BOTH the tag AND the payload
2858
+ // type is there nothing left to discriminate on; refuse there, the one
2859
+ // case the tag-only check below used to refuse broadly for any collision.
2860
+ for (let i = 0; i < sets.length; i++)
2861
+ for (let j = i + 1; j < sets.length; j++)
2862
+ for (const left of sets[i])
2863
+ for (const right of sets[j])
2864
+ if (left.tag === right.tag && left.payload === right.payload && left.nominal === right.nominal) {
2865
+ if (left.callableMembers === null || right.callableMembers === null)
2866
+ return null;
2867
+ if (left.callableMembers.some((member) => right.callableMembers.includes(member)))
2868
+ return null;
2869
+ }
2870
+ const targetType = cppTypeOf(target);
2871
+ // An arm classifier selects one executable runtime member before its exact
2872
+ // payload load runs. TypeScript union annotations do not license
2873
+ // ToNumber/ToString coercion at this boundary.
2874
+ const loads = target.arms.map((arm, index) => index === catchAll ? text : convertedValueText({ kind: 'dynamic', reason: 'declared-any-never-narrowed' }, arm.value, text));
2875
+ if (loads.some((load) => load === null))
2876
+ return null;
2877
+ const armText = (index) => `${targetType}::ofArm<${index}>(${loads[index]})`;
2878
+ // A tag shared with another arm is not enough to pick this one -- the
2879
+ // payload's own recorded C++ type is the tiebreaker, asked here as a
2880
+ // CONDITION (rather than left to `unboxValue`'s internal abort) so
2881
+ // dispatch lands on the right arm instead of merely refusing the wrong
2882
+ // one once already committed to it.
2883
+ //
2884
+ // An arm claims a SET of tags, not one, because an arm can itself be a
2885
+ // union or an optional -- `number | string | null` reaching a cell typed
2886
+ // `undefined | null | (number | string)` is ordinary TypeScript, and the
2887
+ // outer sum's third arm answers to both `Number` and `String`. Asking for
2888
+ // one tag per arm refused every such shape outright, and refusing here is
2889
+ // invisible to the caller: `emit-return.ts` used to fall through to the
2890
+ // bare operand and hand clang a `gea::Value` where a `gea::TaggedUnion`
2891
+ // was declared. The condition is the disjunction over the arm's own
2892
+ // discriminants, and the recursive load below builds the inner chain.
2893
+ const test = (discriminant) => {
2894
+ if (discriminant.callableMembers !== null) {
2895
+ const membership = discriminant.callableMembers
2896
+ .map((member) => `${text}.callableDeclarationIdentity() == gea::detail::callableDeclarationTagFor<&${cppThunkName(member)}>()`)
2897
+ .join(' || ');
2898
+ return `(${text}.tag() == gea::Value::Tag::Function && (${membership}))`;
2899
+ }
2900
+ return discriminant.nominal !== null
2901
+ ? `(${text}.tag() == gea::Value::Tag::Object && ${text}.classObject() && ` +
2902
+ `gea::detail::classIdentityExtends(${text}.classIdentity(), &gea::detail::RefOperationsFor<${discriminant.nominal}>::table))`
2903
+ : discriminant.payload !== null
2904
+ ? `(${text}.tag() == gea::Value::Tag::${discriminant.tag} && ` +
2905
+ `${text}.payloadType() == gea::detail::payloadTypeTagFor<${discriminant.payload}>())`
2906
+ : `${text}.tag() == gea::Value::Tag::${discriminant.tag}`;
2907
+ };
2908
+ const condition = (index) => sets[index].map((entry) => test(entry)).join(' || ');
2909
+ // The final arm must not be an implicit fallback. An exact load used to
2910
+ // refuse a nonmatching tag by itself, but a coercive Number/String arm
2911
+ // would otherwise turn (for example) Boolean into whichever arm happens
2912
+ // to be last. A box matching no published member is invalid for this
2913
+ // union and refuses before any member coercion can run.
2914
+ const refused = `([]() -> ${targetType} { gea::detail::refusePayloadMismatch("a dynamic value admitted by no union arm"); }())`;
2915
+ const typed = target.arms.map((_, index) => index).filter((index) => index !== catchAll);
2916
+ let result = catchAll >= 0 ? armText(catchAll) : refused;
2917
+ for (let position = typed.length - 1; position >= 0; position--) {
2918
+ const index = typed[position];
2919
+ result =
2920
+ position === typed.length - 1 && catchAll < 0
2921
+ ? `${condition(index)} ? ${armText(index)} : ${result}`
2922
+ : `${condition(index)} ? ${armText(index)} : (${result})`;
2923
+ }
2924
+ return typed.length > 1 || (typed.length === 1 && catchAll >= 0) ? `(${result})` : result;
2925
+ }
2926
+ // `gea::Optional<T>` read straight out of a box: the tag decides which of
2927
+ // the two states the box holds, exactly the way `dynamicTagFor` already
2928
+ // decides it for the boxing direction (`Value::box` writes `Tag::Null`/
2929
+ // `Tag::Undefined` for the two absent values and nothing else needs
2930
+ // recording, since an absent optional carries no payload). A box tagged
2931
+ // with the OTHER absent value, or any present value, unboxes the payload
2932
+ // exactly as a bare (non-optional) target would -- the whole reason this is
2933
+ // an `if`, not a further tag branch, is that only ONE tag reads as empty and
2934
+ // every other live tag still has to reach the ordinary payload load below.
2935
+ if (target.kind === 'optional') {
2936
+ // The wrapper owns only its stated absence. Every other dynamic tag is an
2937
+ // exact assertion into its payload; `null` must not become an absent
2938
+ // `undefined` optional (or vice versa), and a typed payload must never
2939
+ // acquire ToNumber/ToString semantics merely by crossing this boundary.
2940
+ const payload = target.payload.kind === 'dynamic' ? text : unboxedLoadText(target.payload, text);
2941
+ if (payload === null)
2942
+ return null;
2943
+ const targetType = cppTypeOf(target);
2944
+ const absentTag = target.absence === 'null' ? 'Null' : 'Undefined';
2945
+ return `(${text}.tag() == gea::Value::Tag::${absentTag} ? ${targetType}() : ${targetType}(${payload}))`;
2946
+ }
2947
+ // A dynamic container commonly carries dynamic elements even when the
2948
+ // assertion names a typed Array. BSON deserialization is the concrete case:
2949
+ // it builds `Array<Value>` because each element is decided by the wire type,
2950
+ // then its public result is asserted as `string[]`, `Document[]`, and so on.
2951
+ // The runtime bridge preserves an exact typed payload by identity and
2952
+ // otherwise rebuilds it element by element through `DynamicCarrier`, so the
2953
+ // result is a native `ArrayObject<Element>` and a bad element refuses rather
2954
+ // than being reinterpreted or leaving the whole Array boxed.
2955
+ if (target.kind === 'array-object') {
2956
+ return `gea::detail::unboxDynamicArray<${cppTypeOf(target.element)}>(${text}, ${cppStringLiteral(assertionSite(target))})`;
2957
+ }
2958
+ if (target.kind === 'dictionary' && target.key === 'string' && target.ownership === 'shared-refcount') {
2959
+ return `gea::detail::unboxDynamicDictionary<${cppTypeOf(target.value)}>(${text}, ${cppStringLiteral(assertionSite(target))})`;
2960
+ }
2961
+ // An open document deserializer cannot know the concrete interface a later
2962
+ // assertion will name, so it stores a native `Dictionary<Value>`. Requiring
2963
+ // that payload to already be the target record's C++ struct rejects valid
2964
+ // BSON/JSON document assertions. Rebuild the declared record from checked
2965
+ // dynamic field reads instead. The exact-payload branch preserves identity
2966
+ // when the box already carries this record; the fallback evaluates the
2967
+ // source once and leaves extra dictionary keys outside the statically
2968
+ // declared result, matching the assertion's structural view.
2969
+ if (target.kind === 'record' && target.accessors.length === 0) {
2970
+ if (target.ownership === 'borrowed')
2971
+ return null;
2972
+ const targetType = cppTypeOf(target);
2973
+ const holder = 'gea_dynamic_record';
2974
+ const fields = target.fields.map((field, index) => {
2975
+ const present = `gea_dynamic_record_present_${index}`;
2976
+ const value = `gea_dynamic_record_value_${index}`;
2977
+ const load = field.value.kind === 'dynamic' ? value : unboxedLoadText(field.value, value);
2978
+ return { field, present, value, load };
2979
+ });
2980
+ if (fields.some((field) => field.load === null))
2981
+ return null;
2982
+ const checks = fields
2983
+ .filter(({ field }) => field.required)
2984
+ .map(({ field, present }) => `if (!${present}) gea::detail::refusePayloadMismatch("a dynamic record lacks required field ${field.key}");`);
2985
+ const values = fields.map(({ field, present, value }) => `const gea::Value ${value} = ${present} ? gea::detail::dynamicRecordField(${holder}, ${cppStringLiteral(field.key)}) : gea::Value();`);
2986
+ const reads = fields.map(({ field, present, load }) => {
2987
+ const materialized = load;
2988
+ return field.required ? materialized : `(${present} ? ${materialized} : ${cppTypeOf(field.value)}{})`;
2989
+ });
2990
+ const presences = fields.filter(({ field }) => !field.required).map(({ present }) => present);
2991
+ const structure = `${cppRecordStructName(target.shapeId)}{${[...reads, ...presences].join(', ')}}`;
2992
+ const built = target.ownership === 'shared-refcount' ? `gea::makeRef<${cppRecordStructName(target.shapeId)}>(${structure})` : structure;
2993
+ const exactPayload = target.ownership === 'shared-refcount'
2994
+ ? `if (${holder}.tag() == gea::Value::Tag::Object && ${holder}.payloadType() == gea::detail::payloadTypeTagFor<${targetType}>()) ` +
2995
+ `return gea::detail::unboxValue<${targetType}>(${holder}, gea::Value::Tag::Object, ${cppStringLiteral(assertionSite(target))}); `
2996
+ : '';
2997
+ return (`[](const gea::Value& ${holder}) -> ${targetType} { ` +
2998
+ exactPayload +
2999
+ `if (${holder}.tag() != gea::Value::Tag::Object && ${holder}.tag() != gea::Value::Tag::Function) ` +
3000
+ `gea::detail::refusePayloadMismatch("an assertion to a record requires an object"); ` +
3001
+ `${fields.map(({ field, present }) => `const bool ${present} = gea::detail::dynamicRecordHasField(${holder}, ${cppStringLiteral(field.key)});`).join(' ')} ` +
3002
+ `${checks.join(' ')} ${values.join(' ')} return ${built}; }(${text})`);
3003
+ }
3004
+ // The exact inverse of the boxing table, asked of the same function, so a
3005
+ // carrier can never be boxed under one tag and read back under another.
3006
+ const tag = dynamicTagFor(target);
3007
+ if (tag === null)
3008
+ return null;
3009
+ // `undefined` has no payload to read out of the box at all -- the tag IS
3010
+ // the whole value -- so this is a verify-then-produce, never a load:
3011
+ // `gea::detail::unboxUndefinedValue` checks the tag alone (there is no
3012
+ // payload-type half of the check `unboxValue` performs for everything
3013
+ // else, because there is no payload to have recorded one for) and hands
3014
+ // back the one value `undefined` ever is, `gea::Undefined{}`
3015
+ // (`cppConstantLiteral`'s own spelling, unreachable from here without a
3016
+ // `ConstantLiteral` this function was never handed).
3017
+ if (tag === 'Undefined')
3018
+ return `gea::detail::unboxUndefinedValue(${text}, ${cppStringLiteral(assertionSite(target))})`;
3019
+ // `null` is the same verify-then-produce, and this used to refuse it on a
3020
+ // false premise ("neither has a C++ value this read can produce"). No
3021
+ // PAYLOAD is not no VALUE: `null`'s carrier is `std::nullptr_t`, whose one
3022
+ // inhabitant is `nullptr`, so the tag check plus that inhabitant is a
3023
+ // complete read. BigInt has an ordinary payload: `Value::box` records the
3024
+ // `gea::BigInt` type and `unboxValue` checks it like every other scalar.
3025
+ if (tag === 'Null')
3026
+ return `gea::detail::unboxNullValue(${text}, ${cppStringLiteral(assertionSite(target))})`;
3027
+ // `gea::detail::unboxValue` checks BOTH the tag and the payload's recorded
3028
+ // C++ type (`payloadTypeTagFor`), so a box holding a different struct than
3029
+ // the narrowing claimed refuses by name instead of reinterpreting bytes.
3030
+ // That is what makes reading an object out of a box safe here at all: the
3031
+ // tag alone says only "some object".
3032
+ return `gea::detail::unboxValue<${cppTypeOf(target)}>(${text}, gea::Value::Tag::${tag}, ${cppStringLiteral(assertionSite(target))})`;
3033
+ };
3034
+ /**
3035
+ * `generic-function-set` into a superset: each source index becomes the
3036
+ * target's index of the same member. A one-member source is a constant; a
3037
+ * wider one is a table lookup. `null` when the pair is not a set widening
3038
+ * (a member the target lacks is a narrowing, and no store performs one).
3039
+ */
3040
+ export const genericFunctionSetWideningText = (source, target, text) => {
3041
+ if (target.kind !== 'generic-function-set')
3042
+ return null;
3043
+ // A member's own name, read as a callable: the one member is index 0. The
3044
+ // callable's text is a cell read with no effect to keep.
3045
+ if (source.kind !== 'generic-function-set') {
3046
+ return (source.kind === 'function' ||
3047
+ source.kind === 'function-family' ||
3048
+ source.kind === 'function-value-family' ||
3049
+ source.kind === 'function-value-dispatch') &&
3050
+ target.members.length === 1
3051
+ ? 'static_cast<std::uint8_t>(0)'
3052
+ : null;
3053
+ }
3054
+ const indexes = source.members.map((member) => target.members.indexOf(member));
3055
+ if (indexes.some((index) => index < 0))
3056
+ return null;
3057
+ if (indexes.every((index, position) => index === position))
3058
+ return text;
3059
+ if (indexes.length === 1)
3060
+ return `static_cast<std::uint8_t>(${indexes[0]})`;
3061
+ return `([&]() -> std::uint8_t { static constexpr std::uint8_t gea_set_remap[] = {${indexes.join(', ')}}; return gea_set_remap[${text}]; }())`;
3062
+ };
3063
+ /**
3064
+ * `never[]` read where `E[]` is wanted: the one empty array of `E`. See
3065
+ * `conversions.ts`'s `gea::emptyArraySentinel` recipe for why identity, not a
3066
+ * fresh `[]`, is the answer, and which pairs it admits. The dynamic target
3067
+ * decides at runtime (`emptyArraySentinelOrBoxed`): an empty source is the
3068
+ * sentinel, a populated one -- a real `undefined[]`, which the carrier cannot
3069
+ * tell apart -- is boxed element by element.
3070
+ */
3071
+ export const emptyArraySentinelText = (source, target, text) => {
3072
+ if (source.kind !== 'array-object' || target.kind !== 'array-object' || source.element.kind !== 'undefined')
3073
+ return null;
3074
+ if (source.ownership !== 'shared-refcount' || target.ownership !== 'shared-refcount')
3075
+ return null;
3076
+ if (target.element.kind === 'dynamic')
3077
+ return `gea::emptyArraySentinelOrBoxed(${text})`;
3078
+ if (carriesUndefined(target.element))
3079
+ return null;
3080
+ return `gea::emptyArraySentinel<${cppTypeOf(target.element)}>(${text})`;
3081
+ };
3082
+ /**
3083
+ * Any callable read where a function IDENTITY is wanted -- the carrier of
3084
+ * `(...args: never[]) => R`, TypeScript's `AnyFunction`.
3085
+ *
3086
+ * Nothing is fabricated: `gea::CallableObject` already stores a
3087
+ * `Ref<FunctionObjectIdentity>` beside its thunk, precisely so that copying or
3088
+ * adapting a callable cannot mint a second ECMAScript identity, and this reads
3089
+ * that field. Offered for the carriers `cppTypeOf` spells as a `CallableObject`
3090
+ * or a `CallableConstructorObject`; `gea::ConstructorObject` carries no such
3091
+ * field, so the constructor-only kinds keep the refusal rather than get a
3092
+ * fabricated one.
3093
+ *
3094
+ * There is deliberately no reverse: an identity carries no calling convention,
3095
+ * so a call through it has nothing to use and must refuse.
3096
+ */
3097
+ const callableIdentityText = (source, target, text) => {
3098
+ if (target.kind !== 'callable-identity')
3099
+ return null;
3100
+ switch (source.kind) {
3101
+ case 'function':
3102
+ case 'function-family':
3103
+ case 'function-value-family':
3104
+ case 'function-value-dispatch':
3105
+ case 'function-and-constructor':
3106
+ return `(${text}).functionObjectIdentity()`;
3107
+ default:
3108
+ return null;
3109
+ }
3110
+ };
3111
+ /**
3112
+ * A call-only function object viewed under a type that ALSO names its
3113
+ * `[[Construct]]` -- `Factory as typeof Factory & (new (v: number) => T)`.
3114
+ *
3115
+ * Nothing is rebuilt. ECMA-262 gives a pre-`class` constructor function both
3116
+ * internal methods when it is created; the assertion changes only which of
3117
+ * them the program may name, so the widened view keeps the source's
3118
+ * environment, its owner and its `FunctionObjectIdentity` -- one own-property
3119
+ * table, one `===` answer. `gea::withConstructEntry` is that one step.
3120
+ *
3121
+ * The construct entry is looked up at run time by the source's own invoke
3122
+ * pointer, published beside the construct thunk
3123
+ * (`translation-unit.ts`'s `constructThunkOf`). That keeps the conversion a
3124
+ * property of the VALUE: this step never has to decide which declaration a
3125
+ * cell holds, and a function whose declaration published no such entry refuses
3126
+ * by name instead of constructing through another body.
3127
+ *
3128
+ * The call halves must be the SAME frame. An adapted one is a different
3129
+ * function object, and the identity this preserves is the whole point.
3130
+ */
3131
+ const callableConstructEntryText = (source, target, text) => {
3132
+ if (target.kind !== 'function-and-constructor')
3133
+ return null;
3134
+ const call = source.kind === 'function' ||
3135
+ source.kind === 'function-family' ||
3136
+ source.kind === 'function-value-family' ||
3137
+ source.kind === 'function-value-dispatch'
3138
+ ? source.abi
3139
+ : null;
3140
+ if (call === null || abiKey(call) !== abiKey(target.call))
3141
+ return null;
3142
+ return `gea::withConstructEntry<${cppTypeOf(target)}>(${text})`;
3143
+ };
3144
+ const claimed = (text) => (text === null ? undefined : text);
3145
+ /**
3146
+ * The chain, in the order it has always run. Each entry is one recipe with a
3147
+ * stable id; `conversionRecipeOf` names the entry that claims a pair without
3148
+ * rendering it, so the conversion census (`conversion/nodes.ts`) can state
3149
+ * the same answer this printer gives, from the same table.
3150
+ */
3151
+ export const conversionChain = [
3152
+ { id: 'native-sum-widening', apply: (source, target, text) => claimed(widenedNativeSumText(source, target, text)) },
3153
+ // A choice among generic functions widening into a superset: the index is
3154
+ // remapped, member by member. Before the identical-spelling identity below,
3155
+ // which would pass the SOURCE's index through unchanged -- both sets are one
3156
+ // `std::uint8_t`, and `0` means a different member in each.
3157
+ { id: 'generic-set-remap', apply: (source, target, text) => claimed(genericFunctionSetWideningText(source, target, text)) },
3158
+ // Two carriers this backend spells identically convert by identity. The
3159
+ // representation tells them apart for reasons the C++ type does not carry --
3160
+ // `constructor-family` names the class its `new` reaches and
3161
+ // `constructor-value-dispatch` does not -- but both are one
3162
+ // `gea::ConstructorObject<...>`, and a value of one already IS a value of the
3163
+ // other. Checking the spelling rather than enumerating the pairs is what keeps
3164
+ // this from becoming a second table that could disagree with `cppTypeOf`.
3165
+ { id: 'same-cpp-type', apply: (source, target, text) => (cppTypeOf(source) === cppTypeOf(target) ? text : undefined) },
3166
+ { id: 'empty-array-sentinel', apply: (source, target, text) => claimed(emptyArraySentinelText(source, target, text)) },
3167
+ { id: 'callable-identity', apply: (source, target, text) => claimed(callableIdentityText(source, target, text)) },
3168
+ { id: 'callable-construct-entry', apply: (source, target, text) => claimed(callableConstructEntryText(source, target, text)) },
3169
+ {
3170
+ id: 'constructor-upcast',
3171
+ apply: (source, target, text) => {
3172
+ const member = constructorUpcastMember(source, target);
3173
+ return member === null ? undefined : `gea::upcastConstructor<${cppTypeOf(target)}, &${cppConstructThunkName(member)}>(${text})`;
3174
+ }
3175
+ },
3176
+ {
3177
+ // The same store when the two conventions also disagree on a parameter
3178
+ // (`global.Request = LightweightRequest`, whose `input` names its own class
3179
+ // where the base's names the base): the wrapper converts each argument the
3180
+ // base convention hands it into the one the derived thunk takes.
3181
+ id: 'constructor-adapter',
3182
+ apply: (source, target, text) => {
3183
+ if (source.kind !== 'constructor-family' || target.kind !== 'constructor-family')
3184
+ return undefined;
3185
+ const [member] = source.members;
3186
+ if (member === undefined || source.members.length !== 1 || !target.members.includes(member))
3187
+ return undefined;
3188
+ if (source.abi.receiver !== null || target.abi.receiver !== null)
3189
+ return undefined;
3190
+ if (source.abi.restFrom !== target.abi.restFrom || source.abi.parameters.length !== target.abi.parameters.length)
3191
+ return undefined;
3192
+ if (classRefTransportKind(source.abi.result, target.abi.result) !== 'upcast')
3193
+ return undefined;
3194
+ const formals = [];
3195
+ const actuals = [];
3196
+ for (const [index, parameter] of target.abi.parameters.entries()) {
3197
+ const slot = source.abi.parameters[index];
3198
+ const name = `gea_argument_${index}`;
3199
+ formals.push(`${cppAbiParameterType(parameter)} ${name}`);
3200
+ if (cppAbiParameterType(parameter) === cppAbiParameterType(slot)) {
3201
+ actuals.push(`std::forward<${cppAbiParameterType(parameter)}>(${name})`);
3202
+ continue;
3203
+ }
3204
+ const converted = tryCandidateText(() => convertedValueText(parameter.value, slot.value, name));
3205
+ if (converted === null)
3206
+ return undefined;
3207
+ actuals.push(converted);
3208
+ }
3209
+ const result = cppResultTypeOf(target.abi.result);
3210
+ return (`[](const ${cppTypeOf(source)}& gea_from) { ${cppTypeOf(target)} gea_to; ` +
3211
+ `gea_to.construct_ = +[](void* gea_environment${formals.map((formal) => `, ${formal}`).join('')}) -> ${result} { ` +
3212
+ `return ${result}(${cppConstructThunkName(member)}(gea_environment${actuals.map((actual) => `, ${actual}`).join('')})); }; ` +
3213
+ `gea_to.environment = gea_from.environment; gea_to.environmentOwner = gea_from.environmentOwner; return gea_to; }(${text})`);
3214
+ }
3215
+ },
3216
+ { id: 'regexp-match-array-base', apply: (source, target, text) => claimed(regexpMatchArrayBaseText(source, target, text)) },
3217
+ // A derived class-ref stored where a base class-ref is declared, rendered as
3218
+ // an EXPLICIT construction of the target. Checks the same ancestry direction as
3219
+ // the conversion authority: this chain is also queried speculatively while
3220
+ // matching union arms, where a BASE arm and a DERIVED target do not imply the
3221
+ // base value survived the control-flow guard. Treating that downcast as a store
3222
+ // would make a union recast consume an arm the narrowing excluded, then ask
3223
+ // `gea::Ref` for a constructor C++ does not provide. A real base-to-derived
3224
+ // narrowing falls through to `narrowedLoadText`'s checked downcast.
3225
+ {
3226
+ id: 'class-upcast',
3227
+ apply: (source, target, text) => source.kind === 'class-ref' &&
3228
+ target.kind === 'class-ref' &&
3229
+ source.ownership === target.ownership &&
3230
+ source.ancestors.includes(target.declaration)
3231
+ ? `${cppTypeOf(target)}(${text})`
3232
+ : undefined
3233
+ },
3234
+ // A host handle stored where one of its own base types is declared, the same
3235
+ // rule as `class-upcast` above for a hierarchy this compiler did not lay out.
3236
+ // An `NSStackView` IS an `NSView`: the host stated the inheritance
3237
+ // (`PluginCapabilities.nativeBases`, carried here on the carrier itself) and
3238
+ // the generated bridge gives the wrapper structs that same inheritance, so
3239
+ // the target performs this one implicitly and for free. Rendered as an
3240
+ // explicit construction of the base for `class-upcast`'s reason -- this chain
3241
+ // is also probed speculatively while matching union arms, where the opposite
3242
+ // direction is a downcast no store may perform, and the ancestry check is
3243
+ // what tells the two apart.
3244
+ //
3245
+ // Before this step the pair had no node in the conversion census, so every
3246
+ // program that passed a derived host handle to a base-typed parameter was
3247
+ // refused by the certificate ("no runtime conversion is installed") while the
3248
+ // printer sitting behind it had known how to render it all along.
3249
+ {
3250
+ id: 'native-handle-upcast',
3251
+ apply: (source, target, text) => source.kind === 'native-handle' && target.kind === 'native-handle' && target.native !== null && source.bases.includes(target.native)
3252
+ ? `${cppTypeOf(target)}(${text})`
3253
+ : undefined
3254
+ },
3255
+ { id: 'callable-part', apply: (source, target, text) => claimed(callablePartText(source, target, text)) },
3256
+ // An EMPTY array literal flowing into a native array-like carrier. `[]` with
3257
+ // no contextual array type checks as the empty tuple, which derives a record
3258
+ // with no fields; `(path.match(/.../g) || [])` is the shape. A
3259
+ // value-initialized native handle IS what the source names -- an array-like
3260
+ // holding nothing -- so the conversion mints one rather than refusing a program
3261
+ // the checker already proved compatible. Gated on the source declaring NOTHING:
3262
+ // a record with even one field would be dropping data.
3263
+ {
3264
+ id: 'empty-record-into-native',
3265
+ apply: (source, target) => source.kind === 'record' && source.fields.length === 0 && source.accessors.length === 0 && target.kind === 'native-record-ref'
3266
+ ? target.ownership === 'shared-refcount'
3267
+ ? `gea::makeRef<${target.native ?? cppTypeOf(target, 'owned')}>()`
3268
+ : `${cppTypeOf(target, 'owned')}{}`
3269
+ : undefined
3270
+ },
3271
+ // The next five are implicit converting constructors `gea::CallableObject`
3272
+ // declares, so the identity text renders each -- see each predicate's own doc
3273
+ // for why that is sound. `resultAdaptedCallableText` after them is the pair
3274
+ // that is NOT an implicit constructor: same frame, a result the header cannot
3275
+ // convert on its own (`resultAdapterOf`).
3276
+ { id: 'callable-drops-unbound-parameters', apply: (source, target, text) => (dropsUnboundParameters(source, target) ? text : undefined) },
3277
+ {
3278
+ id: 'callable-drops-all-parameters-into-result-arm',
3279
+ apply: (source, target, text) => (dropsAllParametersIntoResultArm(source, target) ? text : undefined)
3280
+ },
3281
+ { id: 'callable-widens-result-into-arm', apply: (source, target, text) => (widensResultIntoArm(source, target) ? text : undefined) },
3282
+ {
3283
+ id: 'callable-discards-result-into-void',
3284
+ apply: (source, target, text) => (discardsResultIntoVoid(source, target) ? text : undefined)
3285
+ },
3286
+ { id: 'callable-rebases-rest', apply: (source, target, text) => (rebasesRestOverLeadingParameters(source, target) ? text : undefined) },
3287
+ { id: 'result-adapted-callable', apply: (source, target, text) => claimed(resultAdaptedCallableText(source, target, text)) },
3288
+ // `gea::Optional<T>` declares a converting constructor from `T`, so a present
3289
+ // payload widens by being written where the optional is expected.
3290
+ {
3291
+ id: 'optional-wrap-identity',
3292
+ apply: (source, target, text) => target.kind === 'optional' && representationKey(target.payload) === representationKey(source) ? text : undefined
3293
+ },
3294
+ // A DYNAMIC source must be asked whether the box itself is empty before any
3295
+ // payload conversion happens. Falling through to the payload-only wrap below
3296
+ // rendered an unconditional `unboxValue<T>` that C++ then widened into the
3297
+ // optional, reading the box as if it could never be empty: `asNullable(null)`
3298
+ // for a declared `string | null` return certified, compiled, and aborted at
3299
+ // runtime inside `unboxValue`.
3300
+ {
3301
+ id: 'optional-from-dynamic',
3302
+ apply: (source, target, text) => (target.kind === 'optional' && source.kind === 'dynamic' ? unboxedLoadText(target, text) : undefined)
3303
+ },
3304
+ // A source that is ITSELF optional keeps its own presence here: absent stays
3305
+ // absent, present converts one payload down, and the empty branch is a
3306
+ // default-constructed target. Unwrapping the target and converting into the
3307
+ // payload alone rendered an unconditional `(*text)`, and `flag && image` over
3308
+ // an `Img | null` segfaulted on `null` having certified clean. It is the ONLY
3309
+ // answer for an optional source: a payload this cannot convert refuses rather
3310
+ // than falling through, because the fallthrough was that same unwrap one frame
3311
+ // deeper (hono's `c.json` answered `HTTP/1.1 0 unknown` through it).
3312
+ {
3313
+ id: 'optional-payload-convert',
3314
+ apply: (source, target, text) => {
3315
+ if (target.kind !== 'optional' || source.kind !== 'optional')
3316
+ return undefined;
3317
+ const payload = convertedValueText(source.payload, target.payload, `(*${text})`);
3318
+ if (payload === null)
3319
+ return null;
3320
+ return `(${text}.has_value() ? ${cppTypeOf(target)}{${cppTypeOf(target.payload)}{${payload}}} : ${cppTypeOf(target)}{})`;
3321
+ }
3322
+ },
3323
+ {
3324
+ id: 'union-into-optional-payload',
3325
+ apply: (source, target, text) => target.kind === 'optional' && source.kind === 'tagged-union'
3326
+ ? claimed(sumIntoPayloadText(source, target.payload, text, (arm) => `${cppTypeOf(target)}{${cppTypeOf(target.payload)}{${arm}}}`))
3327
+ : undefined
3328
+ },
3329
+ // A UNION source that still carries the target's own absence as a bare arm
3330
+ // has proven nothing about presence: `number | string | null | undefined`
3331
+ // narrowed past the `null` guard alone is `optional(number | string,
3332
+ // undefined)`, and the undefined arm is as live as either payload arm. The
3333
+ // payload-only wrap below would load the payload's arm unconditionally into a
3334
+ // PRESENT optional, so `value === undefined` never held. `narrowedLoadText`'s
3335
+ // optional case tests the absent arm; a payload none of the arms carries whole
3336
+ // makes it refuse, and that refusal is not an answer.
3337
+ {
3338
+ id: 'union-absence-narrow',
3339
+ apply: (source, target, text) => target.kind === 'optional' && source.kind === 'tagged-union' && source.arms.some((arm) => arm.value.kind === target.absence)
3340
+ ? claimed(tryCandidateText(() => narrowedLoadText(source, target, text)))
3341
+ : undefined
3342
+ },
3343
+ // A source that CONVERTS to the payload: `((gl, v) => void)` written where
3344
+ // `((gl, v, textures) => void) | undefined` is expected. The payload is named
3345
+ // EXPLICITLY rather than letting two constructors chain, because C++ permits
3346
+ // one user-defined conversion per implicit sequence. Both constructions are
3347
+ // spelled so the target's presence carrier survives: a narrowed
3348
+ // `Error | null | undefined` must remain `Optional<Error>` after the undefined
3349
+ // guard so a following truthiness test can read its presence flag.
3350
+ {
3351
+ id: 'optional-wrap-converted',
3352
+ apply: (source, target, text) => {
3353
+ if (target.kind !== 'optional')
3354
+ return undefined;
3355
+ const payload = convertedValueText(source, target.payload, text);
3356
+ return payload !== null ? `${cppTypeOf(target)}{${cppTypeOf(target.payload)}{${payload}}}` : undefined;
3357
+ }
3358
+ },
3359
+ // Two real sums, neither one arm of the other: `narrowedLoadText` would
3360
+ // misread the target as an arm value to search FOR inside the source and
3361
+ // refuse. A proper subset is control-flow narrowing, not a recast, and renders
3362
+ // from the TARGET arms so arms excluded by the guard never appear: a recast
3363
+ // searches for a home for every source arm and would emit a downcast of a base
3364
+ // arm the guard proved dead.
3365
+ //
3366
+ // Admission is `narrowingReachesTarget` (`conversion/build.ts`) -- the same
3367
+ // recursive reachability the census's own registry admission already asks
3368
+ // (`conversions.ts`'s `narrowing` sub-union branch) -- and not a flat
3369
+ // top-level key-set comparison, because a NARROWING can be NESTED: `typeof
3370
+ // value === 'string'` proven false inside `undefined | null |
3371
+ // (string|number|bigint|boolean|symbol|object|function)` leaves the same
3372
+ // three-arm outer shape with a smaller seven-minus-one inner union, which
3373
+ // shares no top-level key with its target at all. The flat check refused
3374
+ // that pair and let `union-recast` answer instead, which searches for a
3375
+ // home for the whole nested arm as ONE value; finding none, it fell through
3376
+ // `narrowedLoadText`'s per-leaf search into `unreachable-value`'s
3377
+ // dead-branch discard, which cannot tell "this leaf is provably absent"
3378
+ // from "no leaf of this live arm equals bare `undefined`" and collapsed
3379
+ // every live arm of the inner union into one bogus `Undefined` home.
3380
+ // Certified, emitted, and wrong for every arm past the first guard. Using
3381
+ // the registry's own predicate here is what keeps the two from disagreeing
3382
+ // again the way its doc comment says they cannot.
3383
+ {
3384
+ id: 'union-subset-narrow',
3385
+ apply: (source, target, text) => {
3386
+ if (source.kind !== 'tagged-union' || target.kind !== 'tagged-union')
3387
+ return undefined;
3388
+ return narrowingReachesTarget(source, target) ? claimed(narrowedUnionSubsetText(source, target, text)) : undefined;
3389
+ }
3390
+ },
3391
+ {
3392
+ id: 'union-recast',
3393
+ apply: (source, target, text) => source.kind === 'tagged-union' && target.kind === 'tagged-union' ? claimed(recastedUnionText(source, target, text)) : undefined
3394
+ },
3395
+ // The same shape one level out: two records declaring the same fields under
3396
+ // two shape ids. Tried, not committed to -- a pair this cannot rebuild is not
3397
+ // necessarily a pair nothing can convert.
3398
+ {
3399
+ id: 'record-recast',
3400
+ apply: (source, target, text) => source.kind === 'record' && target.kind === 'record' ? claimed(recastedRecordText(source, target, text)) : undefined
3401
+ },
3402
+ // The record recast still wrapped in its presence bit, admitted by
3403
+ // `conversions.ts`'s `gea::Optional::recastPayload` on the identical
3404
+ // `recordsRecastable` question. An IIFE with a local rather than a conditional
3405
+ // over `text`: `text` is an arbitrary expression and must be evaluated once.
3406
+ {
3407
+ id: 'optional-record-recast',
3408
+ apply: (source, target, text) => {
3409
+ if (source.kind !== 'optional' ||
3410
+ target.kind !== 'optional' ||
3411
+ source.payload.kind !== 'record' ||
3412
+ target.payload.kind !== 'record' ||
3413
+ source.absence !== target.absence)
3414
+ return undefined;
3415
+ const payload = recastedRecordText(source.payload, target.payload, '(*gea_present)');
3416
+ if (payload === null)
3417
+ return undefined;
3418
+ const targetType = cppTypeOf(target);
3419
+ return (`([&]() -> ${targetType} { const auto& gea_present = ${text}; ` +
3420
+ `if (!gea_present.has_value()) return ${targetType}(); ` +
3421
+ `return ${targetType}(${payload}); }())`);
3422
+ }
3423
+ },
3424
+ // A closed record's fields poured into an open dictionary; see
3425
+ // `recordCastableToDictionary` for why only this direction is sound.
3426
+ // `record-to-array` is the same distance: an unannotated rest parameter's two
3427
+ // views (`recordCastableToArray`).
3428
+ {
3429
+ id: 'record-to-dictionary',
3430
+ apply: (source, target, text) => source.kind === 'record' && target.kind === 'dictionary' ? claimed(recastedRecordToDictionaryText(source, target, text)) : undefined
3431
+ },
3432
+ // One shape wider: an open dictionary's values widened into another's
3433
+ // (`dictionaryCastableToDictionary`).
3434
+ {
3435
+ id: 'dictionary-to-dictionary',
3436
+ apply: (source, target, text) => source.kind === 'dictionary' && target.kind === 'dictionary' ? claimed(recastedDictionaryText(source, target, text)) : undefined
3437
+ },
3438
+ // An Array of one element carrier copied into an Array of another, each
3439
+ // element through its own conversion. A copy, so only for an array that is
3440
+ // not written through both names afterwards: a matcher table built once
3441
+ // (hono's `[handlers.map(...), emptyParam]` literal) or a shared empty one.
3442
+ {
3443
+ id: 'array-copy-recast',
3444
+ apply: (source, target, text) => {
3445
+ if (source.kind !== 'array-object' || target.kind !== 'array-object')
3446
+ return undefined;
3447
+ if (source.recursive || target.recursive || source.extension || target.extension)
3448
+ return undefined;
3449
+ if (source.ownership !== 'shared-refcount' || target.ownership !== 'shared-refcount')
3450
+ return undefined;
3451
+ // Never across the dynamic boundary. An element boxed or unboxed on the
3452
+ // way marks a container one authority typed `dynamic` -- an evolving
3453
+ // `var array = []` whose pushes the census could not type -- and such an
3454
+ // array is a shared MUTABLE local passed by reference: copying it hands
3455
+ // the callee a twin, so its pushes never reach the caller
3456
+ // (`test/runtime/array-object-call-argument-aliasing.ts`, and the
3457
+ // three.js `getProgramCacheKey` glyph miscompile its `.runtime.js`
3458
+ // sibling reproduces). Both polarities were live miscompiles that the
3459
+ // refusal fixed on 2026-09-12; re-admitting them here silently undid
3460
+ // that fix. The record- and union-element copies this recipe was added
3461
+ // for (hono's matcher tables, built from literals) do not cross it --
3462
+ // and the test is the ELEMENT's own carrier, not a field somewhere
3463
+ // inside a record element: a record field boxed on the way is a shape
3464
+ // change, not the census-refused evolving array this rule names.
3465
+ const dynamicElement = (element) => element.kind === 'dynamic' || (element.kind === 'optional' && element.payload.kind === 'dynamic');
3466
+ if (dynamicElement(source.element) !== dynamicElement(target.element))
3467
+ return undefined;
3468
+ const element = cppTypeOf(source.element);
3469
+ const converted = tryCandidateText(() => convertedValueText(source.element, target.element, 'gea_element'));
3470
+ if (converted === null)
3471
+ return null;
3472
+ const storage = cppTypeOf(target, 'owned');
3473
+ return (`[](const ${cppTypeOf(source)}& gea_from) { auto gea_array = gea::makeRef<${storage}>(); ` +
3474
+ `if (gea_from.get() != nullptr) gea_array->appendRangeConverted(*gea_from, 0, [](const ${element}& gea_element) { return ${converted}; }); ` +
3475
+ `return gea_array; }(${text})`);
3476
+ }
3477
+ },
3478
+ {
3479
+ // An array asserted to a tuple (`ownRoute as [string, H[]][]`): the tuple's
3480
+ // positions are read out of the array and converted one by one. A shorter
3481
+ // array than the tuple has no value for a required position, which the
3482
+ // assertion claimed away; it fails loudly rather than reading past the end.
3483
+ id: 'array-to-tuple',
3484
+ apply: (source, target, text) => {
3485
+ if (source.kind !== 'array-object' || target.kind !== 'record' || source.ownership !== 'shared-refcount')
3486
+ return undefined;
3487
+ if (source.recursive || source.extension || target.accessors.length > 0 || target.fields.length === 0)
3488
+ return undefined;
3489
+ if (!target.fields.every((field, index) => field.key === String(index) && field.required))
3490
+ return undefined;
3491
+ const reads = [];
3492
+ for (const [index, field] of target.fields.entries()) {
3493
+ const converted = tryCandidateText(() => convertedValueText(source.element, field.value, `gea_from->at(${index})`));
3494
+ if (converted === null)
3495
+ return null;
3496
+ reads.push(field.value.kind === 'scalar' && field.value.domain !== 'bigint'
3497
+ ? `static_cast<decltype(${cppRecordStructName(target.shapeId)}::${cppRecordFieldName(field.key)})>(${converted})`
3498
+ : converted);
3499
+ }
3500
+ const structure = `${cppRecordStructName(target.shapeId)}{${reads.join(', ')}}`;
3501
+ const built = target.ownership === 'shared-refcount' ? `gea::makeRef<${cppRecordStructName(target.shapeId)}>(${structure})` : structure;
3502
+ return (`[](const ${cppTypeOf(source)}& gea_from) { ` +
3503
+ `if (gea_from.get() == nullptr || gea_from->size() < ${target.fields.length}) gea::host::throwRuntimeError("TypeError", "an array asserted to a tuple is shorter than the tuple"); ` +
3504
+ `return ${built}; }(${text})`);
3505
+ }
3506
+ },
3507
+ {
3508
+ id: 'record-to-array',
3509
+ apply: (source, target, text) => source.kind === 'record' && target.kind === 'array-object' ? claimed(recastedRecordToArrayText(source, target, text)) : undefined
3510
+ },
3511
+ // TWO PROMISES WHOSE PAYLOADS DIFFER. `widenedStoreText` reads a `promise`
3512
+ // target as "widen the written VALUE into a promise of it", correct for the
3513
+ // `return` terminator and wrong here, where the source is already a promise.
3514
+ // All three `[[PromiseState]]` values are carried: a rejection moves the
3515
+ // `exception_ptr` (the thrown carrier is the same on both sides), an unsettled
3516
+ // promise stays unsettled, and a `Promise<void>` on either side has no
3517
+ // `value()`, which is why a void payload against a richer one refuses.
3518
+ //
3519
+ // `Promise<void>` against `Promise<undefined>` is the exception, and not a
3520
+ // hole being papered over: the two are one run-time promise wearing two
3521
+ // TypeScript types (ECMA-262 27.2.1.4 fulfils a promise resolved with
3522
+ // nothing with `undefined`, which `cppResultTypeOf` elides for `void`), so
3523
+ // the state crosses and the unit value is supplied or dropped. That recipe
3524
+ // links through `observe` rather than snapshotting a settled state, because
3525
+ // its source is routinely still PENDING -- `@hono/node-server`'s
3526
+ // `responseViaCache` awaits inside -- and a snapshot of a pending promise is
3527
+ // a target nothing ever settles.
3528
+ //
3529
+ // `Promise<never>` is the one void-payload SOURCE that does not refuse, and
3530
+ // it is not a hole being papered over: a promise that cannot fulfil has no
3531
+ // fulfilment value for the target to be missing. Only the rejection has to
3532
+ // cross, so the recipe forwards it rather than reading a `value()` that does
3533
+ // not exist -- and it forwards a rejection that has not happened YET as
3534
+ // well, through `observe`, because a pending `Promise<never>` may still
3535
+ // reject and dropping that would swallow the error the whole carrier exists
3536
+ // to deliver.
3537
+ {
3538
+ id: 'promise-payload',
3539
+ apply: (source, target, text) => {
3540
+ if (source.kind !== 'promise' || target.kind !== 'promise')
3541
+ return undefined;
3542
+ const bottomSource = source.value.kind === 'void' && source.value.bottom === true;
3543
+ if (!bottomSource && (source.value.kind === 'void' || target.value.kind === 'void')) {
3544
+ if (!isUnitPromisePayload(source.value) || !isUnitPromisePayload(target.value))
3545
+ return undefined;
3546
+ return (`([&]() -> ${cppTypeOf(target)} { const auto& ${promiseSourceName} = ${text}; ` +
3547
+ `if (${promiseSourceName}.rejected()) return ${cppTypeOf(target)}::rejected_with(${promiseSourceName}.rejection()); ` +
3548
+ `${cppTypeOf(target)} ${promiseTargetName}; ` +
3549
+ `${promiseSourceName}.observe([${promiseTargetName}](auto&&...) mutable ` +
3550
+ `{ ${promiseTargetName}.resolve(${target.value.kind === 'void' ? '' : cppUndefinedValue}); }, ` +
3551
+ `[${promiseTargetName}](const std::exception_ptr& ${promiseRejectionName}) mutable ` +
3552
+ `{ ${promiseTargetName}.reject(${promiseRejectionName}); }); ` +
3553
+ `return ${promiseTargetName}; }())`);
3554
+ }
3555
+ if (source.value.kind === 'void') {
3556
+ if (source.value.bottom !== true)
3557
+ return undefined;
3558
+ return (`([&]() -> ${cppTypeOf(target)} { const auto& ${promiseSourceName} = ${text}; ` +
3559
+ `if (${promiseSourceName}.rejected()) return ${cppTypeOf(target)}::rejected_with(${promiseSourceName}.rejection()); ` +
3560
+ `${cppTypeOf(target)} ${promiseTargetName}; ` +
3561
+ `${promiseSourceName}.observe([]() {}, [${promiseTargetName}](const std::exception_ptr& ${promiseRejectionName}) mutable ` +
3562
+ `{ ${promiseTargetName}.reject(${promiseRejectionName}); }); ` +
3563
+ `return ${promiseTargetName}; }())`);
3564
+ }
3565
+ const settledPayload = convertedValueText(source.value, target.value, `${promiseSourceName}.value()`);
3566
+ if (settledPayload === null)
3567
+ return undefined;
3568
+ const pendingPayload = convertedValueText(source.value, target.value, promiseValueName);
3569
+ if (pendingPayload === null)
3570
+ return undefined;
3571
+ // A source still PENDING links through `observe`, exactly as the unit
3572
+ // recipe above does and for the same reason: hono's `#cachedBody` is one
3573
+ // callable returning `Promise<string | ArrayBuffer | Blob | ...>` that
3574
+ // every reader (`text()`, `json()`, `formData()`) adapts to its own
3575
+ // `Promise<T>` at the call, and the promise it hands back is the body
3576
+ // read that has not arrived yet. Snapshotting it (`if (!settled()) return
3577
+ // Target()`) minted a target nothing ever settled -- every `await
3578
+ // c.req.json()` hung and the POST never answered. The conversion runs
3579
+ // when the value exists; a conversion that throws (a dynamic arm not of
3580
+ // the declared type) becomes the target's rejection, which is what the
3581
+ // same failure inside a `then` handler is.
3582
+ return (`([&]() -> ${cppTypeOf(target)} { const auto& ${promiseSourceName} = ${text}; ` +
3583
+ `if (${promiseSourceName}.rejected()) return ${cppTypeOf(target)}::rejected_with(${promiseSourceName}.rejection()); ` +
3584
+ `if (${promiseSourceName}.settled()) return ${cppTypeOf(target)}(${settledPayload}); ` +
3585
+ `${cppTypeOf(target)} ${promiseTargetName}; ` +
3586
+ `${promiseSourceName}.observe([${promiseTargetName}](const ${cppTypeOf(source.value)}& ${promiseValueName}) mutable ` +
3587
+ `{ try { ${promiseTargetName}.resolve(${pendingPayload}); } catch (...) { ${promiseTargetName}.reject(std::current_exception()); } }, ` +
3588
+ `[${promiseTargetName}](const std::exception_ptr& ${promiseRejectionName}) mutable ` +
3589
+ `{ ${promiseTargetName}.reject(${promiseRejectionName}); }); ` +
3590
+ `return ${promiseTargetName}; }())`);
3591
+ }
3592
+ },
3593
+ // A TypeScript boundary does not call an ECMAScript abstract operation. A
3594
+ // dynamic source enters every typed carrier through its exact checked load;
3595
+ // String/Number, arithmetic, templates and property keys render their own
3596
+ // coercions at their semantic sites.
3597
+ { id: 'dynamic-unbox', apply: (source, target, text) => (source.kind === 'dynamic' ? unboxedLoadText(target, text) : undefined) },
3598
+ // A String OBJECT reconciled against the `string` primitive its alias widens
3599
+ // to (hono's `HtmlEscapedString`). Before the two general paths for the same
3600
+ // reason the dynamic case is: `narrowedLoadText` would read this as a search
3601
+ // through a union's arms and refuse, and `widenedStoreText` reads it backwards.
3602
+ {
3603
+ id: 'string-object-stringify',
3604
+ apply: (source, target, text) => (target.kind === 'string' ? claimed(stringObjectStringifyText(source, text)) : undefined)
3605
+ },
3606
+ { id: 'narrowed-load', apply: (source, target, text) => claimed(narrowedLoadText(source, target, text)) },
3607
+ // Ahead of `widened-store`, which claims every pair it reaches and refuses
3608
+ // the ones it cannot widen -- so nothing after it ever runs. A storage-free
3609
+ // `undefined` entering a carrier with no room for an absence is a branch
3610
+ // flow analysis proved dead, not a missing load: `undefined` is also
3611
+ // `never`'s carrier (`representation/primitives.ts`), and a pair the loads
3612
+ // refuse can only be reached when the source holds nothing. `var [d = 7] =
3613
+ // []` is the standing case -- the extraction past an empty tuple is
3614
+ // `undefined` outright, the default's present arm converts it to the bound
3615
+ // `number`, and the `is-defined` test guarding that arm is the constant
3616
+ // `false`. Rendered as the throw the call-site argument path already
3617
+ // renders (`emit-callable.ts`) so the dead arm does not block the live one.
3618
+ // The other direction is a read flow analysis proved yields `undefined` out
3619
+ // of a cell that holds a value on every other path: the value is
3620
+ // discarded, not converted. A carrier that CAN hold an absence -- an
3621
+ // optional, a sum, a box -- is `widened-store`'s to fill with one.
3622
+ {
3623
+ id: 'unreachable-value',
3624
+ apply: (source, target, text) => {
3625
+ const holdsAbsence = (carrier) => carrier.kind === 'optional' ||
3626
+ carrier.kind === 'tagged-union' ||
3627
+ carrier.kind === 'dynamic' ||
3628
+ carrier.kind === 'undefined' ||
3629
+ carrier.kind === 'null' ||
3630
+ carrier.kind === 'void';
3631
+ if (source.kind === 'undefined' && !holdsAbsence(target))
3632
+ return `gea::host::unreachableValue<${cppTypeOf(target)}>()`;
3633
+ if (target.kind === 'undefined' && !holdsAbsence(source))
3634
+ return `(static_cast<void>(${text}), gea::Undefined{})`;
3635
+ return undefined;
3636
+ }
3637
+ },
3638
+ { id: 'widened-store', apply: (source, target, text) => widenedStoreText(target, source, text) }
3639
+ ];
3640
+ const runConversionChain = (source, target, text) => {
3641
+ for (const step of conversionChain) {
3642
+ const rendered = step.apply(source, target, text);
3643
+ if (rendered === undefined)
3644
+ continue;
3645
+ return { step, text: rendered };
3646
+ }
3647
+ return { step: null, text: null };
3648
+ };
3649
+ export const alignedValueText = (ctx, site, source, target, text) => {
3650
+ if (representationKey(source) === representationKey(target))
3651
+ return text;
3652
+ const node = ctx.conversions.nodeFor(source, target);
3653
+ const refused = node.capability.kind === 'never';
3654
+ ctx.printerDrift.push({
3655
+ owner: String(ctx.owner),
3656
+ site,
3657
+ kind: refused ? 'refused' : 'converted',
3658
+ source: representationKey(source),
3659
+ target: representationKey(target),
3660
+ reason: node.capability.kind === 'never' ? node.capability.reason : node.capability.kind,
3661
+ sourceRepresentation: source,
3662
+ targetRepresentation: target
3663
+ });
3664
+ return refused ? convertedValueText(source, target, text) : recipeText(ctx, node, text);
3665
+ };
3666
+ /**
3667
+ * A census node's recipe, rendered by one of the two renderers the census
3668
+ * owns. The chain first: the registry's atoms ARE its narrowing, widening
3669
+ * and recasting steps, and its static recipes are named by the step that
3670
+ * claimed the pair (`conversions.ts`'s `staticRecipe`). Then the structural
3671
+ * record view (`conversion/record-view.ts`), which renders both the
3672
+ * `view:structural-record` recipe and the registry's `gea::record::recast`
3673
+ * atom -- the eager graph minted that atom for a by-value record entering
3674
+ * an interface's shared shape before the view plan existed, and the chain's
3675
+ * `record-recast` step spells only the by-reference recast, so the view is
3676
+ * that atom's renderer. Keyed by capability KIND rather than by materializer
3677
+ * id on purpose: a node's id says which table answered, not which text
3678
+ * spells it, and every non-`never` node has exactly one of these two.
3679
+ *
3680
+ * `class-family` is a third renderer, asked before the chain rather than
3681
+ * folded into it: `classFamilyLoadText` orders its cascade over
3682
+ * `ctx.classes`, the whole-program class table, and the chain
3683
+ * (`convertedValueText`) is deliberately ctx-free -- see `algebra.ts`'s doc
3684
+ * on the kind for why an `atom` cannot carry this recipe.
3685
+ */
3686
+ export const recipeText = (ctx, node, text) => {
3687
+ if (node.capability.kind === 'identity')
3688
+ return text;
3689
+ if (node.capability.kind === 'never')
3690
+ return null;
3691
+ if (node.capability.kind === 'coercion')
3692
+ return coercionText(node.capability.operation, text, node.source, ctx.layouts);
3693
+ if (node.capability.kind === 'class-family')
3694
+ return classFamilyLoadText(ctx, node.source, node.target, text);
3695
+ // `conversions.ts`'s read of a structural value as a class nothing instantiates.
3696
+ if (node.capability.kind === 'atom' && node.capability.materializer.id === 'gea::host::unreachableValue')
3697
+ return `((void)(${text}), gea::host::unreachableValue<${cppTypeOf(node.target)}>())`;
3698
+ // `conversions.ts`'s read of a class instance's structural view as the class: the boxed origin, narrowed.
3699
+ if (node.capability.kind === 'atom' && node.capability.materializer.id === 'gea::record::viewOrigin')
3700
+ return narrowedLoadText({ kind: 'dynamic', reason: 'declared-any-never-narrowed' }, node.target, node.source.kind === 'optional'
3701
+ ? `((${text}).has_value() ? gea::record::viewOrigin(*(${text})) : gea::Value())`
3702
+ : `gea::record::viewOrigin(${text})`);
3703
+ if ((node.capability.kind === 'atom' || node.capability.kind === 'static') && node.capability.materializer.nativeSelection) {
3704
+ const helper = ctx.nativeSelectionHelpers?.get(node.id);
3705
+ return helper
3706
+ ? `${helper.name}(${text})`
3707
+ : nativeSelectionText(node.capability.materializer.nativeSelection, node.source, node.target, text);
3708
+ }
3709
+ return convertedValueText(node.source, node.target, text) ?? structuralRecordViewText(ctx, node.source, node.target, text);
3710
+ };
3711
+ /**
3712
+ * The text of a node a `convert` instruction NAMES, rather than of the pair
3713
+ * it spans: a coercion node shares its (source, target) pair with the store
3714
+ * node `alignedValueText` would look up -- `string -> scalar(number)` is an
3715
+ * exact tag read as a store and StringToNumber as a coercion -- so the
3716
+ * instruction's own node id is the only thing that says which one runs.
3717
+ */
3718
+ export const namedConversionText = (ctx, site, node, text) => {
3719
+ const refused = node.capability.kind === 'never';
3720
+ ctx.printerDrift.push({
3721
+ owner: String(ctx.owner),
3722
+ site,
3723
+ kind: refused ? 'refused' : 'converted',
3724
+ source: representationKey(node.source),
3725
+ target: representationKey(node.target),
3726
+ reason: node.capability.kind === 'never' ? node.capability.reason : node.capability.kind,
3727
+ sourceRepresentation: node.source,
3728
+ targetRepresentation: node.target
3729
+ });
3730
+ return recipeText(ctx, node, text);
3731
+ };
3732
+ export const convertedValueText = (source, target, text) => {
3733
+ if (representationKey(source) === representationKey(target))
3734
+ return text;
3735
+ // A void TARGET is the language's explicit discard conversion. The source
3736
+ // operation has already evaluated its effects; this expression preserves a
3737
+ // valid C++ void value for callers that return or forward the conversion,
3738
+ // without naming a storage type that cannot exist. A void SOURCE cannot
3739
+ // produce any non-void target and remains a named refusal.
3740
+ if (target.kind === 'void')
3741
+ return `(void)(${text})`;
3742
+ if (source.kind === 'void') {
3743
+ throw createCppEmitBlockedError(`conversion:${representationKey(source)}->${representationKey(target)}`, `reconciles a ${source.kind} source with a ${target.kind} target`);
3744
+ }
3745
+ // Refused HERE: `cppTypeOf` throws a bare `Error` on lattice bottom that
3746
+ // escapes the per-body catch and kills the whole compile otherwise.
3747
+ if (containsUnresolved(source) || containsUnresolved(target)) {
3748
+ const bottom = [...walkRepresentation(source), ...walkRepresentation(target)].find((f) => f.kind === 'unresolved');
3749
+ throw createCppEmitBlockedError(`conversion:${representationKey(source)}->${representationKey(target)}`, `reconciles against ${bottom?.reason ?? 'an unresolved representation'}`);
3750
+ }
3751
+ return runConversionChain(source, target, text).text;
3752
+ };
3753
+ /**
3754
+ * Which chain step answers a pair, decided by running the chain on a
3755
+ * placeholder: the steps decide from the two carriers alone and only thread
3756
+ * the text through, so the answer is the one `convertedValueText` gives. A
3757
+ * step that claims the pair but cannot render it is reported with
3758
+ * `renders: false`; a pair no step claims is `null`.
3759
+ */
3760
+ /**
3761
+ * Whether this backend can spell a carrier at all. Asked about every pair the
3762
+ * registry is probed with -- spelling the same target ten thousand times,
3763
+ * catching a throw each time it cannot be, was a measurable share of that
3764
+ * loop -- so remembered by identity, as `representationKey`'s own memo is.
3765
+ * The mapping's throw is still the answer; `manifest.ts`'s `isSpellable`
3766
+ * reads it the same way.
3767
+ */
3768
+ const spellability = new WeakMap();
3769
+ export const isSpellable = (representation) => {
3770
+ const known = spellability.get(representation);
3771
+ if (known !== undefined)
3772
+ return known;
3773
+ let spellable;
3774
+ try {
3775
+ cppTypeOf(representation);
3776
+ spellable = true;
3777
+ }
3778
+ catch {
3779
+ spellable = false;
3780
+ }
3781
+ spellability.set(representation, spellable);
3782
+ return spellable;
3783
+ };
3784
+ /**
3785
+ * The chain's answer for a pair, as a predicate: the registry's static recipe
3786
+ * and a record view's field and arm pairs (`conversion/record-view.ts`) ask
3787
+ * this one question, so a view the census admits is a view the printer
3788
+ * renders.
3789
+ */
3790
+ export const chainConverts = (source, target) => isSpellable(source) && isSpellable(target) && (conversionRecipeOf(source, target)?.renders ?? false);
3791
+ export const conversionRecipeOf = (source, target) => {
3792
+ if (representationKey(source) === representationKey(target))
3793
+ return { id: 'identity', renders: true };
3794
+ if (target.kind === 'void')
3795
+ return { id: 'discard-into-void', renders: true };
3796
+ if (source.kind === 'void' || containsUnresolved(source) || containsUnresolved(target))
3797
+ return null;
3798
+ const probed = tryCandidateText(() => {
3799
+ const outcome = runConversionChain(source, target, 'gea_conversion_probe');
3800
+ return outcome.step === null ? null : `${outcome.step.id}\u0000${outcome.text === null ? '' : 'renders'}`;
3801
+ });
3802
+ if (probed === null)
3803
+ return null;
3804
+ const [id, renders] = probed.split('\u0000');
3805
+ return { id: id ?? 'unknown', renders: renders === 'renders' };
3806
+ };
3807
+ /**
3808
+ * Whether this carrier OWNS what it holds, so handing it over is a move rather
3809
+ * than a copy.
3810
+ *
3811
+ * A `string` owns a heap buffer the same way a counted reference owns a count,
3812
+ * and hands it over the same way. `JSON.stringify`'s result is the case that
3813
+ * made this visible: a 300 KB text was copied out of the value the serializer
3814
+ * had just built into the cell that names it, once per call.
3815
+ *
3816
+ * An `optional` owns whatever its payload owns and nothing else --
3817
+ * `gea::Optional<gea::Ref<T>>` is a `Ref` plus a presence bit, and moving it
3818
+ * moves the `Ref`. `binary_trees` is the case: `sum(node.left)` reads a
3819
+ * `TreeNode | null` field into a temporary and passes it by value, which is an
3820
+ * increment on the way in and a decrement, a branch and an out-of-line
3821
+ * destructor on the way out, twice for every one of a million nodes. The
3822
+ * hand-written baseline it is measured against passes a raw pointer.
3823
+ */
3824
+ const ownsItsStorage = (carrier) => {
3825
+ if (carrier.kind === 'string')
3826
+ return true;
3827
+ if (carrier.kind === 'class-ref')
3828
+ return carrier.ownership === 'shared-refcount';
3829
+ if (carrier.kind === 'optional')
3830
+ return ownsItsStorage(carrier.payload);
3831
+ return false;
3832
+ };
3833
+ /**
3834
+ * A DYING refcounted value moves into the slot that takes it rather than being
3835
+ * copied into it -- see `ir/transfer.ts`'s `buildDyingArgumentIndex` for what
3836
+ * makes a value dying, which is the whole of the safety argument.
3837
+ *
3838
+ * Two conditions here, both about the CARRIER rather than the value's life:
3839
+ * only a carrier that owns a count has anything to hand over, and the slot must
3840
+ * hold exactly what the value already holds. `gea::Ref` has no converting move
3841
+ * constructor, so an upcast to a base class binds `std::move(x)` right back to
3842
+ * the copying `Ref(const Ref<Other>&)` -- and any slot needing a real
3843
+ * conversion has already built a temporary of its own, which moving cannot
3844
+ * make cheaper.
3845
+ */
3846
+ export const movedValueText = (ctx, value, held, text) => {
3847
+ if (transferOf(ctx.dyingArguments, ctx.ownedValues, ctx.ownedDyingValues, value.value) !== 'move')
3848
+ return text;
3849
+ // A value the emitter withheld renders as its own expression, not as a cell:
3850
+ // `f(std::move((a->elementAtIndex(i))))` moves a prvalue, which gcc rejects
3851
+ // as a pessimizing move under -Werror, and there is nothing to gain -- the
3852
+ // temporary already binds to the by-value slot without a copy.
3853
+ if (isDeferredValue(ctx, value.value))
3854
+ return text;
3855
+ const carrier = value.representation;
3856
+ // A `string` owns a heap buffer the same way a counted reference owns a
3857
+ // count, and hands it over the same way. `JSON.stringify`'s result is the
3858
+ // case that made this visible: a 300 KB text was copied out of the value the
3859
+ // serializer had just built into the cell that names it, once per call.
3860
+ if (!ownsItsStorage(carrier))
3861
+ return text;
3862
+ if (held !== null && representationKey(held) !== representationKey(carrier))
3863
+ return text;
3864
+ return `std::move(${text})`;
3865
+ };
3866
+ /**
3867
+ * The proven-partial-dead-arm sibling of `emit.ts`'s `emitConvert` -- see
3868
+ * `MergeLiveArmRebuildOperation`'s doc comment (`ir/model.ts`) for why a
3869
+ * general `convert` cannot express this.
3870
+ *
3871
+ * `operation.liveArms` is the proof: which of the source's (optional-
3872
+ * unwrapped) tagged-union arms are not proven dead at this merge, computed
3873
+ * once by the control-flow lowering that owns the guard and carried here
3874
+ * unchanged -- never recomputed. `ir/lower-narrow.ts` derives it from
3875
+ * `partialDeadMergeArms` for `&&`; `ir/lower-destructuring.ts` derives it from
3876
+ * the top-level arms surviving a default's `is-defined` guard. Only those
3877
+ * indices are tested, in source-arm order, on the source's own discriminant
3878
+ * (`.is<N>()`/`.get<N>()`, the
3879
+ * identical accessors this file already renders for a narrowed tagged
3880
+ * union above), and the chain falls through to the last live index without
3881
+ * testing it -- sound because reaching this expression at all already proves
3882
+ * the held arm is one of them. The source's own absence (if it is optional)
3883
+ * reads the result's own absent value instead of testing any arm at all.
3884
+ *
3885
+ * Each live arm converts into the result's carrier through `convertedValueText`
3886
+ * (below) FIRST, unmodified, and only when that finds no installed load AND
3887
+ * the result is boolean-shaped (`isBooleanShapedMergeTarget`) does it fall
3888
+ * back to `ToBoolean` (`emit-presence.ts`'s `booleanTestText`) instead. That
3889
+ * fallback is not a general "any carrier converts to boolean" capability -- it
3890
+ * is not added to `convertedValueText`'s own representation-keyed dispatch,
3891
+ * which would let it fire for any unrelated occurrence of the same
3892
+ * representation pair. It fires only here, for an arm THIS merge's own
3893
+ * partial-dead-arm proof already marked live, because a `&&` merge's own
3894
+ * published type collapsing to `boolean | undefined` is the checker having
3895
+ * already proven the kept contribution meaningful only as its truthiness:
3896
+ * ECMA-262's `&&` picks its kept operand by `ToBoolean` in the first place, so
3897
+ * restating that answer as a plain `bool` is not a new fact about the arm's
3898
+ * carrier, it is the exact question this merge already asked of it.
3899
+ */
3900
+ export const emitMergeLiveArmRebuild = (ctx, lines, operation) => {
3901
+ const sourceRepresentation = operation.source.representation;
3902
+ const payload = sourceRepresentation.kind === 'optional' ? sourceRepresentation.payload : sourceRepresentation;
3903
+ if (payload.kind !== 'tagged-union') {
3904
+ throw createCppEmitBlockedError(`conversion:${representationKey(sourceRepresentation)}->tagged-union`, `rebuilds live arms of ${representationKey(sourceRepresentation)}, which is not a tagged union`);
3905
+ }
3906
+ const target = operation.result.representation;
3907
+ const sourceText = operandText(ctx, operation.source);
3908
+ const unwrapped = sourceRepresentation.kind === 'optional' ? `(*${sourceText})` : sourceText;
3909
+ const booleanPayload = { kind: 'scalar', domain: 'boolean' };
3910
+ const armText = (index) => {
3911
+ const arm = payload.arms[index];
3912
+ if (!arm) {
3913
+ throw createCppEmitBlockedError(`conversion:${representationKey(sourceRepresentation)}->arm-${index}`, `cites live arm ${index} of ${representationKey(sourceRepresentation)}, which has no such arm`);
3914
+ }
3915
+ const loaded = `${unwrapped}.get<${index}>()`;
3916
+ if (operation.nativeTransport) {
3917
+ const citation = operation.nativeTransport.arms.find((entry) => entry.index === index);
3918
+ const node = citation ? ctx.conversions.nodeById(citation.conversion) : null;
3919
+ const text = node ? namedConversionText(ctx, 'emit-narrowing.ts:merge-native-arm', node, loaded) : null;
3920
+ if (text !== null)
3921
+ return text;
3922
+ throw createCppEmitBlockedError(`conversion:${citation?.conversion ?? 'merge-native-arm'}`, 'a sealed native merge arm has no conversion spelling');
3923
+ }
3924
+ const converted = convertedValueText(arm.value, target, loaded);
3925
+ if (converted !== null)
3926
+ return converted;
3927
+ if (isBooleanShapedMergeTarget(target)) {
3928
+ const boolText = booleanTestText(loaded, arm.value);
3929
+ if (representationKey(target) === representationKey(booleanPayload))
3930
+ return boolText;
3931
+ const wrapped = convertedValueText(booleanPayload, target, boolText);
3932
+ if (wrapped !== null)
3933
+ return wrapped;
3934
+ }
3935
+ throw createCppEmitBlockedError(`conversion:${representationKey(arm.value)}->${representationKey(target)}`, `rebuilds live arm ${index} (${representationKey(arm.value)}) of ${representationKey(sourceRepresentation)} into ` +
3936
+ `${representationKey(target)}, which no installed load performs`);
3937
+ };
3938
+ const indices = operation.liveArms;
3939
+ const last = indices[indices.length - 1];
3940
+ if (last === undefined) {
3941
+ throw createCppEmitBlockedError(`conversion:${representationKey(sourceRepresentation)}->no-live-arms`, `rebuilds ${representationKey(sourceRepresentation)} with no live arms recorded`);
3942
+ }
3943
+ let dispatch = armText(last);
3944
+ for (const index of indices.slice(0, -1).reverse()) {
3945
+ dispatch = `${unwrapped}.is<${index}>() ? ${armText(index)} : (${dispatch})`;
3946
+ }
3947
+ if (indices.length > 1)
3948
+ dispatch = `(${dispatch})`;
3949
+ const absenceText = () => {
3950
+ const citation = operation.nativeTransport?.absence;
3951
+ if (citation === undefined || sourceRepresentation.kind !== 'optional')
3952
+ return sourceRepresentation.kind === 'optional'
3953
+ ? cppConstantLiteral(sourceRepresentation.absence, sourceRepresentation.absence, target)
3954
+ : '';
3955
+ const node = ctx.conversions.nodeById(citation);
3956
+ const text = node
3957
+ ? namedConversionText(ctx, 'emit-narrowing.ts:merge-native-absence', node, cppConstantLiteral(sourceRepresentation.absence, sourceRepresentation.absence, { kind: sourceRepresentation.absence }))
3958
+ : null;
3959
+ if (text !== null)
3960
+ return text;
3961
+ throw createCppEmitBlockedError(`conversion:${citation}`, 'a sealed native merge absence has no conversion spelling');
3962
+ };
3963
+ const text = sourceRepresentation.kind === 'optional' && operation.sourceAbsenceLive
3964
+ ? `(${sourceText}.has_value() ? ${dispatch} : ${absenceText()})`
3965
+ : dispatch;
3966
+ lines.push(`${defineValue(ctx, operation.result)} = ${text};`);
3967
+ };