@scriptc/compiler 0.0.8 → 0.0.10

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 (90) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +13 -0
  2. package/dist/backend/cc.js +14 -0
  3. package/dist/backend/cc.js.map +1 -1
  4. package/dist/backend/emission/emit-exprs.js +244 -17
  5. package/dist/backend/emission/emit-exprs.js.map +1 -1
  6. package/dist/backend/emission/emit-types.d.ts +3 -0
  7. package/dist/backend/emission/emit-types.js +12 -0
  8. package/dist/backend/emission/emit-types.js.map +1 -1
  9. package/dist/backend/emission/emit-walkers.d.ts +7 -5
  10. package/dist/backend/emission/emit-walkers.js +52 -12
  11. package/dist/backend/emission/emit-walkers.js.map +1 -1
  12. package/dist/backend/emission/emitter.d.ts +1 -1
  13. package/dist/backend/emission/emitter.js +30 -9
  14. package/dist/backend/emission/emitter.js.map +1 -1
  15. package/dist/backend/llvm/dyn.d.ts +1 -0
  16. package/dist/backend/llvm/dyn.js +83 -1
  17. package/dist/backend/llvm/dyn.js.map +1 -1
  18. package/dist/backend/llvm/emitter.js +357 -20
  19. package/dist/backend/llvm/emitter.js.map +1 -1
  20. package/dist/diagnostics/diagnostic.d.ts +73 -14
  21. package/dist/diagnostics/diagnostic.js +112 -31
  22. package/dist/diagnostics/diagnostic.js.map +1 -1
  23. package/dist/diagnostics/render.js +8 -0
  24. package/dist/diagnostics/render.js.map +1 -1
  25. package/dist/frontend/lowering/lib-boundary.js +11 -1
  26. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  27. package/dist/frontend/lowering/lower-assert.js +16 -0
  28. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  29. package/dist/frontend/lowering/lower-builtins.d.ts +24 -0
  30. package/dist/frontend/lowering/lower-builtins.js +256 -3
  31. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  32. package/dist/frontend/lowering/lower-calls.d.ts +71 -11
  33. package/dist/frontend/lowering/lower-calls.js +942 -18
  34. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  35. package/dist/frontend/lowering/lower-classes.js +27 -1
  36. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  37. package/dist/frontend/lowering/lower-containers.d.ts +21 -2
  38. package/dist/frontend/lowering/lower-containers.js +386 -53
  39. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  40. package/dist/frontend/lowering/lower-dgram.js +92 -17
  41. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  42. package/dist/frontend/lowering/lower-emitter.js +54 -0
  43. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  44. package/dist/frontend/lowering/lower-exprs.js +231 -17
  45. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  46. package/dist/frontend/lowering/lower-modules.js +33 -2
  47. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  48. package/dist/frontend/lowering/lower-namespaces.d.ts +25 -11
  49. package/dist/frontend/lowering/lower-namespaces.js +50 -15
  50. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  51. package/dist/frontend/lowering/lower-server.js +211 -4
  52. package/dist/frontend/lowering/lower-server.js.map +1 -1
  53. package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
  54. package/dist/frontend/lowering/lower-stmts.js +660 -19
  55. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  56. package/dist/frontend/lowering/lower-stream.js +92 -5
  57. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  58. package/dist/frontend/lowering/lowerer.d.ts +102 -15
  59. package/dist/frontend/lowering/lowerer.js +378 -30
  60. package/dist/frontend/lowering/lowerer.js.map +1 -1
  61. package/dist/frontend/lowering/surfaces.js +20 -3
  62. package/dist/frontend/lowering/surfaces.js.map +1 -1
  63. package/dist/frontend/npm-static-rewrite.d.ts +7 -2
  64. package/dist/frontend/npm-static-rewrite.js +566 -138
  65. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  66. package/dist/frontend/npm-static.js +11 -2
  67. package/dist/frontend/npm-static.js.map +1 -1
  68. package/dist/frontend/program.js +5 -5
  69. package/dist/frontend/program.js.map +1 -1
  70. package/dist/frontend/shared.d.ts +6 -1
  71. package/dist/frontend/shared.js +46 -1
  72. package/dist/frontend/shared.js.map +1 -1
  73. package/dist/frontend/types.d.ts +13 -0
  74. package/dist/frontend/types.js +88 -9
  75. package/dist/frontend/types.js.map +1 -1
  76. package/dist/index.js +169 -32
  77. package/dist/index.js.map +1 -1
  78. package/dist/ir/nodes.d.ts +230 -12
  79. package/dist/ir/nodes.js +85 -6
  80. package/dist/ir/nodes.js.map +1 -1
  81. package/dist/ir/validate.js +109 -9
  82. package/dist/ir/validate.js.map +1 -1
  83. package/dist/library/fence-eval.d.ts +77 -0
  84. package/dist/library/fence-eval.js +385 -0
  85. package/dist/library/fence-eval.js.map +1 -0
  86. package/dist/library/profile.d.ts +11 -1
  87. package/dist/library/profile.js +102 -18
  88. package/dist/library/profile.js.map +1 -1
  89. package/package.json +2 -2
  90. package/surface-manifest.json +10 -3
