@scriptc/compiler 0.0.21 → 0.0.23

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 (92) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +117 -37
  2. package/dist/backend/cc-cache.test.d.ts +1 -0
  3. package/dist/backend/cc-cache.test.js +2507 -0
  4. package/dist/backend/cc-cache.test.js.map +1 -0
  5. package/dist/backend/cc.d.ts +69 -11
  6. package/dist/backend/cc.js +1937 -222
  7. package/dist/backend/cc.js.map +1 -1
  8. package/dist/backend/emission/emit-async.js +3 -1
  9. package/dist/backend/emission/emit-async.js.map +1 -1
  10. package/dist/backend/emission/emit-exprs.d.ts +10 -0
  11. package/dist/backend/emission/emit-exprs.js +482 -18
  12. package/dist/backend/emission/emit-exprs.js.map +1 -1
  13. package/dist/backend/emission/emit-stmts.js +13 -5
  14. package/dist/backend/emission/emit-stmts.js.map +1 -1
  15. package/dist/backend/emission/emit-types.js +4 -1
  16. package/dist/backend/emission/emit-types.js.map +1 -1
  17. package/dist/backend/emission/emit-walkers.js +108 -7
  18. package/dist/backend/emission/emit-walkers.js.map +1 -1
  19. package/dist/backend/emission/emitter.d.ts +27 -1
  20. package/dist/backend/emission/emitter.js +84 -3
  21. package/dist/backend/emission/emitter.js.map +1 -1
  22. package/dist/backend/llvm/dyn.d.ts +1 -0
  23. package/dist/backend/llvm/dyn.js +305 -8
  24. package/dist/backend/llvm/dyn.js.map +1 -1
  25. package/dist/backend/llvm/emitter.js +831 -23
  26. package/dist/backend/llvm/emitter.js.map +1 -1
  27. package/dist/backend/llvm/shapes.js +3 -2
  28. package/dist/backend/llvm/shapes.js.map +1 -1
  29. package/dist/compat/fetch-profile.d.ts +118 -0
  30. package/dist/compat/fetch-profile.js +531 -0
  31. package/dist/compat/fetch-profile.js.map +1 -0
  32. package/dist/coverage/surface-manifest.js +69 -0
  33. package/dist/coverage/surface-manifest.js.map +1 -1
  34. package/dist/diagnostics/diagnostic.d.ts +10 -0
  35. package/dist/diagnostics/diagnostic.js +27 -1
  36. package/dist/diagnostics/diagnostic.js.map +1 -1
  37. package/dist/frontend/lowering/lower-builtins.d.ts +24 -15
  38. package/dist/frontend/lowering/lower-builtins.js +217 -69
  39. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  40. package/dist/frontend/lowering/lower-calls.js +41 -0
  41. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  42. package/dist/frontend/lowering/lower-classes.js +32 -11
  43. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  44. package/dist/frontend/lowering/lower-exprs.d.ts +3 -3
  45. package/dist/frontend/lowering/lower-exprs.js +87 -15
  46. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  47. package/dist/frontend/lowering/lower-generators.js +7 -1
  48. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  49. package/dist/frontend/lowering/lower-island.d.ts +123 -7
  50. package/dist/frontend/lowering/lower-island.js +1976 -26
  51. package/dist/frontend/lowering/lower-island.js.map +1 -1
  52. package/dist/frontend/lowering/lower-modules.js +17 -1
  53. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  54. package/dist/frontend/lowering/lower-stmts.js +33 -9
  55. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  56. package/dist/frontend/lowering/lowerer.d.ts +40 -0
  57. package/dist/frontend/lowering/lowerer.js +290 -18
  58. package/dist/frontend/lowering/lowerer.js.map +1 -1
  59. package/dist/frontend/lowering/surfaces.js +55 -12
  60. package/dist/frontend/lowering/surfaces.js.map +1 -1
  61. package/dist/frontend/program.d.ts +20 -4
  62. package/dist/frontend/program.js +391 -49
  63. package/dist/frontend/program.js.map +1 -1
  64. package/dist/frontend/resolve.d.ts +10 -0
  65. package/dist/frontend/resolve.js +37 -0
  66. package/dist/frontend/resolve.js.map +1 -1
  67. package/dist/frontend/ts7/enums.d.ts +9 -0
  68. package/dist/frontend/ts7/enums.js +8 -6
  69. package/dist/frontend/ts7/enums.js.map +1 -1
  70. package/dist/frontend/ts7/program.js +4 -1
  71. package/dist/frontend/ts7/program.js.map +1 -1
  72. package/dist/frontend/types.d.ts +16 -9
  73. package/dist/frontend/types.js +119 -38
  74. package/dist/frontend/types.js.map +1 -1
  75. package/dist/index.d.ts +7 -1
  76. package/dist/index.js +70 -14
  77. package/dist/index.js.map +1 -1
  78. package/dist/ir/nodes.d.ts +55 -26
  79. package/dist/ir/nodes.js +42 -29
  80. package/dist/ir/nodes.js.map +1 -1
  81. package/dist/ir/validate.js +106 -9
  82. package/dist/ir/validate.js.map +1 -1
  83. package/dist/library/int-infer.js +7 -1
  84. package/dist/library/int-infer.js.map +1 -1
  85. package/dist/library/int-infer.test.js +80 -0
  86. package/dist/library/int-infer.test.js.map +1 -1
  87. package/dist/library/sidecar-validate.js +5 -0
  88. package/dist/library/sidecar-validate.js.map +1 -1
  89. package/dist/library/sidecar.d.ts +3 -0
  90. package/dist/library/sidecar.js.map +1 -1
  91. package/package.json +2 -2
  92. package/surface-manifest.json +1097 -130
@@ -1,4 +1,4 @@
1
- import { canMarshalFuncIntoIsland, CAUGHT, DYN, F64, islandCallbackRet, islandPromisePayloadTag, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, moduleUsesDynInvoke, moduleUsesFetch, moduleUsesFsWatch, moduleUsesHttpServer, moduleUsesNet, moduleUsesProcessEvents, moduleUsesStream, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, typeEquals, typeKey, VOID } from "../../ir/nodes.js";
1
+ import { canMarshalFuncIntoIsland, CAUGHT, DYN, F64, islandCallbackRet, islandPromisePayloadTag, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, moduleUsesDynInvoke, moduleUsesFetch, moduleUsesFsWatch, moduleUsesHttpServer, moduleUsesNet, moduleUsesProcessEvents, moduleUsesStream, moduleUsesTls, moduleUsesTlsCa, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, typeEquals, typeKey, VOID } from "../../ir/nodes.js";
2
2
  import { computeMayThrow } from "../emission/may-throw.js";
3
3
  import { mangleArgPack, mangleAsyncSpawn, mangleClassNew, mangleClassObj, mangleClassRetain, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenResThunk, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordNew, mangleRecordStruct, mangleResolveThunk, mangleTrampoline, mangleVtStruct, mangleWrapper } from "../mangle.js";
4
4
  import { BlockBuilder } from "./blocks.js";