@@ -1,4 +1,4 @@
1
- import { canMarshalFuncIntoIsland, CAUGHT, F64, islandCallbackRet, 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, 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";
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";
@@ -60,6 +60,8 @@ const LIB_FN_SYMS = {
60
60
  "dyn.errInstanceof": "scr_dyn_err_instanceof",
61
61
  "num.toExponential": "scr_num_to_exponential",
62
62
  "num.toFixed0": "scr_num_to_fixed0",
63
+ "num.sameValue": "scr_num_same_value",
64
+ "intl.numFormatEnUs": "scr_intl_num_format_en_us",
63
65
  "number.isFinite": "scr_num_is_finite",
64
66
  "number.isNaN": "scr_num_is_nan",
65
67
  "number.isInteger": "scr_num_is_integer",
@@ -187,6 +189,19 @@ const LIB_FN_SYMS = {
187
189
  "buffer.concatLen": "scr_bytes_concat_len",
188
190
  "buffer.byteLenStr": "scr_bytes_byte_length_str",
189
191
  "buffer.isEncoding": "scr_bytes_is_encoding",
192
+ // The checked-dynamic compare/equals validators (scr_bytes_io.c):
193
+ // Node's argument ladders throw catchably (MAY_THROW_LIB_FNS).
194
+ "dyn.toStringCoerce": "scr_dyn_string_coerce_js",
195
+ "buffer.compareChk": "scr_buffer_compare_chk",
196
+ "bytes.equalsChk": "scr_bytes_equals_chk",
197
+ "bytes.compareChk": "scr_bytes_compare_chk",
198
+ "buffer.newStringFail": "scr_buffer_new_string_fail",
199
+ "fs.toUnixTimestamp": "scr_fs_to_unix_timestamp",
200
+ "fs.existsChk": "scr_fs_exists_async",
201
+ "fs.mkdtempSyncChk": "scr_fs_mkdtemp_sync_chk",
202
+ "net.connectAttempt": "scr_net_connect_attempt",
203
+ "fs.lchmodSyncChk": "scr_fs_lchmod_sync_chk",
204
+ "fsp.lchmodChk": "scr_fsp_lchmod_chk",
190
205
  "fs.readFileSyncBuf": "scr_fs_read_file_bytes",
191
206
  "fs.readFileSyncBytes": "scr_fs_read_file_bytes",
192
207
  "fs.writeFileSyncBytes": "scr_fs_write_file_bytes",
@@ -332,9 +347,10 @@ const LIB_FN_SYMS = {
332
347
  // ambient-this read never throw. The fs dyn read is the sync-fs story.
333
348
  "json.parse": "scr_json_parse",
334
349
  "dyn.keySet": "scr_dyn_key_set",
350
+ "dyn.iterPack": "scr_dyn_iter_pack",
335
351
  "dyn.defineProps": "scr_dyn_define_props",
336
352
  "dyn.typeof": "scr_dyn_typeof",
337
- "dyn.toString": "scr_dyn_to_string",
353
+ "dyn.toString": "scr_dyn_to_string_method",
338
354
  "dyn.this": "scr_dyn_this_get",
339
355
  "insp.dyn": "scr_insp_dyn",
340
356
  "insp.dynS": "scr_insp_dyn_s",
@@ -376,6 +392,8 @@ const LIB_FN_SYMS = {
376
392
  "emitter.names": "scr_emitter_event_names",
377
393
  "emitter.listeners": "scr_emitter_listeners",
378
394
  "emitter.setMax": "scr_emitter_set_max",
395
+ "emitter.setMaxChk": "scr_emitter_set_max_chk",
396
+ "emitter.setDefaultMaxChk": "scr_emitter_set_default_max_chk",
379
397
  "emitter.getMax": "scr_emitter_get_max",
380
398
  "emitter.setDefaultMax": "scr_emitter_set_default_max",
381
399
  "emitter.getDefaultMax": "scr_emitter_get_default_max",
@@ -483,6 +501,9 @@ const LIB_FN_SYMS = {
483
501
  // generic shapes; the throwing members ride the pending check.
484
502
  "tp.setTimeout": "scr_tp_set_timeout",
485
503
  "tp.setImmediate": "scr_tp_set_immediate",
504
+ // stream/promises' finished form (+1 promise; may-throw pending check;
505
+ // the pipeline form needs the stream-array compound and emits below).
506
+ "sp.finished": "scr_sp_finished",
486
507
  "dc.channel": "scr_dc_channel",
487
508
  "dc.subscribe": "scr_dc_subscribe",
488
509
  "dc.unsubscribe": "scr_dc_unsubscribe",
@@ -544,6 +565,14 @@ const LIB_FN_SYMS = {
544
565
  "dyn.objKeys": "scr_dyn_obj_keys",
545
566
  "dyn.hasOwn": "scr_dyn_has_own",
546
567
  "dyn.assign": "scr_dyn_assign",
568
+ // variadic Object.assign: the source pack (push never throws; the
569
+ // spread flatten throws V8's spread-call TypeErrors) and the final
570
+ // left-to-right copy (ToObject TypeError on a nullish target).
571
+ "dyn.packPush": "scr_dyn_pack_push",
572
+ "dyn.packPushSpread": "scr_dyn_pack_push_spread",
573
+ "dyn.packPushSpreadIter": "scr_dyn_pack_push_spread_iter",
574
+ "dyn.assignAll": "scr_dyn_assign_all",
575
+ "dyn.objCreateNullProto": "scr_dyn_new_obj_null_proto",
547
576
  "dyn.objValues": "scr_dyn_obj_values",
548
577
  "dyn.objEntries": "scr_dyn_obj_entries",
549
578
  "dyn.structuredClone": "scr_structured_clone",
@@ -586,8 +615,10 @@ const USES_TIMERS_LIB_FNS = new Set([
586
615
  "stream.setTransform", "stream.setFlush",
587
616
  "process.activeResources",
588
617
  "stream.finished", "stream.finishedDyn", "stream.pipeline", "stream.pipelineDyn",
618
+ "sp.finished", "sp.pipeline",
589
619
  "net.listen", "net.listenCb", "net.listenOpts", "net.listenOptsCb",
590
- "net.connect", "net.connectCb", "net.connectLookup",
620
+ "net.connect", "net.connectCb", "net.connectLookup", "net.connectAttempt",
621
+ "fs.existsChk",
591
622
  "http.createServer", "http.createServerEmpty",
592
623
  "http.request", "http.requestCb", "http.requestUrl", "http.requestUrlCb",
593
624
  "http.requestConn", "http.requestConnCb",
@@ -640,6 +671,18 @@ const DV_GET_KIND = {
640
671
  dvGetBigUint64Number: 8,
641
672
  dvGetBigInt64Number: 9,
642
673
  };
674
+ /** ScrDataViewGet per dvSet* method (the setters reuse the getter kinds;
675
+ * no BIG setters exist). */
676
+ const DV_SET_KIND = {
677
+ dvSetUint8: 0,
678
+ dvSetInt8: 1,
679
+ dvSetUint16: 2,
680
+ dvSetInt16: 3,
681
+ dvSetUint32: 4,
682
+ dvSetInt32: 5,
683
+ dvSetFloat32: 6,
684
+ dvSetFloat64: 7,
685
+ };
643
686
  class LlEmitter {
644
687
  mod;
645
688
  /** Interned string literals: UTF-8 text → { symbol, byte length } —
@@ -1015,7 +1058,7 @@ class LlEmitter {
1015
1058
  // LIBRARY mode: the runtime entry points the generated library
1016
1059
  // symbols delegate to — declared before the extern block flushes.
1017
1060
  if (this.mod.lib !== undefined) {
1018
- this.declare(`declare void @scr_library_entry(i1 zeroext)`);
1061
+ this.declare(`declare void @scr_library_entry(i1 zeroext, ptr)`);
1019
1062
  this.declare(`declare void @scr_library_reset()`);
1020
1063
  this.declare(`declare void @scr_library_check_exc()`);
1021
1064
  this.declare(`declare void @scr_library_set_sink(ptr, ptr)`);
@@ -1259,6 +1302,45 @@ class LlEmitter {
1259
1302
  const lib = this.mod.lib;
1260
1303
  const autoReset = lib.resultResetSymbol === null;
1261
1304
  const out = [``, `; ── library-mode entries (profile: ${lib.profileName}) ──`, ``];
1305
+ // Every entry's prologue records its external symbol in the funnel's
1306
+ // current-entry slot (structured trap-teaching field 2); the symbols
1307
+ // live as internal constants, one per entry.
1308
+ const symConst = (sym) => `@sc_lib_sym_${sym}`;
1309
+ const emitSymConst = (sym) => {
1310
+ out.push(`${symConst(sym)} = internal constant [${Buffer.byteLength(sym, "utf8") + 1} x i8] c"${llStrBytes(sym)}"`);
1311
+ };
1312
+ emitSymConst(lib.initSymbol);
1313
+ if (lib.resultResetSymbol !== null)
1314
+ emitSymConst(lib.resultResetSymbol);
1315
+ if (lib.collectSymbol !== null)
1316
+ emitSymConst(lib.collectSymbol);
1317
+ for (const e of lib.exports)
1318
+ emitSymConst(e.symbol);
1319
+ out.push(``);
1320
+ // The runtime detected-trap overlay table (scr_runtime.h declares it,
1321
+ // the library trap funnel consults it): flat code/teaching/remediation
1322
+ // triples, one per runtime trap code (SC4013–SC4019) the profile
1323
+ // declares text for — the same data the C emission defines, so the
1324
+ // funnel-assembled sink message is emission-invariant by construction.
1325
+ // The empty table still defines the symbols the funnel links against.
1326
+ const ovlCells = [];
1327
+ lib.trapOverlays.forEach((o, i) => {
1328
+ const cell = (name, text) => {
1329
+ if (text === undefined) {
1330
+ ovlCells.push("ptr null");
1331
+ return;
1332
+ }
1333
+ const sym = `@sc_lib_ovl_${i}_${name}`;
1334
+ out.push(`${sym} = internal constant [${Buffer.byteLength(text, "utf8") + 1} x i8] c"${llStrBytes(text)}"`);
1335
+ ovlCells.push(`ptr ${sym}`);
1336
+ };
1337
+ cell("code", o.code);
1338
+ cell("teach", o.teaching);
1339
+ cell("rem", o.remediation);
1340
+ });
1341
+ out.push(ovlCells.length === 0
1342
+ ? `@scr_library_trap_overlays = constant [1 x ptr] zeroinitializer`
1343
+ : `@scr_library_trap_overlays = constant [${ovlCells.length} x ptr] [${ovlCells.join(", ")}]`, `@scr_library_trap_overlays_len = constant i64 ${lib.trapOverlays.length}`, ``);
1262
1344
  // The init entry: full deterministic reset-and-reevaluate. Program
1263
1345
  // globals release and zero first (run-once guards included), then the
1264
1346
  // runtime session reset, the error-vt interval stamps verbatim from
@@ -1268,7 +1350,7 @@ class LlEmitter {
1268
1350
  const zero = ty === "double" ? f64Lit(0) : ty === "ptr" ? "null" : "false";
1269
1351
  return ` store ${ty} ${zero}, ptr @${mangleGlobal(g.id)} ; ${g.name}`;
1270
1352
  });
1271
- out.push(`define void @${lib.initSymbol}() ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_entry(i1 zeroext true) ; init always resets the result arena`, ...globalReleaseLines("ci"), ...zeroStores, ` call void @scr_library_reset()`, ...stamps, ` call void @${mangleFunction(this.mod.entry)}()`, ` call void @scr_library_check_exc()`, ` ret void`, `}`, ``, `define void @${lib.sinkRegisterSymbol}(ptr %fn, ptr %ctx) ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_set_sink(ptr %fn, ptr %ctx)`, ` ret void`, `}`, ``);
1353
+ out.push(`define void @${lib.initSymbol}() ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_entry(i1 zeroext true, ptr ${symConst(lib.initSymbol)}) ; init always resets the result arena`, ...globalReleaseLines("ci"), ...zeroStores, ` call void @scr_library_reset()`, ...stamps, ` call void @${mangleFunction(this.mod.entry)}()`, ` call void @scr_library_check_exc()`, ` ret void`, `}`, ``, `define void @${lib.sinkRegisterSymbol}(ptr %fn, ptr %ctx) ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_set_sink(ptr %fn, ptr %ctx)`, ` ret void`, `}`, ``);
1272
1354
  if (lib.identity !== undefined) {
1273
1355
  // Profile-declared identity getters (the ask-2 sidecar's boot-time
1274
1356
  // pairing fence): pure data returns with NO entry prologue — exempt
@@ -1279,14 +1361,14 @@ class LlEmitter {
1279
1361
  out.push(`define i64 @${lib.identity.buildIdSymbol}() ${FN_ATTRS} { ; identity getter build_id 0x${lib.identity.buildId}`, `entry:`, ` ret i64 ${buildId}`, `}`, ``, `define i32 @${lib.identity.abiVersionSymbol}() ${FN_ATTRS} { ; identity getter abi_version`, `entry:`, ` ret i32 ${lib.identity.abiVersion}`, `}`, ``);
1280
1362
  }
1281
1363
  if (lib.resultResetSymbol !== null) {
1282
- out.push(`define void @${lib.resultResetSymbol}() ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_entry(i1 zeroext false)`, ` call void @scr_library_arena_reset()`, ` ret void`, `}`, ``);
1364
+ out.push(`define void @${lib.resultResetSymbol}() ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_entry(i1 zeroext false, ptr ${symConst(lib.resultResetSymbol)})`, ` call void @scr_library_arena_reset()`, ` ret void`, `}`, ``);
1283
1365
  }
1284
1366
  if (lib.collectSymbol !== null) {
1285
- out.push(`define void @${lib.collectSymbol}() ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_entry(i1 zeroext false)`, ` call void @scr_library_collect() ; arena reset + a full cycle collection`, ` ret void`, `}`, ``);
1367
+ out.push(`define void @${lib.collectSymbol}() ${FN_ATTRS} {`, `entry:`, ` call void @scr_library_entry(i1 zeroext false, ptr ${symConst(lib.collectSymbol)})`, ` call void @scr_library_collect() ; arena reset + a full cycle collection`, ` ret void`, `}`, ``);
1286
1368
  }
1287
1369
  for (const e of lib.exports) {
1288
1370
  const params = [];
1289
- const body = [` call void @scr_library_entry(i1 zeroext ${autoReset ? "true" : "false"})`];
1371
+ const body = [` call void @scr_library_entry(i1 zeroext ${autoReset ? "true" : "false"}, ptr ${symConst(e.symbol)})`];
1290
1372
  const args = [];
1291
1373
  if (e.inboundBytesTrap !== undefined) {
1292
1374
  // The bytes-in helper's trap message: the compiler-assembled
@@ -3999,7 +4081,15 @@ class LlEmitter {
3999
4081
  const t = B.tmp();
4000
4082
  B.line(`${a} = call double @scr_union_get_f64(ptr ${l.name})`);
4001
4083
  B.line(`${b} = call double @scr_union_get_f64(ptr ${r.name})`);
4002
- B.line(`${t} = fcmp oeq double ${a}, ${b}`);
4084
+ if (e.sameValue) {
4085
+ // Object.is's f64 compare: NaN equals NaN, +0 differs
4086
+ // from -0 — the runtime SameValue.
4087
+ this.declare(`declare zeroext i1 @scr_num_same_value(double, double)`);
4088
+ B.line(`${t} = call zeroext i1 @scr_num_same_value(double ${a}, double ${b})`);
4089
+ }
4090
+ else {
4091
+ B.line(`${t} = fcmp oeq double ${a}, ${b}`);
4092
+ }
4003
4093
  B.line(`store i1 ${t}, ptr ${slot}`);
4004
4094
  break;
4005
4095
  }
@@ -4103,6 +4193,33 @@ class LlEmitter {
4103
4193
  B.line(`${t} = load ptr, ptr ${slot}`);
4104
4194
  return this.own({ name: t, type: e.type });
4105
4195
  }
4196
+ if (e.left.type.kind === "dyn") {
4197
+ // The checked-dynamic form: the runtime kind decides (UNDEF/
4198
+ // NULL take the default; a wrapped island value asks the
4199
+ // engine); the right runs lazily in its branch (already dyn).
4200
+ const l = this.emitExpr(e.left);
4201
+ this.moveTemp(l);
4202
+ this.declare(`declare zeroext i1 @scr_dyn_is_nullish(ptr)`);
4203
+ const slot = B.slot();
4204
+ B.entryAllocas.push(`${slot} = alloca ptr`);
4205
+ const isN = B.tmp();
4206
+ B.line(`${isN} = call zeroext i1 @scr_dyn_is_nullish(ptr ${l.name})`);
4207
+ const lu = B.newLabel("nuld.u");
4208
+ const lv = B.newLabel("nuld.v");
4209
+ const lj = B.newLabel("nuld.j");
4210
+ B.condBr(isN, lu, lv);
4211
+ B.startBlock(lu);
4212
+ this.releaseValue(l.name, e.left.type);
4213
+ this.emitBranchInto(slot, e.right);
4214
+ B.br(lj);
4215
+ B.startBlock(lv);
4216
+ B.line(`store ptr ${l.name}, ptr ${slot}`);
4217
+ B.br(lj);
4218
+ B.startBlock(lj);
4219
+ const t = B.tmp();
4220
+ B.line(`${t} = load ptr, ptr ${slot}`);
4221
+ return this.own({ name: t, type: e.type });
4222
+ }
4106
4223
  if (e.left.type.kind !== "union")
4107
4224
  throw new Error("llvm emitter bug: nullish left is not a union");
4108
4225
  const def = this.unionsById.get(e.left.type.unionId);
@@ -5272,14 +5389,18 @@ class LlEmitter {
5272
5389
  }
5273
5390
  if (e.name === "promise.reject") {
5274
5391
  // A fresh promise rejected through the exception cell: the
5275
- // %Error-rooted reason moves in as the cell's OBJ payload, and
5276
- // reject_pending moves the cell into the promise (consumed
5277
- // immediately — no pending check runs in between).
5392
+ // %Error-rooted reason moves in as the cell's OBJ payload (a
5393
+ // checked-dynamic reason rides the thrown-dyn REF representation
5394
+ // instead — identity flows to catch/unhandledRejection observers,
5395
+ // emitThrowValue's dyn arm), and reject_pending moves the cell
5396
+ // into the promise (consumed immediately — no pending check runs
5397
+ // in between).
5278
5398
  if (e.type.kind !== "promise")
5279
5399
  throw new Error("llvm emitter bug: promise.reject type");
5280
5400
  const reasonT = e.args[0].type;
5281
- if (reasonT.kind !== "object")
5401
+ if (reasonT.kind !== "object" && reasonT.kind !== "dyn") {
5282
5402
  throw new Error("llvm emitter bug: promise.reject reason");
5403
+ }
5283
5404
  this.declare(`declare ptr @scr_promise_new()`);
5284
5405
  this.declare(`declare void @scr_promise_reject_pending(ptr)`);
5285
5406
  const reason = this.emitExpr(e.args[0]);
@@ -5421,6 +5542,16 @@ class LlEmitter {
5421
5542
  B.line(`${t} = call ptr @${conv}(${valTy} ${v.name})`);
5422
5543
  return this.own({ name: t, type: e.type });
5423
5544
  }
5545
+ case "dynFromJsval": {
5546
+ // Island value → DOM: the by-reference wrap (scr_dyn_from_jsval
5547
+ // retains the cell in; engine scalars normalize to native DOM
5548
+ // kinds at wrap time). Operand borrowed, result +1, never throws.
5549
+ const v = this.emitExpr(e.value);
5550
+ this.declare(`declare ptr @scr_dyn_from_jsval(ptr)`);
5551
+ const t = B.tmp();
5552
+ B.line(`${t} = call ptr @scr_dyn_from_jsval(ptr ${v.name})`);
5553
+ return this.own({ name: t, type: e.type });
5554
+ }
5424
5555
  case "caughtToDyn": {
5425
5556
  // A catch binding flowing into an `unknown` slot: the snapshot's
5426
5557
  // runtime kind converts through the interned helper (+1 fresh
@@ -5452,6 +5583,40 @@ class LlEmitter {
5452
5583
  // boxed thunk builds its own typed copies. The callee's source
5453
5584
  // spelling rides along for Node's "<name> is not a function".
5454
5585
  const callee = this.emitExpr(e.callee);
5586
+ if (e.spreads !== undefined && e.spreads.length > 0) {
5587
+ // The RUNTIME-ARITY form (`f(...args)`): one fresh DOM array
5588
+ // collects the arguments left-to-right — plain args move in
5589
+ // (push takes ownership), spread args FLATTEN (push_spread
5590
+ // retains elements in and throws V8's spread-call TypeError for
5591
+ // non-iterable DOM kinds, checked per spread — JS's
5592
+ // ArgumentListEvaluation order) — then apply calls through the
5593
+ // array's elements (borrowed, exactly scr_dyn_call).
5594
+ this.declare(`declare ptr @scr_dyn_new_arr()`);
5595
+ this.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
5596
+ this.declare(`declare void @scr_dyn_arr_push_spread(ptr, ptr, ptr)`);
5597
+ this.declare(`declare ptr @scr_dyn_apply(ptr, ptr, ptr)`);
5598
+ const spreadAt = new Map(e.spreads.map((s) => [s.arg, s.what]));
5599
+ const pack = B.tmp();
5600
+ B.line(`${pack} = call ptr @scr_dyn_new_arr()`);
5601
+ this.own({ name: pack, type: DYN });
5602
+ e.args.forEach((a, i) => {
5603
+ const v = this.emitExpr(a);
5604
+ const spreadWhat = spreadAt.get(i);
5605
+ if (spreadWhat !== undefined) {
5606
+ B.line(`call void @scr_dyn_arr_push_spread(ptr ${pack}, ptr ${v.name}, ptr ${this.cstr(spreadWhat)})`);
5607
+ this.emitPendingCheck();
5608
+ }
5609
+ else {
5610
+ this.moveTemp(v);
5611
+ B.line(`call void @scr_dyn_arr_push(ptr ${pack}, ptr ${v.name})`);
5612
+ }
5613
+ });
5614
+ const t = B.tmp();
5615
+ B.line(`${t} = call ptr @scr_dyn_apply(ptr ${callee.name}, ptr ${pack}, ptr ${this.cstr(e.calleeName)})`);
5616
+ const out = this.own({ name: t, type: e.type });
5617
+ this.emitPendingCheck();
5618
+ return out;
5619
+ }
5455
5620
  const args = e.args.map((a) => this.emitExpr(a));
5456
5621
  let argsPtr = "null";
5457
5622
  if (args.length > 0) {
@@ -5568,7 +5733,8 @@ class LlEmitter {
5568
5733
  B.startBlock(lNotObj);
5569
5734
  const isArr = B.tmp();
5570
5735
  B.line(`${isArr} = icmp eq i32 ${kd}, ${DK.ARR}`);
5571
- B.condBr(isArr, lArr, lj);
5736
+ const lNotArr = B.newLabel("dhk.na");
5737
+ B.condBr(isArr, lArr, lNotArr);
5572
5738
  B.startBlock(lArr);
5573
5739
  if (e.key === "length") {
5574
5740
  B.line(`store i1 true, ptr ${slot}`);
@@ -5583,9 +5749,20 @@ class LlEmitter {
5583
5749
  B.line(`store i1 ${inR}, ptr ${slot}`);
5584
5750
  }
5585
5751
  B.br(lj);
5752
+ // An ISLAND-held receiver fences loudly (Node asks the real
5753
+ // engine object — `false` would be a silent wrong answer); the
5754
+ // helper answers false for every other kind, so this arm is a
5755
+ // plain unconditional call.
5756
+ B.startBlock(lNotArr);
5757
+ this.declare(`declare zeroext i1 @scr_dyn_isl_fence(ptr, ptr)`);
5758
+ const fenced = B.tmp();
5759
+ B.line(`${fenced} = call zeroext i1 @scr_dyn_isl_fence(ptr ${d.name}, ptr ${this.cstr("'in'")})`);
5760
+ B.line(`store i1 ${fenced}, ptr ${slot}`);
5761
+ B.br(lj);
5586
5762
  B.startBlock(lj);
5587
5763
  const raw = B.tmp();
5588
5764
  B.line(`${raw} = load i1, ptr ${slot}`);
5765
+ this.emitPendingCheck();
5589
5766
  if (!e.negated)
5590
5767
  return { name: raw, type: e.type };
5591
5768
  const neg = B.tmp();
@@ -5665,13 +5842,18 @@ class LlEmitter {
5665
5842
  }
5666
5843
  else if (e.test === "error") {
5667
5844
  // `u instanceof Error`: the DOM's error encoding — an object
5668
- // carrying the reserved "%error" marker key.
5845
+ // carrying the reserved "%error" marker key — or a real engine
5846
+ // Error held by reference (the isl helper answers false for
5847
+ // every non-JSVAL kind, so the call is unconditional).
5669
5848
  const kd = this.dynKind(d.name);
5670
5849
  const isObj = B.tmp();
5671
5850
  B.line(`${isObj} = icmp eq i32 ${kd}, ${DK.OBJ}`);
5672
5851
  const slot = B.slot();
5673
5852
  B.entryAllocas.push(`${slot} = alloca i1`);
5674
- B.line(`store i1 false, ptr ${slot}`);
5853
+ this.declare(`declare zeroext i1 @scr_dyn_isl_is_error(ptr)`);
5854
+ const isl = B.tmp();
5855
+ B.line(`${isl} = call zeroext i1 @scr_dyn_isl_is_error(ptr ${d.name})`);
5856
+ B.line(`store i1 ${isl}, ptr ${slot}`);
5675
5857
  const lObj = B.newLabel("dts.o");
5676
5858
  const lj = B.newLabel("dts.j");
5677
5859
  B.condBr(isObj, lObj, lj);
@@ -5705,13 +5887,33 @@ class LlEmitter {
5705
5887
  }
5706
5888
  return acc;
5707
5889
  };
5890
+ // ISLAND-held nodes route the tests that depend on the engine's
5891
+ // answer through the scr_dyn_isl_* helpers (false on every
5892
+ // other kind — the calls stay unconditional and branch-free).
5893
+ const orIsl = (acc, helper, arg) => {
5894
+ this.declare(`declare zeroext i1 @${helper}(ptr${arg !== undefined ? ", ptr" : ""})`);
5895
+ const c = B.tmp();
5896
+ B.line(`${c} = call zeroext i1 @${helper}(ptr ${d.name}${arg !== undefined ? `, ptr ${arg}` : ""})`);
5897
+ const o = B.tmp();
5898
+ B.line(`${o} = or i1 ${acc}, ${c}`);
5899
+ return o;
5900
+ };
5708
5901
  if (e.test === "nullish") {
5709
5902
  test = oneOf([DK.UNDEF, DK.NULL]);
5710
5903
  }
5711
5904
  else if (e.test === "object") {
5712
5905
  // `typeof v === "object"`: objects, arrays, bytes, native
5713
- // handles, promises, AND null.
5714
- test = oneOf([DK.OBJ, DK.ARR, DK.BYTES, DK.HANDLE, DK.PROMISE, DK.NULL]);
5906
+ // handles, promises, AND null — engine-held objects by the
5907
+ // engine's own typeof.
5908
+ test = orIsl(oneOf([DK.OBJ, DK.ARR, DK.BYTES, DK.HANDLE, DK.PROMISE, DK.NULL]), "scr_dyn_isl_typeof_is", this.cstr("object"));
5909
+ }
5910
+ else if (e.test === "array") {
5911
+ // Array.isArray: the DOM's array kind, or the engine's own
5912
+ // answer for an engine-held value.
5913
+ test = orIsl(oneOf([DK.ARR]), "scr_dyn_isl_is_array");
5914
+ }
5915
+ else if (e.test === "function") {
5916
+ test = orIsl(oneOf([DK.FUNC]), "scr_dyn_isl_typeof_is", this.cstr("function"));
5715
5917
  }
5716
5918
  else {
5717
5919
  const kindOf = {
@@ -5721,8 +5923,6 @@ class LlEmitter {
5721
5923
  undefined: DK.UNDEF,
5722
5924
  null: DK.NULL,
5723
5925
  bytes: DK.BYTES,
5724
- array: DK.ARR,
5725
- function: DK.FUNC,
5726
5926
  };
5727
5927
  test = oneOf([kindOf[e.test]]);
5728
5928
  }
@@ -5834,6 +6034,13 @@ class LlEmitter {
5834
6034
  return simple("scr_jsval_from_bool", "i1", false);
5835
6035
  case "string":
5836
6036
  return simple("scr_jsval_from_str", "ptr", false);
6037
+ case "dyn":
6038
+ // A CHECKED-DYNAMIC (DOM) value entering the island: deep copy,
6039
+ // data kinds only — boxed functions/handles/promises throw the
6040
+ // catchable TypeError in the runtime, and a wrapped island value
6041
+ // unwraps to the SAME engine value (the identity round trip).
6042
+ // The C emitter's rule, mirrored.
6043
+ return simple("scr_jsval_from_dyn", "ptr", true);
5837
6044
  case "bytes":
5838
6045
  // A typed array crossing IN: a COPY (the boundary's copy stance).
5839
6046
  return simple("scr_jsval_from_bytes", "ptr", true);
@@ -6037,6 +6244,18 @@ class LlEmitter {
6037
6244
  return t;
6038
6245
  });
6039
6246
  }
6247
+ case "callSpread": {
6248
+ // Spread application (`f(...pre, ...s)`): the prelude helper's
6249
+ // real spread syntax — iterator protocols are the engine's own,
6250
+ // the guards front-run V8's spread-call TypeError texts (the name
6251
+ // literal is the spread expression's spelling).
6252
+ this.declare(`declare ptr @scr_jsval_call_spread(ptr, ptr, ptr, ptr)`);
6253
+ return fallible(() => {
6254
+ const t = B.tmp();
6255
+ B.line(`${t} = call ptr @scr_jsval_call_spread(ptr ${a(0)}, ptr ${a(1)}, ptr ${a(2)}, ptr ${nameSym()})`);
6256
+ return t;
6257
+ });
6258
+ }
6040
6259
  case "construct": {
6041
6260
  const pack = argPack(args.slice(1).map((x) => x.name));
6042
6261
  this.declare(`declare ptr @scr_jsval_construct(ptr, i32, ptr)`);
@@ -8131,6 +8350,18 @@ class LlEmitter {
8131
8350
  // DataView getters: an omitted littleEndian is big-endian (the JS
8132
8351
  // default). Throw Node's constant RangeError on a bad offset.
8133
8352
  return call("scr_dataview_get", "double (ptr, double, i32, i1 zeroext)", `ptr ${r.name}, double ${args[0].name}, i32 ${DV_GET_KIND[method]}, i1 ${args[1]?.name ?? "false"}`, false, true);
8353
+ case "dvSetUint8":
8354
+ case "dvSetInt8":
8355
+ case "dvSetUint16":
8356
+ case "dvSetInt16":
8357
+ case "dvSetUint32":
8358
+ case "dvSetInt32":
8359
+ case "dvSetFloat32":
8360
+ case "dvSetFloat64":
8361
+ // DataView setters: [offset, value, littleEndian?] — void; throw
8362
+ // the getters' constant RangeError on a bad offset.
8363
+ return call("scr_dataview_set", "void (ptr, double, double, i32, i1 zeroext)", `ptr ${r.name}, double ${args[0].name}, double ${args[1].name}, ` +
8364
+ `i32 ${DV_SET_KIND[method]}, i1 ${args[2]?.name ?? "false"}`, false, true);
8134
8365
  default: {
8135
8366
  const _exhaustive = method;
8136
8367
  void _exhaustive;
@@ -8511,6 +8742,90 @@ class LlEmitter {
8511
8742
  if (USES_TIMERS_LIB_FNS.has(e.fn))
8512
8743
  this.usesTimers = true;
8513
8744
  // The handful with non-generic shapes first.
8745
+ if (e.fn === "error.argTypeThrow") {
8746
+ // Always throws with the runtime-rendered Received tail (the
8747
+ // error.nodeThrow dummy pattern). Borrows all three.
8748
+ const an = this.emitExpr(e.args[0]);
8749
+ const ex = this.emitExpr(e.args[1]);
8750
+ const got = this.emitExpr(e.args[2]);
8751
+ this.declare(`declare void @scr_throw_arg_type(ptr, ptr, ptr)`);
8752
+ B.line(`call void @scr_throw_arg_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`);
8753
+ const ty = this.llType(e.type);
8754
+ if (ty === "void") {
8755
+ this.emitPendingCheck();
8756
+ return { name: "", type: e.type };
8757
+ }
8758
+ const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
8759
+ const out = this.own({ name: dummy, type: e.type });
8760
+ this.emitPendingCheck();
8761
+ return out;
8762
+ }
8763
+ if (e.fn === "error.propTypeThrow") {
8764
+ // The property flavor of argTypeThrow — same always-throw shape.
8765
+ const an = this.emitExpr(e.args[0]);
8766
+ const ex = this.emitExpr(e.args[1]);
8767
+ const got = this.emitExpr(e.args[2]);
8768
+ this.declare(`declare void @scr_throw_prop_type(ptr, ptr, ptr)`);
8769
+ B.line(`call void @scr_throw_prop_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`);
8770
+ const ty = this.llType(e.type);
8771
+ if (ty === "void") {
8772
+ this.emitPendingCheck();
8773
+ return { name: "", type: e.type };
8774
+ }
8775
+ const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
8776
+ const out = this.own({ name: dummy, type: e.type });
8777
+ this.emitPendingCheck();
8778
+ return out;
8779
+ }
8780
+ {
8781
+ // The fs validation-ladder Chk forms that ALWAYS throw (a
8782
+ // validation error or the trailing compiler-rendered fence): every
8783
+ // argument is a ptr (dyns + the fence string), and the typed dummy
8784
+ // is abandoned by the pending check's unwind.
8785
+ const FS_CHK_THROW_SYMS = {
8786
+ "fs.mkdtempChk": "scr_fs_mkdtemp_chk",
8787
+ "fs.readFileChk": "scr_fs_read_file_chk",
8788
+ "fs.opendirChk": "scr_fs_opendir_chk",
8789
+ "fs.watchFileChk": "scr_fs_watch_file_chk",
8790
+ "fs.lchmodChk": "scr_fs_lchmod_chk",
8791
+ "fs.readChk": "scr_fs_read_chk",
8792
+ "fs.streamOptsChk": "scr_fs_stream_opts_chk",
8793
+ "net.connectOptsChk": "scr_net_connect_opts_chk",
8794
+ };
8795
+ const sym = FS_CHK_THROW_SYMS[e.fn];
8796
+ if (sym !== undefined) {
8797
+ const args = e.args.map((a) => this.emitExpr(a));
8798
+ this.declare(`declare void @${sym}(${args.map(() => "ptr").join(", ")})`);
8799
+ B.line(`call void @${sym}(${args.map((a) => `ptr ${a.name}`).join(", ")})`);
8800
+ const ty = this.llType(e.type);
8801
+ if (ty === "void") {
8802
+ this.emitPendingCheck();
8803
+ return { name: "", type: e.type };
8804
+ }
8805
+ const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
8806
+ const out = this.own({ name: dummy, type: e.type });
8807
+ this.emitPendingCheck();
8808
+ return out;
8809
+ }
8810
+ }
8811
+ if (e.fn === "error.nodeThrow") {
8812
+ // The compiler-resolved Node-parity throw (always throws — the
8813
+ // typed dummy is abandoned by the pending check's unwind).
8814
+ const kind = this.emitExpr(e.args[0]);
8815
+ const code = this.emitExpr(e.args[1]);
8816
+ const msg = this.emitExpr(e.args[2]);
8817
+ this.declare(`declare void @scr_throw_node_coded(double, ptr, ptr)`);
8818
+ B.line(`call void @scr_throw_node_coded(double ${kind.name}, ptr ${code.name}, ptr ${msg.name})`);
8819
+ const ty = this.llType(e.type);
8820
+ if (ty === "void") {
8821
+ this.emitPendingCheck();
8822
+ return { name: "", type: e.type };
8823
+ }
8824
+ const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
8825
+ const out = this.own({ name: dummy, type: e.type });
8826
+ this.emitPendingCheck();
8827
+ return out;
8828
+ }
8514
8829
  if (e.fn === "global.undefRead") {
8515
8830
  // A declare-d const nothing defines: Node's catchable
8516
8831
  // ReferenceError at the access (always throws — the typed dummy is
@@ -9742,6 +10057,28 @@ class LlEmitter {
9742
10057
  this.emitPendingCheck();
9743
10058
  return out;
9744
10059
  }
10060
+ if (e.fn === "sp.pipeline") {
10061
+ // pipeline(count, s1..sn) settling a void promise: the stream list
10062
+ // rides the callback form's stack array, no callback slot.
10063
+ const countArg = e.args[0];
10064
+ if (countArg.kind !== "numLit")
10065
+ throw new Error(`llvm emitter bug: ${e.fn} count not a literal`);
10066
+ const n = countArg.value;
10067
+ const args = e.args.map((a) => this.emitExpr(a));
10068
+ const arr = B.slot();
10069
+ B.entryAllocas.push(`${arr} = alloca [${n} x ptr]`);
10070
+ for (let i = 0; i < n; i++) {
10071
+ const p = B.tmp();
10072
+ B.line(`${p} = getelementptr inbounds [${n} x ptr], ptr ${arr}, i64 0, i64 ${i}`);
10073
+ B.line(`store ptr ${args[1 + i].name}, ptr ${p}`);
10074
+ }
10075
+ const t = B.tmp();
10076
+ this.declare(`declare ptr @scr_sp_pipeline(double, ptr)`);
10077
+ B.line(`${t} = call ptr @scr_sp_pipeline(double ${f64Lit(n)}, ptr ${arr})`);
10078
+ const out = this.own({ name: t, type: e.type });
10079
+ this.emitPendingCheck();
10080
+ return out;
10081
+ }
9745
10082
  if (e.fn === "stream.pipeline" || e.fn === "stream.pipelineDyn") {
9746
10083
  // pipeline(count, s1..sn, cb): the destination answers +1. The
9747
10084
  // stream list rides a stack array (the C compound literal).