@@ -129,13 +129,36 @@ const LIB_FN_SYMS = {
129
129
  "process.stderrWrite": "scr_process_stderr_write",
130
130
  "process.isTTY": "scr_process_is_tty",
131
131
  "date.now": "scr_date_now",
132
+ "date.newNow": "scr_date_now",
133
+ "date.newMs": "scr_date_new_ms",
134
+ "date.newString": "scr_date_parse_get_time",
135
+ "date.getTime": "scr_date_get_time",
136
+ "date.valueOf": "scr_date_get_time",
132
137
  "date.parseGetTime": "scr_date_parse_get_time",
133
138
  "date.utc": "scr_date_utc",
139
+ "date.getFullYear": "scr_date_get_full_year_local",
140
+ "date.getUTCFullYear": "scr_date_get_full_year_utc",
141
+ "date.getMonth": "scr_date_get_month_local",
142
+ "date.getUTCMonth": "scr_date_get_month_utc",
143
+ "date.getDate": "scr_date_get_date_local",
144
+ "date.getUTCDate": "scr_date_get_date_utc",
145
+ "date.getDay": "scr_date_get_day_local",
146
+ "date.getUTCDay": "scr_date_get_day_utc",
147
+ "date.getHours": "scr_date_get_hours_local",
148
+ "date.getUTCHours": "scr_date_get_hours_utc",
149
+ "date.getMinutes": "scr_date_get_minutes_local",
150
+ "date.getUTCMinutes": "scr_date_get_minutes_utc",
151
+ "date.getSeconds": "scr_date_get_seconds_local",
152
+ "date.getUTCSeconds": "scr_date_get_seconds_utc",
153
+ "date.getMilliseconds": "scr_date_get_milliseconds",
154
+ "date.getUTCMilliseconds": "scr_date_get_milliseconds",
155
+ "date.getTimezoneOffset": "scr_date_get_timezone_offset",
134
156
  "fs.existsSync": "scr_fs_exists",
135
157
  // ── the throwing slice (MAY_THROW_LIB_FNS members): the generic path
136
158
  // emits the standard pending check after each — emit-exprs.ts's finish.
137
159
  "process.cpuPrevValidate": "scr_cpu_prev_validate",
138
160
  "date.toISOString": "scr_date_to_iso",
161
+ "date.toISOStringValue": "scr_date_to_iso",
139
162
  "process.chdir": "scr_process_chdir",
140
163
  "fs.writeFileSync": "scr_fs_write_file",
141
164
  "fs.appendFileSync": "scr_fs_append_file",
@@ -351,6 +374,18 @@ const LIB_FN_SYMS = {
351
374
  // defineProps throw Node's TypeErrors (all in the may-throw seed set —
352
375
  // the generic path emits the standard pending check). typeof and the
353
376
  // ambient-this read never throw. The fs dyn read is the sync-fs story.
377
+ "fetch.start": "scr_fetch_static",
378
+ "fetch.responseNew": "scr_fetch_response_new",
379
+ "fetch.responseJson": "scr_fetch_response_json",
380
+ "fetch.responseText": "scr_fetch_response_text",
381
+ "fetch.responseBytes": "scr_fetch_response_bytes",
382
+ "fetch.abortControllerNew": "scr_fetch_abort_controller_new",
383
+ "fetch.abortTimeout": "scr_fetch_abort_timeout",
384
+ "fetch.abortNow": "scr_fetch_abort_now",
385
+ "fetch.abortAny": "scr_fetch_abort_any",
386
+ "fetch.streamNew": "scr_fetch_stream_new",
387
+ "fetch.streamFrom": "scr_fetch_stream_from",
388
+ "fetch.readerRead": "scr_fetch_reader_read",
354
389
  "json.parse": "scr_json_parse",
355
390
  "dyn.keySet": "scr_dyn_key_set",
356
391
  "dyn.iterPack": "scr_dyn_iter_pack",
@@ -626,6 +661,8 @@ const STREAM_CANONICAL_CBS = {
626
661
  * the stream/emitter slice of emit-exprs.ts's markings, applied before
627
662
  * dispatch so generic and special shapes share one table. */
628
663
  const USES_TIMERS_LIB_FNS = new Set([
664
+ "fetch.start",
665
+ "fetch.abortTimeout", "fetch.streamNew", "fetch.streamFrom",
629
666
  "readable.new", "writable.new", "duplex.new", "transform.new", "passthrough.new",
630
667
  "readable.init", "writable.init", "duplex.init", "transform.init", "passthrough.init",
631
668
  "readable.newDyn", "writable.newDyn", "duplex.newDyn", "transform.newDyn", "passthrough.newDyn",
@@ -770,6 +807,15 @@ class LlEmitter {
770
807
  * typeKey → thunk symbol (CEmitter.resolveThunks). */
771
808
  resolveThunks = new Map();
772
809
  resolveThunkDefs = [];
810
+ /** ReadableStream.from adapters keep typed arrays by reference and box
811
+ * one current element per pull. */
812
+ streamFromArrayAdapters = new Map();
813
+ /** Identity-preserving static→dyn capsules used by Web APIs whose values
814
+ * remain directly observable (stream chunks and AbortSignal reasons). */
815
+ liveDynRefAdapters = new Map();
816
+ /** Runtime-arm dispatchers for live union values. */
817
+ liveDynUnionRefAdapters = new Map();
818
+ dynPromiseAdapters = new Map();
773
819
  unionsById = new Map();
774
820
  recordsById = new Map();
775
821
  tracedShapes;
@@ -919,6 +965,7 @@ class LlEmitter {
919
965
  llType(t) {
920
966
  switch (t.kind) {
921
967
  case "f64":
968
+ case "date":
922
969
  return "double";
923
970
  case "bool":
924
971
  return "i1";
@@ -1038,6 +1085,7 @@ class LlEmitter {
1038
1085
  // any island entry (the engine's lazy boot consults it) — cc.ts
1039
1086
  // compiles scr_fetch.c on the same predicate.
1040
1087
  const usesFetch = moduleUsesFetch(this.mod);
1088
+ const snapshotsTlsCa = moduleUsesTls(this.mod) || moduleUsesTlsCa(this.mod);
1041
1089
  const hasRefGlobals = globals.some((g) => isRefCounted(g.type)) || fnValueProps.length > 0;
1042
1090
  // Declared NOW — the extern block flushes before main assembles.
1043
1091
  if (usesEvents)
@@ -1054,6 +1102,9 @@ class LlEmitter {
1054
1102
  this.declare(`declare void @scr_http_dyn_install()`);
1055
1103
  if (usesFetch)
1056
1104
  this.declare(`declare void @scr_fetch_install()`);
1105
+ if (snapshotsTlsCa) {
1106
+ this.declare(`declare void @scr_tls_ca_install()`);
1107
+ }
1057
1108
  if (usesEvents && hasRefGlobals) {
1058
1109
  this.declare(`declare void @scr_run_exit_listeners(double)`);
1059
1110
  this.declare(`declare i32 @scr_exit_code_hint_get()`);
@@ -1167,6 +1218,11 @@ class LlEmitter {
1167
1218
  // scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
1168
1219
  // payload at 32).
1169
1220
  `%ScrCaught = type { i64, i32, double, i8, ptr, ptr, ptr, ptr }`,
1221
+ // ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
1222
+ // typed-array access GEPs through this directly: the IR type already
1223
+ // fixes elem, so the hot path needs neither a runtime kind load nor
1224
+ // the generic scr_bytes_get/set call.
1225
+ `%ScrBytes = type { i64, i64, i32, ptr, ptr }`,
1170
1226
  // The capture box { rc, kind, obj_retain, obj_release, obj_trace,
1171
1227
  // slot } — TDZ reads peek the payload slot (offset 40) directly.
1172
1228
  `%ScrBox = type { i64, i32, ptr, ptr, ptr, i64 }`,
@@ -1298,7 +1354,7 @@ class LlEmitter {
1298
1354
  ...(usesEvents ? [` call void @scr_events_install()`] : []),
1299
1355
  // fs.watch programs fill the loop's watch hooks the same way —
1300
1356
  // scr_watch.c links only when this line is emitted.
1301
- ...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(asyncEntry
1357
+ ...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(asyncEntry
1302
1358
  ? [` %top = call ptr @${mangleAsyncSpawn(this.mod.entry)}()`]
1303
1359
  : [` call void @${mangleFunction(this.mod.entry)}()`]),
1304
1360
  // Uncaught exception from top-level code: Node exits 1.
@@ -1671,6 +1727,7 @@ class LlEmitter {
1671
1727
  tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
1672
1728
  break;
1673
1729
  case "f64":
1730
+ case "date":
1674
1731
  this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
1675
1732
  tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
1676
1733
  break;
@@ -1826,6 +1883,7 @@ class LlEmitter {
1826
1883
  tr.push(` br label %done ; void body: the done value is undefined (NONE)`);
1827
1884
  break;
1828
1885
  case "f64":
1886
+ case "date":
1829
1887
  this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
1830
1888
  tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`);
1831
1889
  break;
@@ -2047,7 +2105,7 @@ class LlEmitter {
2047
2105
  const t = this.currentReturnType;
2048
2106
  if (t.kind === "void")
2049
2107
  this.B.terminate("ret void");
2050
- else if (t.kind === "f64")
2108
+ else if (t.kind === "f64" || t.kind === "date")
2051
2109
  this.B.terminate(`ret double ${f64Lit(0)}`);
2052
2110
  else if (t.kind === "bool")
2053
2111
  this.B.terminate("ret i1 false");
@@ -2079,7 +2137,10 @@ class LlEmitter {
2079
2137
  emitThrowValue(v) {
2080
2138
  const B = this.B;
2081
2139
  const t = v.type;
2082
- if (t.kind === "f64") {
2140
+ if (t.kind === "date") {
2141
+ throw new Error("LLVM emitter bug: Date throw reached backend");
2142
+ }
2143
+ else if (t.kind === "f64") {
2083
2144
  this.declare(`declare void @scr_throw_f64(double)`);
2084
2145
  B.line(`call void @scr_throw_f64(double ${v.name})`);
2085
2146
  }
@@ -2128,6 +2189,8 @@ class LlEmitter {
2128
2189
  B.line(`${t} = icmp ne i64 ${len}, 0`);
2129
2190
  return t;
2130
2191
  }
2192
+ case "date":
2193
+ return "true";
2131
2194
  case "array":
2132
2195
  case "record":
2133
2196
  case "object":
@@ -2222,6 +2285,9 @@ class LlEmitter {
2222
2285
  B.line(`store i1 ${t}, ptr ${slot}`);
2223
2286
  break;
2224
2287
  }
2288
+ case "date":
2289
+ B.line(`store i1 true, ptr ${slot} ; Date objects are truthy`);
2290
+ break;
2225
2291
  case "array":
2226
2292
  case "record":
2227
2293
  case "object":
@@ -2766,13 +2832,16 @@ class LlEmitter {
2766
2832
  }
2767
2833
  case "bytesSet": {
2768
2834
  // Typed-array element write: same evaluation order as arraySet;
2769
- // the value is a scalar (the runtime coerces JS-exactly), so no
2770
- // ownership moves. Any invalid index traps — no append.
2771
- const arr = this.emitExpr(s.arr);
2835
+ // the value is a scalar (the kind-specific inline path coerces
2836
+ // JS-exactly), so no ownership moves. Any invalid index traps — no
2837
+ // append. IrBytesElem is static, so never rediscover it through the
2838
+ // generic runtime switch in a hot loop.
2839
+ const arr = this.emitBytesReceiver(s.arr, [s.index, s.value]);
2772
2840
  const idx = this.emitExpr(s.index);
2773
2841
  const v = this.emitExpr(s.value);
2774
- this.declare(`declare void @scr_bytes_set(ptr, double, double)`);
2775
- B.line(`call void @scr_bytes_set(ptr ${arr.name}, double ${idx.name}, double ${v.name})`);
2842
+ if (s.arr.type.kind !== "bytes")
2843
+ throw new Error("llvm emitter bug: bytesSet on non-bytes");
2844
+ this.emitBytesSet(s.arr.type.elem, arr.name, idx.name, v.name);
2776
2845
  break;
2777
2846
  }
2778
2847
  case "fieldSet":
@@ -5255,7 +5324,7 @@ class LlEmitter {
5255
5324
  throw new Error("llvm emitter bug: yieldExpr with no operand (frontend fills undefined)");
5256
5325
  const v = this.emitExpr(e.value);
5257
5326
  const yt = e.value.type;
5258
- if (yt.kind === "f64") {
5327
+ if (yt.kind === "f64" || yt.kind === "date") {
5259
5328
  this.declare(`declare void @scr_gen_yield_f64(double)`);
5260
5329
  B.line(`call void @scr_gen_yield_f64(double ${v.name})`);
5261
5330
  }
@@ -5275,7 +5344,7 @@ class LlEmitter {
5275
5344
  return { name: "", type: e.type };
5276
5345
  }
5277
5346
  const t = B.tmp();
5278
- if (e.type.kind === "f64") {
5347
+ if (e.type.kind === "f64" || e.type.kind === "date") {
5279
5348
  this.declare(`declare double @scr_gen_take_in_f64()`);
5280
5349
  B.line(`${t} = call double @scr_gen_take_in_f64()`);
5281
5350
  return { name: t, type: e.type };
@@ -5307,7 +5376,7 @@ class LlEmitter {
5307
5376
  store(a.name, e.arg.type);
5308
5377
  };
5309
5378
  const parkIn = (name, t) => {
5310
- if (t.kind === "f64") {
5379
+ if (t.kind === "f64" || t.kind === "date") {
5311
5380
  this.declare(`declare void @scr_gen_in_f64(ptr, double)`);
5312
5381
  B.line(`call void @scr_gen_in_f64(ptr ${g.name}, double ${name})`);
5313
5382
  }
@@ -5353,7 +5422,7 @@ class LlEmitter {
5353
5422
  }
5354
5423
  else {
5355
5424
  sendArg((name, t) => {
5356
- if (t.kind === "f64") {
5425
+ if (t.kind === "f64" || t.kind === "date") {
5357
5426
  this.declare(`declare void @scr_gen_ret_f64(ptr, double)`);
5358
5427
  B.line(`call void @scr_gen_ret_f64(ptr ${g.name}, double ${name})`);
5359
5428
  }
@@ -5406,7 +5475,7 @@ class LlEmitter {
5406
5475
  return { name: "", type: e.type };
5407
5476
  }
5408
5477
  const t = B.tmp();
5409
- if (e.type.kind === "f64") {
5478
+ if (e.type.kind === "f64" || e.type.kind === "date") {
5410
5479
  this.declare(`declare double @scr_await_f64(ptr)`);
5411
5480
  B.line(`${t} = call double @scr_await_f64(ptr ${pr.name})`);
5412
5481
  }
@@ -5486,7 +5555,7 @@ class LlEmitter {
5486
5555
  B.startBlock(lp);
5487
5556
  const peek = this.unionPeek(u.name);
5488
5557
  let awaited;
5489
- if (inner.kind === "f64") {
5558
+ if (inner.kind === "f64" || inner.kind === "date") {
5490
5559
  this.declare(`declare double @scr_await_f64(ptr)`);
5491
5560
  const x = B.tmp();
5492
5561
  B.line(`${x} = call double @scr_await_f64(ptr ${peek})`);
@@ -5563,7 +5632,7 @@ class LlEmitter {
5563
5632
  return out;
5564
5633
  }
5565
5634
  let resolve;
5566
- const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
5635
+ const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
5567
5636
  const kindNum = kindNums[inner.kind];
5568
5637
  if (kindNum !== undefined) {
5569
5638
  this.declare(`declare ptr @scr_make_resolve(ptr, i32)`);
@@ -5609,7 +5678,7 @@ class LlEmitter {
5609
5678
  this.declare(`declare ptr @scr_make_reject(ptr)`);
5610
5679
  const p = B.tmp();
5611
5680
  B.line(`${p} = call ptr @scr_promise_new()`);
5612
- const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
5681
+ const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
5613
5682
  const kindNum = kindNums[inner.kind];
5614
5683
  const resolve = B.tmp();
5615
5684
  if (kindNum !== undefined) {
@@ -5724,7 +5793,7 @@ class LlEmitter {
5724
5793
  }
5725
5794
  const v = this.emitExpr(e.args[0]);
5726
5795
  const t = e.args[0].type;
5727
- if (t.kind === "f64") {
5796
+ if (t.kind === "f64" || t.kind === "date") {
5728
5797
  this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
5729
5798
  B.line(`call void @scr_promise_fulfill_f64(ptr ${p}, double ${v.name})`);
5730
5799
  }
@@ -5821,6 +5890,29 @@ class LlEmitter {
5821
5890
  return this.own({ name: t, type: e.type });
5822
5891
  }
5823
5892
  const v = this.emitExpr(e.value);
5893
+ if (e.liveRef) {
5894
+ if (v.type.kind === "union") {
5895
+ const adapter = this.liveDynUnionRefAdapter(v.type);
5896
+ const boxed = B.tmp();
5897
+ B.line(`${boxed} = call ptr @${adapter}(ptr ${v.name})`);
5898
+ return this.own({ name: boxed, type: e.type });
5899
+ }
5900
+ if (!this.streamTypedRefEligible(v.type)) {
5901
+ throw new Error(`llvm emitter bug: live dyn ref of ${typeKey(v.type)}`);
5902
+ }
5903
+ const key = typeKey(v.type);
5904
+ let adapter = this.liveDynRefAdapters.get(key);
5905
+ if (!adapter) {
5906
+ const prefix = `sc_ldr_${this.liveDynRefAdapters.size}`;
5907
+ adapter = this.streamTypedRefMaterializeAdapter(v.type, { prefix, adapters: new Map() }, `${prefix}_materialize`);
5908
+ this.liveDynRefAdapters.set(key, adapter);
5909
+ }
5910
+ const rc = vAdapters(this, v.type);
5911
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
5912
+ const boxed = B.tmp();
5913
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${v.name}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(key)}, i64 ${Buffer.byteLength(key, "utf8")}, ptr @${adapter.snapshot}, ptr ${adapter.commit})`);
5914
+ return this.own({ name: boxed, type: e.type });
5915
+ }
5824
5916
  if (v.type.kind === "func") {
5825
5917
  // A closure boxes as the checked-dynamic tree's function kind: retained closure +
5826
5918
  // the per-signature call thunk + the interned signature key. The
@@ -6560,6 +6652,15 @@ class LlEmitter {
6560
6652
  return t;
6561
6653
  });
6562
6654
  }
6655
+ case "callFnThis": {
6656
+ const pack = argPack(args.slice(2).map((x) => x.name));
6657
+ this.declare(`declare ptr @scr_jsval_call_this(ptr, ptr, i32, ptr)`);
6658
+ return fallible(() => {
6659
+ const t = B.tmp();
6660
+ B.line(`${t} = call ptr @scr_jsval_call_this(ptr ${a(0)}, ptr ${a(1)}, i32 ${args.length - 2}, ptr ${pack})`);
6661
+ return t;
6662
+ });
6663
+ }
6563
6664
  case "callSpread": {
6564
6665
  // Spread application (`f(...pre, ...s)`): the prelude helper's
6565
6666
  // real spread syntax — iterator protocols are the engine's own,
@@ -8568,6 +8669,233 @@ class LlEmitter {
8568
8669
  }
8569
8670
  }
8570
8671
  }
8672
+ /** Whether an index/value expression can overwrite the bytes receiver
8673
+ * binding. Kept deliberately conservative; uncertain or calling shapes
8674
+ * retain the ordinary owned-receiver snapshot. */
8675
+ isStableBytesOperand(e, receiverLocalId) {
8676
+ switch (e.kind) {
8677
+ case "numLit":
8678
+ case "boolLit":
8679
+ case "varRef":
8680
+ case "incDec":
8681
+ return true;
8682
+ case "assignExpr":
8683
+ // A bytes assignment nested under truthiness/ternary can still
8684
+ // produce the numeric index/value expected by the outer access.
8685
+ return (e.localId !== receiverLocalId &&
8686
+ this.isStableBytesOperand(e.value, receiverLocalId));
8687
+ case "bin":
8688
+ return (this.isStableBytesOperand(e.left, receiverLocalId) &&
8689
+ this.isStableBytesOperand(e.right, receiverLocalId));
8690
+ case "unary":
8691
+ case "toBool":
8692
+ return this.isStableBytesOperand(e.operand, receiverLocalId);
8693
+ case "logical":
8694
+ return (this.isStableBytesOperand(e.left, receiverLocalId) &&
8695
+ this.isStableBytesOperand(e.right, receiverLocalId));
8696
+ case "ternary":
8697
+ return (this.isStableBytesOperand(e.cond, receiverLocalId) &&
8698
+ this.isStableBytesOperand(e.then, receiverLocalId) &&
8699
+ this.isStableBytesOperand(e.else_, receiverLocalId));
8700
+ case "bytesIntrinsic":
8701
+ return ((e.method === "get" || e.method === "length" || e.method === "byteLength") &&
8702
+ e.receiver.kind === "varRef" &&
8703
+ e.args.every((arg) => this.isStableBytesOperand(arg, receiverLocalId)));
8704
+ default:
8705
+ return false;
8706
+ }
8707
+ }
8708
+ /** Borrow a direct, unboxed bytes binding when later operands are stable.
8709
+ * Its scope/global owner keeps it alive through the access. */
8710
+ emitBytesReceiver(receiver, following) {
8711
+ if (receiver.kind === "varRef" &&
8712
+ following.every((operand) => this.isStableBytesOperand(operand, receiver.localId))) {
8713
+ const b = this.binding(receiver.localId);
8714
+ if (b.kind !== "boxed") {
8715
+ const value = this.B.tmp();
8716
+ this.B.line(`${value} = load ptr, ptr ${b.slot}`);
8717
+ return { name: value, type: receiver.type };
8718
+ }
8719
+ }
8720
+ return this.emitExpr(receiver);
8721
+ }
8722
+ /** Bounds-check a typed-array element index without an out-of-line call
8723
+ * on the valid path. The range test rejects NaN/infinity before fptoui;
8724
+ * the integer round trip then rejects fractions. The error block retains
8725
+ * the runtime's exact trap text. Leaves the builder in the valid block. */
8726
+ emitBytesIndex(receiver, index) {
8727
+ const B = this.B;
8728
+ const lenPtr = B.tmp();
8729
+ const len = B.tmp();
8730
+ const lenF64 = B.tmp();
8731
+ const nonnegative = B.tmp();
8732
+ const belowLen = B.tmp();
8733
+ const inRange = B.tmp();
8734
+ B.line(`${lenPtr} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
8735
+ B.line(`${len} = load i64, ptr ${lenPtr}`);
8736
+ B.line(`${lenF64} = uitofp i64 ${len} to double`);
8737
+ B.line(`${nonnegative} = fcmp oge double ${index}, ${f64Lit(0)}`);
8738
+ B.line(`${belowLen} = fcmp olt double ${index}, ${lenF64}`);
8739
+ B.line(`${inRange} = and i1 ${nonnegative}, ${belowLen}`);
8740
+ const rangeOk = B.newLabel("bytes.index.range");
8741
+ const invalid = B.newLabel("bytes.index.invalid");
8742
+ const valid = B.newLabel("bytes.index.valid");
8743
+ B.condBr(inRange, rangeOk, invalid);
8744
+ B.startBlock(rangeOk);
8745
+ const idx = B.tmp();
8746
+ const roundTrip = B.tmp();
8747
+ const integral = B.tmp();
8748
+ B.line(`${idx} = fptoui double ${index} to i64`);
8749
+ B.line(`${roundTrip} = uitofp i64 ${idx} to double`);
8750
+ B.line(`${integral} = fcmp oeq double ${roundTrip}, ${index}`);
8751
+ B.condBr(integral, valid, invalid);
8752
+ B.startBlock(invalid);
8753
+ this.declare(`declare double @scr_bytes_get(ptr, double)`);
8754
+ B.line(`call double @scr_bytes_get(ptr ${receiver}, double ${index})`);
8755
+ B.terminate("unreachable");
8756
+ B.startBlock(valid);
8757
+ return idx;
8758
+ }
8759
+ /** Load the raw data pointer from ScrBytes after the bounds check. */
8760
+ emitBytesData(receiver) {
8761
+ const p = this.B.tmp();
8762
+ const data = this.B.tmp();
8763
+ this.B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 3`);
8764
+ this.B.line(`${data} = load ptr, ptr ${p}`);
8765
+ return data;
8766
+ }
8767
+ /** Static typed-array length/byteLength load. */
8768
+ emitBytesLength(elem, receiver, bytes) {
8769
+ const B = this.B;
8770
+ const p = B.tmp();
8771
+ const len = B.tmp();
8772
+ B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
8773
+ B.line(`${len} = load i64, ptr ${p}`);
8774
+ const count = bytes && elem !== "u8" ? B.tmp() : len;
8775
+ if (count !== len)
8776
+ B.line(`${count} = shl i64 ${len}, 2`);
8777
+ const out = B.tmp();
8778
+ B.line(`${out} = uitofp i64 ${count} to double`);
8779
+ return { name: out, type: F64 };
8780
+ }
8781
+ /** Kind-specialized typed-array load. */
8782
+ emitBytesGet(elem, receiver, index) {
8783
+ const B = this.B;
8784
+ const idx = this.emitBytesIndex(receiver, index);
8785
+ const data = this.emitBytesData(receiver);
8786
+ const p = B.tmp();
8787
+ if (elem === "u8") {
8788
+ const raw = B.tmp();
8789
+ const wide = B.tmp();
8790
+ const out = B.tmp();
8791
+ B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
8792
+ B.line(`${raw} = load i8, ptr ${p}, align 1`);
8793
+ B.line(`${wide} = zext i8 ${raw} to i32`);
8794
+ B.line(`${out} = uitofp i32 ${wide} to double`);
8795
+ return { name: out, type: F64 };
8796
+ }
8797
+ if (elem === "f32") {
8798
+ const raw = B.tmp();
8799
+ const out = B.tmp();
8800
+ B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
8801
+ B.line(`${raw} = load float, ptr ${p}, align 1`);
8802
+ B.line(`${out} = fpext float ${raw} to double`);
8803
+ return { name: out, type: F64 };
8804
+ }
8805
+ const raw = B.tmp();
8806
+ const out = B.tmp();
8807
+ B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
8808
+ B.line(`${raw} = load i32, ptr ${p}, align 1`);
8809
+ B.line(`${out} = ${elem === "i32" ? "sitofp" : "uitofp"} i32 ${raw} to double`);
8810
+ return { name: out, type: F64 };
8811
+ }
8812
+ /** ToUint32/ToInt32 coercion with the overwhelmingly common finite,
8813
+ * exactly-representable JS-number range on a cast-only path. Exceptional
8814
+ * and huge values remain inline in the program TU so unrelated runtime
8815
+ * binaries do not acquire another exported helper. */
8816
+ emitBytesU32(value) {
8817
+ const B = this.B;
8818
+ const aboveMin = B.tmp();
8819
+ const belowMax = B.tmp();
8820
+ const fast = B.tmp();
8821
+ B.line(`${aboveMin} = fcmp oge double ${value}, ${f64Lit(-9007199254740992)}`);
8822
+ B.line(`${belowMax} = fcmp ole double ${value}, ${f64Lit(9007199254740992)}`);
8823
+ B.line(`${fast} = and i1 ${aboveMin}, ${belowMax}`);
8824
+ const fastLabel = B.newLabel("bytes.coerce.fast");
8825
+ const slowLabel = B.newLabel("bytes.coerce.slow");
8826
+ const done = B.newLabel("bytes.coerce.done");
8827
+ B.condBr(fast, fastLabel, slowLabel);
8828
+ B.startBlock(fastLabel);
8829
+ const signed = B.tmp();
8830
+ const fastU32 = B.tmp();
8831
+ B.line(`${signed} = fptosi double ${value} to i64`);
8832
+ B.line(`${fastU32} = trunc i64 ${signed} to i32`);
8833
+ B.br(done);
8834
+ B.startBlock(slowLabel);
8835
+ const ordered = B.tmp();
8836
+ const belowInf = B.tmp();
8837
+ const aboveNegInf = B.tmp();
8838
+ const finiteRange = B.tmp();
8839
+ const finite = B.tmp();
8840
+ B.line(`${ordered} = fcmp ord double ${value}, ${value}`);
8841
+ B.line(`${belowInf} = fcmp olt double ${value}, ${F64_INF}`);
8842
+ B.line(`${aboveNegInf} = fcmp ogt double ${value}, ${f64Lit(-Infinity)}`);
8843
+ B.line(`${finiteRange} = and i1 ${belowInf}, ${aboveNegInf}`);
8844
+ B.line(`${finite} = and i1 ${ordered}, ${finiteRange}`);
8845
+ const finiteLabel = B.newLabel("bytes.coerce.finite");
8846
+ const nonfiniteLabel = B.newLabel("bytes.coerce.nonfinite");
8847
+ const slowDone = B.newLabel("bytes.coerce.slow.done");
8848
+ B.condBr(finite, finiteLabel, nonfiniteLabel);
8849
+ B.startBlock(finiteLabel);
8850
+ this.declare(`declare double @llvm.trunc.f64(double)`);
8851
+ const truncated = B.tmp();
8852
+ const residue = B.tmp();
8853
+ const negative = B.tmp();
8854
+ const wrapped = B.tmp();
8855
+ const normalized = B.tmp();
8856
+ const finiteU32 = B.tmp();
8857
+ B.line(`${truncated} = call double @llvm.trunc.f64(double ${value})`);
8858
+ B.line(`${residue} = frem double ${truncated}, ${f64Lit(4294967296)}`);
8859
+ B.line(`${negative} = fcmp olt double ${residue}, ${f64Lit(0)}`);
8860
+ B.line(`${wrapped} = fadd double ${residue}, ${f64Lit(4294967296)}`);
8861
+ B.line(`${normalized} = select i1 ${negative}, double ${wrapped}, double ${residue}`);
8862
+ B.line(`${finiteU32} = fptoui double ${normalized} to i32`);
8863
+ B.br(slowDone);
8864
+ B.startBlock(nonfiniteLabel);
8865
+ B.br(slowDone);
8866
+ B.startBlock(slowDone);
8867
+ const slowU32 = B.tmp();
8868
+ B.line(`${slowU32} = phi i32 [ ${finiteU32}, %${finiteLabel} ], [ 0, %${nonfiniteLabel} ]`);
8869
+ B.br(done);
8870
+ B.startBlock(done);
8871
+ const out = B.tmp();
8872
+ B.line(`${out} = phi i32 [ ${fastU32}, %${fastLabel} ], [ ${slowU32}, %${slowDone} ]`);
8873
+ return out;
8874
+ }
8875
+ /** Kind-specialized typed-array store. */
8876
+ emitBytesSet(elem, receiver, index, value) {
8877
+ const B = this.B;
8878
+ const idx = this.emitBytesIndex(receiver, index);
8879
+ const stored = elem === "f32" ? null : this.emitBytesU32(value);
8880
+ const data = this.emitBytesData(receiver);
8881
+ const p = B.tmp();
8882
+ if (elem === "u8") {
8883
+ const byte = B.tmp();
8884
+ B.line(`${byte} = trunc i32 ${stored} to i8`);
8885
+ B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
8886
+ B.line(`store i8 ${byte}, ptr ${p}, align 1`);
8887
+ return;
8888
+ }
8889
+ if (elem === "f32") {
8890
+ const narrowed = B.tmp();
8891
+ B.line(`${narrowed} = fptrunc double ${value} to float`);
8892
+ B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
8893
+ B.line(`store float ${narrowed}, ptr ${p}, align 1`);
8894
+ return;
8895
+ }
8896
+ B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
8897
+ B.line(`store i32 ${stored}, ptr ${p}, align 1`);
8898
+ }
8571
8899
  /** The bytesIntrinsic surface (emit-exprs.ts's switch, .ll flavored).
8572
8900
  * Receiver and args are borrowed frame temps; string/bytes results come
8573
8901
  * back +1. The MAY_THROW methods get the standard pending check after
@@ -8615,18 +8943,30 @@ class LlEmitter {
8615
8943
  ? call("scr_bytes_read_var", "double (ptr, double, double, i1 zeroext, i1 zeroext)", `ptr ${r0.name}, double ${rest[0].name}, double ${rest[1].name}, i1 ${spec.sign}, i1 ${spec.le}`, false, true)
8616
8944
  : call("scr_bytes_write_var", "double (ptr, double, double, double, i1 zeroext, i1 zeroext)", `ptr ${r0.name}, double ${rest[0].name}, double ${rest[1].name}, double ${rest[2].name}, i1 ${spec.sign}, i1 ${spec.le}`, false, true);
8617
8945
  }
8618
- const r = this.emitExpr(e.receiver);
8619
- const args = e.args.map((a) => this.emitExpr(a));
8620
8946
  const method = e.method;
8947
+ const directElementAccess = method === "length" || method === "byteLength" || method === "get";
8948
+ const r = directElementAccess
8949
+ ? this.emitBytesReceiver(e.receiver, e.args)
8950
+ : this.emitExpr(e.receiver);
8951
+ const args = e.args.map((a) => this.emitExpr(a));
8621
8952
  const NAN = f64Lit(NaN);
8622
8953
  switch (method) {
8623
8954
  case "length":
8624
- return call("scr_bytes_len", "double (ptr)", `ptr ${r.name}`, false, false);
8955
+ if (e.receiver.type.kind !== "bytes") {
8956
+ throw new Error("llvm emitter bug: bytesIntrinsic length on non-bytes");
8957
+ }
8958
+ return this.emitBytesLength(e.receiver.type.elem, r.name, false);
8625
8959
  case "byteLength":
8626
- return call("scr_bytes_byte_len", "double (ptr)", `ptr ${r.name}`, false, false);
8960
+ if (e.receiver.type.kind !== "bytes") {
8961
+ throw new Error("llvm emitter bug: bytesIntrinsic byteLength on non-bytes");
8962
+ }
8963
+ return this.emitBytesLength(e.receiver.type.elem, r.name, true);
8627
8964
  case "get":
8628
8965
  // Any invalid index traps (the array runtime's discipline).
8629
- return call("scr_bytes_get", "double (ptr, double)", `ptr ${r.name}, double ${args[0].name}`, false, false);
8966
+ if (e.receiver.type.kind !== "bytes") {
8967
+ throw new Error("llvm emitter bug: bytesIntrinsic get on non-bytes");
8968
+ }
8969
+ return this.emitBytesGet(e.receiver.type.elem, r.name, args[0].name);
8630
8970
  case "slice":
8631
8971
  return call("scr_bytes_slice", "ptr (ptr, double, double)", `ptr ${r.name}, double ${args[0]?.name ?? f64Lit(0)}, double ${args[1]?.name ?? F64_INF}`, true, false);
8632
8972
  case "subarray":
@@ -9119,6 +9459,410 @@ class LlEmitter {
9119
9459
  B.line(`call void @sc_bad_key()`);
9120
9460
  B.terminate(`unreachable`);
9121
9461
  }
9462
+ dynPromiseAdapter(inner) {
9463
+ if (!isRefCounted(inner) || inner.kind === "dyn") {
9464
+ throw new Error(`dynamic promise adapter requires a concrete reference type, got ${typeKey(inner)}`);
9465
+ }
9466
+ const key = typeKey(inner);
9467
+ const existing = this.dynPromiseAdapters.get(key);
9468
+ if (existing)
9469
+ return existing;
9470
+ const sym = `sc_dpa_${this.dynPromiseAdapters.size}`;
9471
+ this.dynPromiseAdapters.set(key, sym);
9472
+ this.declare(`declare ptr @scr_promise_payload_ref(ptr)`);
9473
+ this.declare(`declare void @scr_dyn_release_v(ptr)`);
9474
+ this.declare(`declare zeroext i1 @scr_exc_pending()`);
9475
+ this.declare(`declare void @scr_promise_reject_pending(ptr)`);
9476
+ const B = new BlockBuilder();
9477
+ const dyn = B.tmp();
9478
+ const value = B.tmp();
9479
+ const pending = B.tmp();
9480
+ B.line(`${dyn} = call ptr @scr_promise_payload_ref(ptr %src)`);
9481
+ B.line(`${value} = call ptr @${this.dyn.dynCheckHelper(inner)}(ptr ${dyn}, ptr null)`);
9482
+ B.line(`call void @scr_dyn_release_v(ptr ${dyn})`);
9483
+ B.line(`${pending} = call zeroext i1 @scr_exc_pending()`);
9484
+ const fail = B.newLabel("sra.fail");
9485
+ const ok = B.newLabel("sra.ok");
9486
+ B.condBr(pending, fail, ok);
9487
+ B.startBlock(fail);
9488
+ B.line(`call void @scr_promise_reject_pending(ptr %dst)`);
9489
+ B.terminate(`ret void`);
9490
+ B.startBlock(ok);
9491
+ if (inner.kind === "string") {
9492
+ this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
9493
+ B.line(`call void @scr_promise_fulfill_str(ptr %dst, ptr ${value})`);
9494
+ }
9495
+ else {
9496
+ const rc = vAdapters(this, inner);
9497
+ this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
9498
+ B.line(`call void @scr_promise_fulfill_ref(ptr %dst, ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(this, inner)})`);
9499
+ }
9500
+ B.terminate(`ret void`);
9501
+ this.resolveThunkDefs.push(`define internal void @${sym}(ptr %dst, ptr %src) ${FN_ATTRS} { ; checked-dynamic promise exit ${key}`, B.render(), `}`, ``);
9502
+ return sym;
9503
+ }
9504
+ streamTypedRefCommitAdapter(t, snapshot) {
9505
+ if (t.kind === "bytes") {
9506
+ const commit = `${snapshot}_commit`;
9507
+ const check = this.dyn.dynCheckHelper(t);
9508
+ this.declare(`declare void @scr_bytes_copy_contents(ptr, ptr)`);
9509
+ this.declare(`declare void @scr_bytes_release(ptr)`);
9510
+ this.resolveThunkDefs.push(`define internal void @${commit}(ptr %target, ptr %d) ${FN_ATTRS} { ; commit live stream element ${typeKey(t)}`, `entry:`, ` %next = call ptr @${check}(ptr %d, ptr null)`, ` %missing = icmp eq ptr %next, null`, ` br i1 %missing, label %done, label %copy`, `copy:`, ` call void @scr_bytes_copy_contents(ptr %target, ptr %next)`, ` call void @scr_bytes_release(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
9511
+ return `@${commit}`;
9512
+ }
9513
+ if (t.kind === "array") {
9514
+ const commit = `${snapshot}_commit`;
9515
+ const check = this.dyn.dynCheckHelper(t);
9516
+ this.declare(`declare void @scr_arr_release(ptr)`);
9517
+ this.resolveThunkDefs.push(`define internal void @${commit}(ptr %target, ptr %d) ${FN_ATTRS} { ; commit live stream element ${typeKey(t)}`, `entry:`, ` %next = call ptr @${check}(ptr %d, ptr null)`, ` %missing = icmp eq ptr %next, null`, ` br i1 %missing, label %done, label %swap`, `swap:`, ` %target_len_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 1`, ` %next_len_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 1`, ` %target_len = load i64, ptr %target_len_ptr`, ` %next_len = load i64, ptr %next_len_ptr`, ` store i64 %next_len, ptr %target_len_ptr`, ` store i64 %target_len, ptr %next_len_ptr`, ` %target_cap_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 2`, ` %next_cap_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 2`, ` %target_cap = load i64, ptr %target_cap_ptr`, ` %next_cap = load i64, ptr %next_cap_ptr`, ` store i64 %next_cap, ptr %target_cap_ptr`, ` store i64 %target_cap, ptr %next_cap_ptr`, ` %target_data_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 7`, ` %next_data_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 7`, ` %target_data = load ptr, ptr %target_data_ptr`, ` %next_data = load ptr, ptr %next_data_ptr`, ` store ptr %next_data, ptr %target_data_ptr`, ` store ptr %target_data, ptr %next_data_ptr`, ` call void @scr_arr_release(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
9518
+ return `@${commit}`;
9519
+ }
9520
+ if (t.kind !== "record")
9521
+ return "null";
9522
+ const shape = this.recordsById.get(t.shapeId);
9523
+ if (!shape) {
9524
+ throw new Error(`llvm emitter bug: stream typed-ref commit of unknown shape ${t.shapeId}`);
9525
+ }
9526
+ const commit = `${snapshot}_commit`;
9527
+ const check = this.dyn.dynCheckHelper(t);
9528
+ const lines = [
9529
+ `define internal void @${commit}(ptr %target, ptr %d) ${FN_ATTRS} { ; commit live stream element ${typeKey(t)}`,
9530
+ `entry:`,
9531
+ ` %next = call ptr @${check}(ptr %d, ptr null)`,
9532
+ ` %missing = icmp eq ptr %next, null`,
9533
+ ` br i1 %missing, label %done, label %swap`,
9534
+ `swap:`,
9535
+ ];
9536
+ const members = [
9537
+ ...shape.fields.map((field, index) => ({
9538
+ index: index + 1,
9539
+ type: llFieldType(field.type),
9540
+ name: field.name,
9541
+ })),
9542
+ ...(shape.indexValue
9543
+ ? [{
9544
+ index: shape.fields.length + 1,
9545
+ type: "ptr",
9546
+ name: "[key: string] overflow",
9547
+ }]
9548
+ : []),
9549
+ ];
9550
+ members.forEach((member, index) => {
9551
+ lines.push(` %tp${index} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %target, i64 0, i32 ${member.index}`, ` %np${index} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %next, i64 0, i32 ${member.index}`, ` %old${index} = load ${member.type}, ptr %tp${index} ; ${member.name}`, ` %new${index} = load ${member.type}, ptr %np${index}`, ` store ${member.type} %new${index}, ptr %tp${index}`, ` store ${member.type} %old${index}, ptr %np${index}`);
9552
+ });
9553
+ lines.push(` call void ${releaseSym(this, t)}(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
9554
+ this.resolveThunkDefs.push(...lines);
9555
+ return `@${commit}`;
9556
+ }
9557
+ /** Box the mutable payload of a union directly, preserving the identity
9558
+ * that AbortSignal reasons and stream chunks expose. Scalar/unit arms use
9559
+ * the ordinary union-to-dyn converter. */
9560
+ liveDynUnionRefAdapter(t) {
9561
+ const key = typeKey(t);
9562
+ const existing = this.liveDynUnionRefAdapters.get(key);
9563
+ if (existing)
9564
+ return existing;
9565
+ const union = this.unionsById.get(t.unionId);
9566
+ if (!union) {
9567
+ throw new Error(`llvm emitter bug: live dyn ref of unknown union ${t.unionId}`);
9568
+ }
9569
+ const mutableArms = union.arms
9570
+ .map((arm, tag) => ({ arm, tag }))
9571
+ .filter(({ arm }) => this.streamTypedRefEligible(arm));
9572
+ if (mutableArms.length === 0) {
9573
+ throw new Error(`llvm emitter bug: live dyn ref of immutable union ${key}`);
9574
+ }
9575
+ const sym = `sc_ldu_${this.liveDynUnionRefAdapters.size}`;
9576
+ this.liveDynUnionRefAdapters.set(key, sym);
9577
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
9578
+ const adapters = new Map();
9579
+ for (const { arm, tag } of mutableArms) {
9580
+ const prefix = `${sym}_${tag}`;
9581
+ adapters.set(tag, this.streamTypedRefMaterializeAdapter(arm, { prefix, adapters: new Map() }, `${prefix}_materialize`));
9582
+ }
9583
+ const B = new BlockBuilder();
9584
+ const tagPtr = B.tmp();
9585
+ const tagValue = B.tmp();
9586
+ B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr %u, i64 0, i32 1`);
9587
+ B.line(`${tagValue} = load i32, ptr ${tagPtr}`);
9588
+ const fallback = B.newLabel("ldu.dyn");
9589
+ const armLabels = mutableArms.map(() => B.newLabel("ldu.ref"));
9590
+ B.terminate(`switch i32 ${tagValue}, label %${fallback} [ ${mutableArms.map(({ tag }, index) => `i32 ${tag}, label %${armLabels[index]}`).join(" ")} ]`);
9591
+ mutableArms.forEach(({ arm, tag }, index) => {
9592
+ const adapter = adapters.get(tag);
9593
+ const rc = vAdapters(this, arm);
9594
+ const armKey = typeKey(arm);
9595
+ B.startBlock(armLabels[index]);
9596
+ const payloadPtr = B.tmp();
9597
+ const payload = B.tmp();
9598
+ const boxed = B.tmp();
9599
+ B.line(`${payloadPtr} = getelementptr inbounds %ScrUnion, ptr %u, i64 0, i32 5`);
9600
+ B.line(`${payload} = load ptr, ptr ${payloadPtr}`);
9601
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${payload}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(armKey)}, i64 ${Buffer.byteLength(armKey, "utf8")}, ptr @${adapter.snapshot}, ptr ${adapter.commit})`);
9602
+ B.terminate(`ret ptr ${boxed}`);
9603
+ });
9604
+ B.startBlock(fallback);
9605
+ const boxed = B.tmp();
9606
+ B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(t)}(ptr %u)`);
9607
+ B.terminate(`ret ptr ${boxed}`);
9608
+ this.resolveThunkDefs.push(`define internal ptr @${sym}(ptr %u) ${FN_ATTRS} { ; materialize live union value ${key}`, B.render(), `}`, ``);
9609
+ return sym;
9610
+ }
9611
+ streamTypedRefEligible(t) {
9612
+ return t.kind === "record" || t.kind === "array" || t.kind === "bytes";
9613
+ }
9614
+ streamTypedRefBoxValue(B, t, value, ctx) {
9615
+ const boxed = B.tmp();
9616
+ if (!this.streamTypedRefEligible(t)) {
9617
+ const valueTy = t.kind === "f64"
9618
+ ? "double"
9619
+ : t.kind === "bool"
9620
+ ? "i1"
9621
+ : "ptr";
9622
+ B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(t)}(${valueTy} ${value})`);
9623
+ return boxed;
9624
+ }
9625
+ const nested = this.streamTypedRefMaterializeAdapter(t, ctx);
9626
+ const rc = vAdapters(this, t);
9627
+ const key = typeKey(t);
9628
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
9629
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(key)}, i64 ${Buffer.byteLength(key, "utf8")}, ptr @${nested.snapshot}, ptr ${nested.commit})`);
9630
+ return boxed;
9631
+ }
9632
+ /** LLVM twin of the C stream materializer: mutable reference children
9633
+ * are their own typed capsules, so writes through a nested alias commit
9634
+ * to the nested static source immediately. */
9635
+ streamTypedRefMaterializeAdapter(t, ctx, preferredSnapshot) {
9636
+ const key = typeKey(t);
9637
+ const existing = ctx.adapters.get(key);
9638
+ if (existing)
9639
+ return existing;
9640
+ const snapshot = preferredSnapshot ??
9641
+ `${ctx.prefix}_nested_${ctx.adapters.size}`;
9642
+ const adapter = { snapshot, commit: "null" };
9643
+ ctx.adapters.set(key, adapter);
9644
+ adapter.commit = this.streamTypedRefCommitAdapter(t, snapshot);
9645
+ const B = new BlockBuilder();
9646
+ if (t.kind === "record") {
9647
+ const shape = this.recordsById.get(t.shapeId);
9648
+ if (!shape) {
9649
+ throw new Error(`llvm emitter bug: stream typed-ref materialize of unknown shape ${t.shapeId}`);
9650
+ }
9651
+ /* Keep the ordinary converter for index-signature/listener records;
9652
+ * their source-identity and overflow walks have extra contracts.
9653
+ * Declared-field records and tuples cover the live stream values. */
9654
+ const ordinary = shape.indexValue || shape.fields.some((field) => field.name === "handleEvent" && field.type.kind === "func");
9655
+ if (ordinary) {
9656
+ const out = B.tmp();
9657
+ B.line(`${out} = call ptr @${this.dyn.toDynHelper(t)}(ptr %p)`);
9658
+ B.terminate(`ret ptr ${out}`);
9659
+ }
9660
+ else if (shape.tuple) {
9661
+ this.declare(`declare ptr @scr_dyn_new_arr()`);
9662
+ this.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
9663
+ const out = B.tmp();
9664
+ B.line(`${out} = call ptr @scr_dyn_new_arr()`);
9665
+ const fields = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
9666
+ for (const field of fields) {
9667
+ const index = shape.fields.indexOf(field) + 1;
9668
+ const fieldPtr = B.tmp();
9669
+ const fieldValue = B.tmp();
9670
+ B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
9671
+ B.line(`${fieldValue} = load ${llFieldType(field.type)}, ptr ${fieldPtr}`);
9672
+ const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
9673
+ B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${boxed})`);
9674
+ }
9675
+ B.terminate(`ret ptr ${out}`);
9676
+ }
9677
+ else {
9678
+ this.declare(`declare ptr @scr_dyn_new_obj()`);
9679
+ this.declare(`declare void @scr_dyn_obj_set(ptr, ptr, i64, ptr)`);
9680
+ const out = B.tmp();
9681
+ B.line(`${out} = call ptr @scr_dyn_new_obj()`);
9682
+ const byName = new Map(shape.fields.map((field) => [field.name, field]));
9683
+ const order = shape.declaredOrder ?? shape.fields.map((field) => field.name);
9684
+ const inOrder = new Set(order);
9685
+ const fields = [
9686
+ ...order.map((name) => byName.get(name)).filter((field) => field !== undefined),
9687
+ ...shape.fields.filter((field) => !inOrder.has(field.name)),
9688
+ ];
9689
+ for (const field of fields) {
9690
+ const index = shape.fields.indexOf(field) + 1;
9691
+ const fieldPtr = B.tmp();
9692
+ const fieldValue = B.tmp();
9693
+ B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
9694
+ B.line(`${fieldValue} = load ${llFieldType(field.type)}, ptr ${fieldPtr}`);
9695
+ const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
9696
+ B.line(`call void @scr_dyn_obj_set(ptr ${out}, ptr ${this.cstr(field.name)}, i64 ${Buffer.byteLength(field.name, "utf8")}, ptr ${boxed})`);
9697
+ }
9698
+ B.terminate(`ret ptr ${out}`);
9699
+ }
9700
+ }
9701
+ else if (t.kind === "array") {
9702
+ const elem = t.elem;
9703
+ this.declare(`declare ptr @scr_dyn_new_arr()`);
9704
+ this.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
9705
+ this.declare(`declare double @scr_arr_len(ptr)`);
9706
+ const out = B.tmp();
9707
+ B.line(`${out} = call ptr @scr_dyn_new_arr()`);
9708
+ const len = B.tmp();
9709
+ B.line(`${len} = call double @scr_arr_len(ptr %p)`);
9710
+ const iSlot = B.slot();
9711
+ B.entryAllocas.push(`${iSlot} = alloca double`);
9712
+ B.line(`store double ${f64Lit(0)}, ptr ${iSlot}`);
9713
+ const condition = B.newLabel("strm.c");
9714
+ const body = B.newLabel("strm.b");
9715
+ const done = B.newLabel("strm.e");
9716
+ B.br(condition);
9717
+ B.startBlock(condition);
9718
+ const index = B.tmp();
9719
+ const more = B.tmp();
9720
+ B.line(`${index} = load double, ptr ${iSlot}`);
9721
+ B.line(`${more} = fcmp olt double ${index}, ${len}`);
9722
+ B.condBr(more, body, done);
9723
+ B.startBlock(body);
9724
+ let value;
9725
+ if (elem.kind === "f64" || elem.kind === "bool") {
9726
+ const valueTy = elem.kind === "f64" ? "double" : "i1";
9727
+ this.declare(`declare ${elem.kind === "bool" ? "zeroext i1" : valueTy} @scr_arr_get_${elem.kind}(ptr, double)`);
9728
+ value = B.tmp();
9729
+ B.line(`${value} = call ${valueTy} @scr_arr_get_${elem.kind}(ptr %p, double ${index})`);
9730
+ }
9731
+ else {
9732
+ this.declare(`declare ptr @scr_arr_get_ref(ptr, double)`);
9733
+ value = B.tmp();
9734
+ B.line(`${value} = call ptr @scr_arr_get_ref(ptr %p, double ${index}) ; +1`);
9735
+ }
9736
+ const boxed = this.streamTypedRefBoxValue(B, elem, value, ctx);
9737
+ B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${boxed})`);
9738
+ if (isRefCounted(elem)) {
9739
+ B.line(`call void ${releaseSym(this, elem)}(ptr ${value})`);
9740
+ }
9741
+ const next = B.tmp();
9742
+ B.line(`${next} = fadd double ${index}, ${f64Lit(1)}`);
9743
+ B.line(`store double ${next}, ptr ${iSlot}`);
9744
+ B.br(condition);
9745
+ B.startBlock(done);
9746
+ B.terminate(`ret ptr ${out}`);
9747
+ }
9748
+ else {
9749
+ const out = B.tmp();
9750
+ B.line(`${out} = call ptr @${this.dyn.toDynHelper(t)}(ptr %p)`);
9751
+ B.terminate(`ret ptr ${out}`);
9752
+ }
9753
+ this.resolveThunkDefs.push(`define internal ptr @${snapshot}(ptr %p) ${FN_ATTRS} { ; materialize live stream value ${key}`, B.render(), `}`, ``);
9754
+ return adapter;
9755
+ }
9756
+ streamFromArrayAdapter(t) {
9757
+ const elem = t.elem;
9758
+ const key = typeKey(elem);
9759
+ const existing = this.streamFromArrayAdapters.get(key);
9760
+ if (existing)
9761
+ return existing;
9762
+ const sym = `sc_sfa_${this.streamFromArrayAdapters.size}`;
9763
+ this.streamFromArrayAdapters.set(key, sym);
9764
+ const B = new BlockBuilder();
9765
+ const unionDef = elem.kind === "union"
9766
+ ? this.unionsById.get(elem.unionId)
9767
+ : undefined;
9768
+ if (elem.kind === "union" && !unionDef) {
9769
+ throw new Error(`llvm emitter bug: streamFrom of unknown union ${elem.unionId}`);
9770
+ }
9771
+ const unionRefArms = unionDef?.arms
9772
+ .map((arm, tag) => ({ arm, tag }))
9773
+ .filter(({ arm }) => isRefCounted(arm) && arm.kind !== "dyn" && arm.kind !== "string") ?? [];
9774
+ const typedRef = isRefCounted(elem) &&
9775
+ elem.kind !== "dyn" &&
9776
+ elem.kind !== "string" &&
9777
+ elem.kind !== "union";
9778
+ const snapshot = `${sym}_materialize`;
9779
+ let value;
9780
+ if (elem.kind === "f64") {
9781
+ this.declare(`declare double @scr_arr_get_f64(ptr, double)`);
9782
+ value = B.tmp();
9783
+ B.line(`${value} = call double @scr_arr_get_f64(ptr %a, double %i)`);
9784
+ }
9785
+ else if (elem.kind === "bool") {
9786
+ this.declare(`declare zeroext i1 @scr_arr_get_bool(ptr, double)`);
9787
+ value = B.tmp();
9788
+ B.line(`${value} = call i1 @scr_arr_get_bool(ptr %a, double %i)`);
9789
+ }
9790
+ else {
9791
+ this.declare(`declare ptr @scr_arr_get_ref(ptr, double)`);
9792
+ value = B.tmp();
9793
+ B.line(`${value} = call ptr @scr_arr_get_ref(ptr %a, double %i) ; +1`);
9794
+ }
9795
+ let boxed;
9796
+ const valueTy = elem.kind === "f64" ? "double" : elem.kind === "bool" ? "i1" : "ptr";
9797
+ if (elem.kind === "union") {
9798
+ if (unionRefArms.length === 0) {
9799
+ boxed = B.tmp();
9800
+ B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(elem)}(ptr ${value})`);
9801
+ }
9802
+ else {
9803
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
9804
+ const boxedSlot = B.slot();
9805
+ B.entryAllocas.push(`${boxedSlot} = alloca ptr`);
9806
+ const tagPtr = B.tmp();
9807
+ const tagValue = B.tmp();
9808
+ B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr ${value}, i64 0, i32 1`);
9809
+ B.line(`${tagValue} = load i32, ptr ${tagPtr}`);
9810
+ const fallback = B.newLabel("sfa.union.dyn");
9811
+ const join = B.newLabel("sfa.union.join");
9812
+ const armLabels = unionRefArms.map(() => B.newLabel("sfa.union.ref"));
9813
+ B.terminate(`switch i32 ${tagValue}, label %${fallback} [ ${unionRefArms.map(({ tag }, i) => `i32 ${tag}, label %${armLabels[i]}`).join(" ")} ]`);
9814
+ unionRefArms.forEach(({ arm, tag }, i) => {
9815
+ const armKey = typeKey(arm);
9816
+ const armSnapshot = `${snapshot}_${tag}`;
9817
+ const armCommit = this.streamTypedRefCommitAdapter(arm, armSnapshot);
9818
+ const rc = vAdapters(this, arm);
9819
+ B.startBlock(armLabels[i]);
9820
+ const payloadPtr = B.tmp();
9821
+ const payload = B.tmp();
9822
+ const armBoxed = B.tmp();
9823
+ B.line(`${payloadPtr} = getelementptr inbounds %ScrUnion, ptr ${value}, i64 0, i32 5`);
9824
+ B.line(`${payload} = load ptr, ptr ${payloadPtr}`);
9825
+ B.line(`${armBoxed} = call ptr @scr_dyn_new_typed_ref(ptr ${payload}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(armKey)}, i64 ${Buffer.byteLength(armKey, "utf8")}, ptr @${armSnapshot}, ptr ${armCommit})`);
9826
+ B.line(`store ptr ${armBoxed}, ptr ${boxedSlot}`);
9827
+ B.br(join);
9828
+ this.resolveThunkDefs.push(`define internal ptr @${armSnapshot}(ptr %p) ${FN_ATTRS} { ; materialize stream union arm ${armKey}`, `entry:`, ` %d = call ptr @${this.dyn.toDynHelper(arm)}(ptr %p)`, ` ret ptr %d`, `}`, ``);
9829
+ });
9830
+ B.startBlock(fallback);
9831
+ const dynBoxed = B.tmp();
9832
+ B.line(`${dynBoxed} = call ptr @${this.dyn.toDynHelper(elem)}(ptr ${value})`);
9833
+ B.line(`store ptr ${dynBoxed}, ptr ${boxedSlot}`);
9834
+ B.br(join);
9835
+ B.startBlock(join);
9836
+ boxed = B.tmp();
9837
+ B.line(`${boxed} = load ptr, ptr ${boxedSlot}`);
9838
+ }
9839
+ }
9840
+ else if (typedRef) {
9841
+ boxed = B.tmp();
9842
+ const rc = vAdapters(this, elem);
9843
+ const keyPtr = this.cstr(key);
9844
+ let commit;
9845
+ if (this.streamTypedRefEligible(elem)) {
9846
+ commit = this.streamTypedRefMaterializeAdapter(elem, { prefix: snapshot, adapters: new Map() }, snapshot).commit;
9847
+ }
9848
+ else {
9849
+ commit = this.streamTypedRefCommitAdapter(elem, snapshot);
9850
+ this.resolveThunkDefs.push(`define internal ptr @${snapshot}(ptr %p) ${FN_ATTRS} { ; materialize stream element ${key}`, `entry:`, ` %d = call ptr @${this.dyn.toDynHelper(elem)}(ptr %p)`, ` ret ptr %d`, `}`, ``);
9851
+ }
9852
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
9853
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${keyPtr}, i64 ${Buffer.byteLength(key, "utf8")}, ptr @${snapshot}, ptr ${commit})`);
9854
+ }
9855
+ else {
9856
+ boxed = B.tmp();
9857
+ B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(elem)}(${valueTy} ${value})`);
9858
+ }
9859
+ if (isRefCounted(elem)) {
9860
+ B.line(`call void ${releaseSym(this, elem)}(ptr ${value})`);
9861
+ }
9862
+ B.terminate(`ret ptr ${boxed}`);
9863
+ this.resolveThunkDefs.push(`define internal ptr @${sym}(ptr %a, double %i) ${FN_ATTRS} { ; ReadableStream.from array<${key}>`, B.render(), `}`, ``);
9864
+ return sym;
9865
+ }
9122
9866
  /** The claimed libCall slice: args are BORROWED (owned temps of the
9123
9867
  * current frame), refcounted results come back +1. LLVM types derive
9124
9868
  * from the call site's IR types (exactly the contract the C prototypes
@@ -9131,6 +9875,70 @@ class LlEmitter {
9131
9875
  // Loop liveness first (one table for generic and special shapes).
9132
9876
  if (USES_TIMERS_LIB_FNS.has(e.fn))
9133
9877
  this.usesTimers = true;
9878
+ if (e.fn === "fetch.responseText" || e.fn === "fetch.responseBytes") {
9879
+ if (e.type.kind !== "promise") {
9880
+ throw new Error(`llvm emitter bug: ${e.fn} result`);
9881
+ }
9882
+ const response = this.emitExpr(e.args[0]);
9883
+ const runtimeFn = e.fn === "fetch.responseText"
9884
+ ? "scr_fetch_response_text"
9885
+ : "scr_fetch_response_bytes";
9886
+ this.declare(`declare ptr @${runtimeFn}(ptr)`);
9887
+ this.declare(`declare ptr @scr_promise_new()`);
9888
+ this.declare(`declare void @scr_promise_race_add(ptr, ptr, ptr)`);
9889
+ const sourceRaw = B.tmp();
9890
+ B.line(`${sourceRaw} = call ptr @${runtimeFn}(ptr ${response.name})`);
9891
+ const source = this.own({
9892
+ name: sourceRaw,
9893
+ type: { kind: "promise", inner: DYN },
9894
+ });
9895
+ const resultRaw = B.tmp();
9896
+ B.line(`${resultRaw} = call ptr @scr_promise_new()`);
9897
+ const result = this.own({ name: resultRaw, type: e.type });
9898
+ B.line(`call void @scr_promise_race_add(ptr ${result.name}, ptr ${source.name}, ptr @${this.dynPromiseAdapter(e.type.inner)})`);
9899
+ return result;
9900
+ }
9901
+ if (e.fn === "fetch.readerRead") {
9902
+ if (e.type.kind !== "promise" || e.type.inner.kind !== "record") {
9903
+ throw new Error("llvm emitter bug: fetch.readerRead result");
9904
+ }
9905
+ const reader = this.emitExpr(e.args[0]);
9906
+ this.declare(`declare ptr @scr_fetch_reader_read(ptr)`);
9907
+ this.declare(`declare ptr @scr_promise_new()`);
9908
+ this.declare(`declare void @scr_promise_race_add(ptr, ptr, ptr)`);
9909
+ const sourceRaw = B.tmp();
9910
+ B.line(`${sourceRaw} = call ptr @scr_fetch_reader_read(ptr ${reader.name})`);
9911
+ const source = this.own({
9912
+ name: sourceRaw,
9913
+ type: { kind: "promise", inner: DYN },
9914
+ });
9915
+ const resultRaw = B.tmp();
9916
+ B.line(`${resultRaw} = call ptr @scr_promise_new()`);
9917
+ const result = this.own({ name: resultRaw, type: e.type });
9918
+ B.line(`call void @scr_promise_race_add(ptr ${result.name}, ptr ${source.name}, ptr @${this.dynPromiseAdapter(e.type.inner)})`);
9919
+ return result;
9920
+ }
9921
+ if (e.fn === "fetch.streamFrom") {
9922
+ const source = this.emitExpr(e.args[0]);
9923
+ let sym = "scr_fetch_stream_from";
9924
+ let extra = "";
9925
+ if (source.type.kind === "array") {
9926
+ sym = "scr_fetch_stream_from_array";
9927
+ extra = `, ptr @${this.streamFromArrayAdapter(source.type)}`;
9928
+ }
9929
+ else if (source.type.kind === "bytes") {
9930
+ sym = "scr_fetch_stream_from_bytes";
9931
+ }
9932
+ else if (source.type.kind === "string") {
9933
+ sym = "scr_fetch_stream_from_string";
9934
+ }
9935
+ this.declare(`declare ptr @${sym}(ptr${source.type.kind === "array" ? ", ptr" : ""})`);
9936
+ const raw = B.tmp();
9937
+ B.line(`${raw} = call ptr @${sym}(ptr ${source.name}${extra})`);
9938
+ const out = this.own({ name: raw, type: e.type });
9939
+ this.emitPendingCheck();
9940
+ return out;
9941
+ }
9134
9942
  // The handful with non-generic shapes first.
9135
9943
  if (e.fn === "error.argTypeThrow") {
9136
9944
  // Always throws with the runtime-rendered Received tail (the