@scriptc/compiler 0.0.7 → 0.0.9

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 (94) 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 +155 -6
  5. package/dist/backend/emission/emit-exprs.js.map +1 -1
  6. package/dist/backend/emission/emit-island.js +9 -1
  7. package/dist/backend/emission/emit-island.js.map +1 -1
  8. package/dist/backend/emission/emit-stmts.js +3 -1
  9. package/dist/backend/emission/emit-stmts.js.map +1 -1
  10. package/dist/backend/emission/emit-types.d.ts +3 -0
  11. package/dist/backend/emission/emit-types.js +12 -0
  12. package/dist/backend/emission/emit-types.js.map +1 -1
  13. package/dist/backend/emission/emit-walkers.d.ts +7 -5
  14. package/dist/backend/emission/emit-walkers.js +45 -18
  15. package/dist/backend/emission/emit-walkers.js.map +1 -1
  16. package/dist/backend/emission/emitter.d.ts +1 -1
  17. package/dist/backend/emission/emitter.js +35 -10
  18. package/dist/backend/emission/emitter.js.map +1 -1
  19. package/dist/backend/emission/may-throw.js +4 -3
  20. package/dist/backend/emission/may-throw.js.map +1 -1
  21. package/dist/backend/llvm/emitter.js +300 -18
  22. package/dist/backend/llvm/emitter.js.map +1 -1
  23. package/dist/diagnostics/diagnostic.d.ts +98 -11
  24. package/dist/diagnostics/diagnostic.js +185 -25
  25. package/dist/diagnostics/diagnostic.js.map +1 -1
  26. package/dist/frontend/lib-contract.d.ts +24 -6
  27. package/dist/frontend/lib-contract.js +125 -44
  28. package/dist/frontend/lib-contract.js.map +1 -1
  29. package/dist/frontend/lowering/lower-assert.js +16 -0
  30. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  31. package/dist/frontend/lowering/lower-builtins.d.ts +11 -0
  32. package/dist/frontend/lowering/lower-builtins.js +127 -4
  33. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  34. package/dist/frontend/lowering/lower-calls.d.ts +77 -18
  35. package/dist/frontend/lowering/lower-calls.js +978 -44
  36. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  37. package/dist/frontend/lowering/lower-classes.d.ts +14 -1
  38. package/dist/frontend/lowering/lower-classes.js +112 -16
  39. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  40. package/dist/frontend/lowering/lower-containers.d.ts +4 -2
  41. package/dist/frontend/lowering/lower-containers.js +236 -44
  42. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  43. package/dist/frontend/lowering/lower-emitter.js +55 -1
  44. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  45. package/dist/frontend/lowering/lower-exprs.d.ts +26 -3
  46. package/dist/frontend/lowering/lower-exprs.js +507 -47
  47. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  48. package/dist/frontend/lowering/lower-island.d.ts +7 -0
  49. package/dist/frontend/lowering/lower-island.js +71 -10
  50. package/dist/frontend/lowering/lower-island.js.map +1 -1
  51. package/dist/frontend/lowering/lower-mixins.d.ts +1 -1
  52. package/dist/frontend/lowering/lower-mixins.js +1 -1
  53. package/dist/frontend/lowering/lower-modules.js +95 -4
  54. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  55. package/dist/frontend/lowering/lower-stmts.js +866 -56
  56. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  57. package/dist/frontend/lowering/lower-stream.js +28 -2
  58. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  59. package/dist/frontend/lowering/lowerer.d.ts +85 -17
  60. package/dist/frontend/lowering/lowerer.js +459 -37
  61. package/dist/frontend/lowering/lowerer.js.map +1 -1
  62. package/dist/frontend/lowering/surfaces.js +20 -3
  63. package/dist/frontend/lowering/surfaces.js.map +1 -1
  64. package/dist/frontend/npm-static-rewrite.d.ts +7 -2
  65. package/dist/frontend/npm-static-rewrite.js +566 -138
  66. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  67. package/dist/frontend/npm-static.js +11 -2
  68. package/dist/frontend/npm-static.js.map +1 -1
  69. package/dist/frontend/program.js +5 -5
  70. package/dist/frontend/program.js.map +1 -1
  71. package/dist/frontend/shared.d.ts +6 -1
  72. package/dist/frontend/shared.js +46 -1
  73. package/dist/frontend/shared.js.map +1 -1
  74. package/dist/frontend/types.d.ts +28 -1
  75. package/dist/frontend/types.js +304 -10
  76. package/dist/frontend/types.js.map +1 -1
  77. package/dist/index.d.ts +2 -1
  78. package/dist/index.js +181 -26
  79. package/dist/index.js.map +1 -1
  80. package/dist/ir/nodes.d.ts +159 -9
  81. package/dist/ir/nodes.js +76 -6
  82. package/dist/ir/nodes.js.map +1 -1
  83. package/dist/ir/validate.js +93 -14
  84. package/dist/ir/validate.js.map +1 -1
  85. package/dist/library/profile.d.ts +17 -3
  86. package/dist/library/profile.js +38 -9
  87. package/dist/library/profile.js.map +1 -1
  88. package/dist/library/sidecar.js +162 -43
  89. package/dist/library/sidecar.js.map +1 -1
  90. package/dist/library/trap-teaching.d.ts +11 -0
  91. package/dist/library/trap-teaching.js +49 -0
  92. package/dist/library/trap-teaching.js.map +1 -0
  93. package/package.json +2 -2
  94. package/surface-manifest.json +46 -4
@@ -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,14 @@ 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",
190
200
  "fs.readFileSyncBuf": "scr_fs_read_file_bytes",
191
201
  "fs.readFileSyncBytes": "scr_fs_read_file_bytes",
192
202
  "fs.writeFileSyncBytes": "scr_fs_write_file_bytes",
@@ -376,6 +386,8 @@ const LIB_FN_SYMS = {
376
386
  "emitter.names": "scr_emitter_event_names",
377
387
  "emitter.listeners": "scr_emitter_listeners",
378
388
  "emitter.setMax": "scr_emitter_set_max",
389
+ "emitter.setMaxChk": "scr_emitter_set_max_chk",
390
+ "emitter.setDefaultMaxChk": "scr_emitter_set_default_max_chk",
379
391
  "emitter.getMax": "scr_emitter_get_max",
380
392
  "emitter.setDefaultMax": "scr_emitter_set_default_max",
381
393
  "emitter.getDefaultMax": "scr_emitter_get_default_max",
@@ -483,6 +495,9 @@ const LIB_FN_SYMS = {
483
495
  // generic shapes; the throwing members ride the pending check.
484
496
  "tp.setTimeout": "scr_tp_set_timeout",
485
497
  "tp.setImmediate": "scr_tp_set_immediate",
498
+ // stream/promises' finished form (+1 promise; may-throw pending check;
499
+ // the pipeline form needs the stream-array compound and emits below).
500
+ "sp.finished": "scr_sp_finished",
486
501
  "dc.channel": "scr_dc_channel",
487
502
  "dc.subscribe": "scr_dc_subscribe",
488
503
  "dc.unsubscribe": "scr_dc_unsubscribe",
@@ -542,6 +557,8 @@ const LIB_FN_SYMS = {
542
557
  // (option/DataClone/cycle errors), and new RegExp's eager compile
543
558
  // (catchable SyntaxError) — all may-throw generics over the DOM.
544
559
  "dyn.objKeys": "scr_dyn_obj_keys",
560
+ "dyn.hasOwn": "scr_dyn_has_own",
561
+ "dyn.assign": "scr_dyn_assign",
545
562
  "dyn.objValues": "scr_dyn_obj_values",
546
563
  "dyn.objEntries": "scr_dyn_obj_entries",
547
564
  "dyn.structuredClone": "scr_structured_clone",
@@ -584,6 +601,7 @@ const USES_TIMERS_LIB_FNS = new Set([
584
601
  "stream.setTransform", "stream.setFlush",
585
602
  "process.activeResources",
586
603
  "stream.finished", "stream.finishedDyn", "stream.pipeline", "stream.pipelineDyn",
604
+ "sp.finished", "sp.pipeline",
587
605
  "net.listen", "net.listenCb", "net.listenOpts", "net.listenOptsCb",
588
606
  "net.connect", "net.connectCb", "net.connectLookup",
589
607
  "http.createServer", "http.createServerEmpty",
@@ -638,6 +656,18 @@ const DV_GET_KIND = {
638
656
  dvGetBigUint64Number: 8,
639
657
  dvGetBigInt64Number: 9,
640
658
  };
659
+ /** ScrDataViewGet per dvSet* method (the setters reuse the getter kinds;
660
+ * no BIG setters exist). */
661
+ const DV_SET_KIND = {
662
+ dvSetUint8: 0,
663
+ dvSetInt8: 1,
664
+ dvSetUint16: 2,
665
+ dvSetInt16: 3,
666
+ dvSetUint32: 4,
667
+ dvSetInt32: 5,
668
+ dvSetFloat32: 6,
669
+ dvSetFloat64: 7,
670
+ };
641
671
  class LlEmitter {
642
672
  mod;
643
673
  /** Interned string literals: UTF-8 text → { symbol, byte length } —
@@ -1013,7 +1043,7 @@ class LlEmitter {
1013
1043
  // LIBRARY mode: the runtime entry points the generated library
1014
1044
  // symbols delegate to — declared before the extern block flushes.
1015
1045
  if (this.mod.lib !== undefined) {
1016
- this.declare(`declare void @scr_library_entry(i1 zeroext)`);
1046
+ this.declare(`declare void @scr_library_entry(i1 zeroext, ptr)`);
1017
1047
  this.declare(`declare void @scr_library_reset()`);
1018
1048
  this.declare(`declare void @scr_library_check_exc()`);
1019
1049
  this.declare(`declare void @scr_library_set_sink(ptr, ptr)`);
@@ -1023,7 +1053,7 @@ class LlEmitter {
1023
1053
  this.declare(`declare ptr @scr_library_str_in(ptr, i64)`);
1024
1054
  }
1025
1055
  if (this.mod.lib.exports.some((e) => e.params.includes("bytes"))) {
1026
- this.declare(`declare ptr @scr_library_bytes_in(ptr, i64)`);
1056
+ this.declare(`declare ptr @scr_library_bytes_in(ptr, i64, ptr)`);
1027
1057
  }
1028
1058
  if (this.mod.lib.exports.some((e) => e.returns === "string")) {
1029
1059
  this.declare(`declare void @scr_library_str_out(ptr, ptr, ptr)`);
@@ -1257,6 +1287,45 @@ class LlEmitter {
1257
1287
  const lib = this.mod.lib;
1258
1288
  const autoReset = lib.resultResetSymbol === null;
1259
1289
  const out = [``, `; ── library-mode entries (profile: ${lib.profileName}) ──`, ``];
1290
+ // Every entry's prologue records its external symbol in the funnel's
1291
+ // current-entry slot (structured trap-teaching field 2); the symbols
1292
+ // live as internal constants, one per entry.
1293
+ const symConst = (sym) => `@sc_lib_sym_${sym}`;
1294
+ const emitSymConst = (sym) => {
1295
+ out.push(`${symConst(sym)} = internal constant [${Buffer.byteLength(sym, "utf8") + 1} x i8] c"${llStrBytes(sym)}"`);
1296
+ };
1297
+ emitSymConst(lib.initSymbol);
1298
+ if (lib.resultResetSymbol !== null)
1299
+ emitSymConst(lib.resultResetSymbol);
1300
+ if (lib.collectSymbol !== null)
1301
+ emitSymConst(lib.collectSymbol);
1302
+ for (const e of lib.exports)
1303
+ emitSymConst(e.symbol);
1304
+ out.push(``);
1305
+ // The runtime detected-trap overlay table (scr_runtime.h declares it,
1306
+ // the library trap funnel consults it): flat code/teaching/remediation
1307
+ // triples, one per runtime trap code (SC4013–SC4019) the profile
1308
+ // declares text for — the same data the C emission defines, so the
1309
+ // funnel-assembled sink message is emission-invariant by construction.
1310
+ // The empty table still defines the symbols the funnel links against.
1311
+ const ovlCells = [];
1312
+ lib.trapOverlays.forEach((o, i) => {
1313
+ const cell = (name, text) => {
1314
+ if (text === undefined) {
1315
+ ovlCells.push("ptr null");
1316
+ return;
1317
+ }
1318
+ const sym = `@sc_lib_ovl_${i}_${name}`;
1319
+ out.push(`${sym} = internal constant [${Buffer.byteLength(text, "utf8") + 1} x i8] c"${llStrBytes(text)}"`);
1320
+ ovlCells.push(`ptr ${sym}`);
1321
+ };
1322
+ cell("code", o.code);
1323
+ cell("teach", o.teaching);
1324
+ cell("rem", o.remediation);
1325
+ });
1326
+ out.push(ovlCells.length === 0
1327
+ ? `@scr_library_trap_overlays = constant [1 x ptr] zeroinitializer`
1328
+ : `@scr_library_trap_overlays = constant [${ovlCells.length} x ptr] [${ovlCells.join(", ")}]`, `@scr_library_trap_overlays_len = constant i64 ${lib.trapOverlays.length}`, ``);
1260
1329
  // The init entry: full deterministic reset-and-reevaluate. Program
1261
1330
  // globals release and zero first (run-once guards included), then the
1262
1331
  // runtime session reset, the error-vt interval stamps verbatim from
@@ -1266,7 +1335,7 @@ class LlEmitter {
1266
1335
  const zero = ty === "double" ? f64Lit(0) : ty === "ptr" ? "null" : "false";
1267
1336
  return ` store ${ty} ${zero}, ptr @${mangleGlobal(g.id)} ; ${g.name}`;
1268
1337
  });
1269
- 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`, `}`, ``);
1338
+ 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`, `}`, ``);
1270
1339
  if (lib.identity !== undefined) {
1271
1340
  // Profile-declared identity getters (the ask-2 sidecar's boot-time
1272
1341
  // pairing fence): pure data returns with NO entry prologue — exempt
@@ -1277,15 +1346,23 @@ class LlEmitter {
1277
1346
  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}`, `}`, ``);
1278
1347
  }
1279
1348
  if (lib.resultResetSymbol !== null) {
1280
- 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`, `}`, ``);
1349
+ 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`, `}`, ``);
1281
1350
  }
1282
1351
  if (lib.collectSymbol !== null) {
1283
- 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`, `}`, ``);
1352
+ 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`, `}`, ``);
1284
1353
  }
1285
1354
  for (const e of lib.exports) {
1286
1355
  const params = [];
1287
- const body = [` call void @scr_library_entry(i1 zeroext ${autoReset ? "true" : "false"})`];
1356
+ const body = [` call void @scr_library_entry(i1 zeroext ${autoReset ? "true" : "false"}, ptr ${symConst(e.symbol)})`];
1288
1357
  const args = [];
1358
+ if (e.inboundBytesTrap !== undefined) {
1359
+ // The bytes-in helper's trap message: the compiler-assembled
1360
+ // structured trap-teaching form (0x01 text 0x1F SC4012 0x1F symbol
1361
+ // [0x1F remediation]) — the same bytes the C emission passes, so
1362
+ // the sink message is emission-invariant by construction.
1363
+ const trapBytes = Buffer.byteLength(e.inboundBytesTrap, "utf8");
1364
+ out.push(`@sc_lib_bytes_trap_${e.symbol} = internal constant [${trapBytes + 1} x i8] c"${llStrBytes(e.inboundBytesTrap)}"`, ``);
1365
+ }
1289
1366
  e.params.forEach((cls, i) => {
1290
1367
  switch (cls) {
1291
1368
  case "f64":
@@ -1319,7 +1396,7 @@ class LlEmitter {
1319
1396
  break;
1320
1397
  case "bytes":
1321
1398
  params.push(`ptr %a${i}_ptr`, `i64 %a${i}_len`);
1322
- body.push(` %c${i} = call ptr @scr_library_bytes_in(ptr %a${i}_ptr, i64 %a${i}_len)`);
1399
+ body.push(` %c${i} = call ptr @scr_library_bytes_in(ptr %a${i}_ptr, i64 %a${i}_len, ptr @sc_lib_bytes_trap_${e.symbol})`);
1323
1400
  args.push(`ptr %c${i}`);
1324
1401
  break;
1325
1402
  }
@@ -2558,9 +2635,13 @@ class LlEmitter {
2558
2635
  if (isRefCounted(v.type))
2559
2636
  this.moveTemp(v);
2560
2637
  const shape = this.recordShape(s.shapeId);
2561
- const iv = shape.indexValue;
2638
+ // Signature-free shapes dispatch over their (one-typed) declared
2639
+ // fields and TRAP on a miss (scr_record_key_miss — JS would add
2640
+ // the property, which a monomorphic struct cannot); overflow
2641
+ // shapes keep the map insert tail.
2642
+ const iv = shape.indexValue ?? shape.fields[0]?.type;
2562
2643
  if (!iv)
2563
- throw new Error(`llvm emitter bug: keyed write on non-overflow shape ${s.shapeId}`);
2644
+ throw new Error(`llvm emitter bug: keyed write on field-free non-overflow shape ${s.shapeId}`);
2564
2645
  const vAcc = iv.kind === "f64" ? "f64" : iv.kind === "bool" ? "bool" : "ref";
2565
2646
  if (s.overflowOnly === true) {
2566
2647
  // A LITERAL key naming no declared field: a plain overflow
@@ -2571,7 +2652,7 @@ class LlEmitter {
2571
2652
  }
2572
2653
  const join = B.newLabel("rks.j");
2573
2654
  this.declare(`declare zeroext i1 @scr_str_eq(ptr, ptr)`);
2574
- if (iv.kind === "dyn") {
2655
+ if (iv.kind === "dyn" && shape.indexValue) {
2575
2656
  // A dyn-valued shape (the C recordKeySetHelper's dyn arm,
2576
2657
  // inline): declared keys VALIDATE the dyn value against the
2577
2658
  // field's type first (dynCheck — a mismatched write throws the
@@ -2656,6 +2737,19 @@ class LlEmitter {
2656
2737
  B.br(join);
2657
2738
  B.startBlock(ln);
2658
2739
  }
2740
+ if (!shape.indexValue) {
2741
+ // The MISS on a fixed shape: release the moved-in value, throw
2742
+ // the catchable TypeError naming the key (scr_record_key_miss —
2743
+ // JS would add the property, the documented divergence).
2744
+ if (isRefCounted(iv))
2745
+ this.releaseValue(v.name, iv);
2746
+ this.declare(`declare void @scr_record_key_miss(ptr)`);
2747
+ B.line(`call void @scr_record_key_miss(ptr ${key.name})`);
2748
+ B.br(join);
2749
+ B.startBlock(join);
2750
+ this.emitPendingCheck();
2751
+ break;
2752
+ }
2659
2753
  const ovf = this.recordOvfPtr(obj.name, s.shapeId);
2660
2754
  this.mapSet(ovf, "str", vAcc, key.name, v.name);
2661
2755
  B.br(join);
@@ -3972,7 +4066,15 @@ class LlEmitter {
3972
4066
  const t = B.tmp();
3973
4067
  B.line(`${a} = call double @scr_union_get_f64(ptr ${l.name})`);
3974
4068
  B.line(`${b} = call double @scr_union_get_f64(ptr ${r.name})`);
3975
- B.line(`${t} = fcmp oeq double ${a}, ${b}`);
4069
+ if (e.sameValue) {
4070
+ // Object.is's f64 compare: NaN equals NaN, +0 differs
4071
+ // from -0 — the runtime SameValue.
4072
+ this.declare(`declare zeroext i1 @scr_num_same_value(double, double)`);
4073
+ B.line(`${t} = call zeroext i1 @scr_num_same_value(double ${a}, double ${b})`);
4074
+ }
4075
+ else {
4076
+ B.line(`${t} = fcmp oeq double ${a}, ${b}`);
4077
+ }
3976
4078
  B.line(`store i1 ${t}, ptr ${slot}`);
3977
4079
  break;
3978
4080
  }
@@ -4051,6 +4153,31 @@ class LlEmitter {
4051
4153
  // TAG against its unit arms as the test. Pass-through shape: the
4052
4154
  // result IS the left box. Narrowed shape: the single non-unit
4053
4155
  // arm's payload extracts (+1 for ref kinds) and the box releases.
4156
+ if (e.left.type.kind === "jsval") {
4157
+ // The island form: engine nullish test, lazy right (jsval).
4158
+ const l = this.emitExpr(e.left);
4159
+ this.moveTemp(l);
4160
+ this.declare(`declare zeroext i1 @scr_jsval_is_nullish(ptr)`);
4161
+ const slot = B.slot();
4162
+ B.entryAllocas.push(`${slot} = alloca ptr`);
4163
+ const isN = B.tmp();
4164
+ B.line(`${isN} = call zeroext i1 @scr_jsval_is_nullish(ptr ${l.name})`);
4165
+ const lu = B.newLabel("nulj.u");
4166
+ const lv = B.newLabel("nulj.v");
4167
+ const lj = B.newLabel("nulj.j");
4168
+ B.condBr(isN, lu, lv);
4169
+ B.startBlock(lu);
4170
+ this.releaseValue(l.name, e.left.type);
4171
+ this.emitBranchInto(slot, e.right);
4172
+ B.br(lj);
4173
+ B.startBlock(lv);
4174
+ B.line(`store ptr ${l.name}, ptr ${slot}`);
4175
+ B.br(lj);
4176
+ B.startBlock(lj);
4177
+ const t = B.tmp();
4178
+ B.line(`${t} = load ptr, ptr ${slot}`);
4179
+ return this.own({ name: t, type: e.type });
4180
+ }
4054
4181
  if (e.left.type.kind !== "union")
4055
4182
  throw new Error("llvm emitter bug: nullish left is not a union");
4056
4183
  const def = this.unionsById.get(e.left.type.unionId);
@@ -5220,14 +5347,18 @@ class LlEmitter {
5220
5347
  }
5221
5348
  if (e.name === "promise.reject") {
5222
5349
  // A fresh promise rejected through the exception cell: the
5223
- // %Error-rooted reason moves in as the cell's OBJ payload, and
5224
- // reject_pending moves the cell into the promise (consumed
5225
- // immediately — no pending check runs in between).
5350
+ // %Error-rooted reason moves in as the cell's OBJ payload (a
5351
+ // checked-dynamic reason rides the thrown-dyn REF representation
5352
+ // instead — identity flows to catch/unhandledRejection observers,
5353
+ // emitThrowValue's dyn arm), and reject_pending moves the cell
5354
+ // into the promise (consumed immediately — no pending check runs
5355
+ // in between).
5226
5356
  if (e.type.kind !== "promise")
5227
5357
  throw new Error("llvm emitter bug: promise.reject type");
5228
5358
  const reasonT = e.args[0].type;
5229
- if (reasonT.kind !== "object")
5359
+ if (reasonT.kind !== "object" && reasonT.kind !== "dyn") {
5230
5360
  throw new Error("llvm emitter bug: promise.reject reason");
5361
+ }
5231
5362
  this.declare(`declare ptr @scr_promise_new()`);
5232
5363
  this.declare(`declare void @scr_promise_reject_pending(ptr)`);
5233
5364
  const reason = this.emitExpr(e.args[0]);
@@ -5400,6 +5531,40 @@ class LlEmitter {
5400
5531
  // boxed thunk builds its own typed copies. The callee's source
5401
5532
  // spelling rides along for Node's "<name> is not a function".
5402
5533
  const callee = this.emitExpr(e.callee);
5534
+ if (e.spreads !== undefined && e.spreads.length > 0) {
5535
+ // The RUNTIME-ARITY form (`f(...args)`): one fresh DOM array
5536
+ // collects the arguments left-to-right — plain args move in
5537
+ // (push takes ownership), spread args FLATTEN (push_spread
5538
+ // retains elements in and throws V8's spread-call TypeError for
5539
+ // non-iterable DOM kinds, checked per spread — JS's
5540
+ // ArgumentListEvaluation order) — then apply calls through the
5541
+ // array's elements (borrowed, exactly scr_dyn_call).
5542
+ this.declare(`declare ptr @scr_dyn_new_arr()`);
5543
+ this.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
5544
+ this.declare(`declare void @scr_dyn_arr_push_spread(ptr, ptr, ptr)`);
5545
+ this.declare(`declare ptr @scr_dyn_apply(ptr, ptr, ptr)`);
5546
+ const spreadAt = new Map(e.spreads.map((s) => [s.arg, s.what]));
5547
+ const pack = B.tmp();
5548
+ B.line(`${pack} = call ptr @scr_dyn_new_arr()`);
5549
+ this.own({ name: pack, type: DYN });
5550
+ e.args.forEach((a, i) => {
5551
+ const v = this.emitExpr(a);
5552
+ const spreadWhat = spreadAt.get(i);
5553
+ if (spreadWhat !== undefined) {
5554
+ B.line(`call void @scr_dyn_arr_push_spread(ptr ${pack}, ptr ${v.name}, ptr ${this.cstr(spreadWhat)})`);
5555
+ this.emitPendingCheck();
5556
+ }
5557
+ else {
5558
+ this.moveTemp(v);
5559
+ B.line(`call void @scr_dyn_arr_push(ptr ${pack}, ptr ${v.name})`);
5560
+ }
5561
+ });
5562
+ const t = B.tmp();
5563
+ B.line(`${t} = call ptr @scr_dyn_apply(ptr ${callee.name}, ptr ${pack}, ptr ${this.cstr(e.calleeName)})`);
5564
+ const out = this.own({ name: t, type: e.type });
5565
+ this.emitPendingCheck();
5566
+ return out;
5567
+ }
5403
5568
  const args = e.args.map((a) => this.emitExpr(a));
5404
5569
  let argsPtr = "null";
5405
5570
  if (args.length > 0) {
@@ -5797,8 +5962,12 @@ class LlEmitter {
5797
5962
  ? this.islandAdapter(fn.params.length, fn.ret.kind)
5798
5963
  : this.islandTypedAdapter(fn);
5799
5964
  this.declare(`declare ptr @scr_jsval_from_closure(ptr, i32, ptr)`);
5965
+ // ISLAND-REST closures encode a NEGATIVE arity (the C emitter's
5966
+ // rule): the wrapper hands the trailing slot the engine array of
5967
+ // the surplus arguments.
5968
+ const arity = fn.rest === true && fn.restAbi === "jsval" ? -fn.params.length : fn.params.length;
5800
5969
  const t = B.tmp();
5801
- B.line(`${t} = call ptr @scr_jsval_from_closure(ptr ${v.name}, i32 ${fn.params.length}, ptr @${adapter})`);
5970
+ B.line(`${t} = call ptr @scr_jsval_from_closure(ptr ${v.name}, i32 ${arity}, ptr @${adapter})`);
5802
5971
  return this.own({ name: t, type: e.type });
5803
5972
  }
5804
5973
  default: {
@@ -5981,6 +6150,18 @@ class LlEmitter {
5981
6150
  return t;
5982
6151
  });
5983
6152
  }
6153
+ case "callSpread": {
6154
+ // Spread application (`f(...pre, ...s)`): the prelude helper's
6155
+ // real spread syntax — iterator protocols are the engine's own,
6156
+ // the guards front-run V8's spread-call TypeError texts (the name
6157
+ // literal is the spread expression's spelling).
6158
+ this.declare(`declare ptr @scr_jsval_call_spread(ptr, ptr, ptr, ptr)`);
6159
+ return fallible(() => {
6160
+ const t = B.tmp();
6161
+ B.line(`${t} = call ptr @scr_jsval_call_spread(ptr ${a(0)}, ptr ${a(1)}, ptr ${a(2)}, ptr ${nameSym()})`);
6162
+ return t;
6163
+ });
6164
+ }
5984
6165
  case "construct": {
5985
6166
  const pack = argPack(args.slice(1).map((x) => x.name));
5986
6167
  this.declare(`declare ptr @scr_jsval_construct(ptr, i32, ptr)`);
@@ -5997,6 +6178,30 @@ class LlEmitter {
5997
6178
  B.line(`${t} = call ptr @scr_jsval_obj_lit(i32 ${args.length / 2}, ptr ${pack})`);
5998
6179
  return this.own({ name: t, type: e.type });
5999
6180
  }
6181
+ case "tplStrings": {
6182
+ const pack = argPack(args.map((x) => x.name));
6183
+ this.declare(`declare ptr @scr_jsval_tpl_strings(i32, ptr)`);
6184
+ const t = B.tmp();
6185
+ B.line(`${t} = call ptr @scr_jsval_tpl_strings(i32 ${args.length / 2}, ptr ${pack})`);
6186
+ return this.own({ name: t, type: e.type });
6187
+ }
6188
+ case "objSpread": {
6189
+ this.declare(`declare ptr @scr_jsval_obj_spread(ptr, ptr)`);
6190
+ return fallible(() => {
6191
+ const t = B.tmp();
6192
+ B.line(`${t} = call ptr @scr_jsval_obj_spread(ptr ${a(0)}, ptr ${a(1)})`);
6193
+ return t;
6194
+ });
6195
+ }
6196
+ case "defineGetter": {
6197
+ // Getter completion for an island literal (the C emitter's
6198
+ // scr_jsval_define_getter shape): defines key a(1) on obj a(0)
6199
+ // as an engine getter invoking a(2); answers the object (+1).
6200
+ this.declare(`declare ptr @scr_jsval_define_getter(ptr, ptr, ptr)`);
6201
+ const t = B.tmp();
6202
+ B.line(`${t} = call ptr @scr_jsval_define_getter(ptr ${a(0)}, ptr ${a(1)}, ptr ${a(2)})`);
6203
+ return this.own({ name: t, type: e.type });
6204
+ }
6000
6205
  case "arrLit": {
6001
6206
  const pack = argPack(args.map((x) => x.name));
6002
6207
  this.declare(`declare ptr @scr_jsval_arr_lit(i32, ptr)`);
@@ -6306,7 +6511,7 @@ class LlEmitter {
6306
6511
  d.push(` %fnp = getelementptr inbounds %ScrClosure, ptr %c, i64 0, i32 1`, ` %fn = load ptr, ptr %fnp`);
6307
6512
  if (ret.async) {
6308
6513
  // The closure returns a +1 ScrPromise; from_promise takes ownership.
6309
- const tagN = { void: 0, f64: 1, bool: 2, string: 3, jsval: 4, json: 0 }[ret.tag];
6514
+ const tagN = { void: 0, f64: 1, bool: 2, string: 3, jsval: 4, json: 0, dyn: 0 }[ret.tag];
6310
6515
  this.declare(`declare ptr @scr_jsval_from_promise(ptr, i32)`);
6311
6516
  d.push(` %pr = call ptr %fn(${passed.join(", ")})`, ` %prn = icmp eq ptr %pr, null`, ` br i1 %prn, label %pnull, label %pok`, `pnull:`, ` ret ptr null`, `pok:`, ` %pj = call ptr @scr_jsval_from_promise(ptr %pr, i32 ${tagN})`, ` ret ptr %pj`);
6312
6517
  }
@@ -6331,6 +6536,13 @@ class LlEmitter {
6331
6536
  this.declare(`declare void @scr_str_release(ptr)`);
6332
6537
  d.push(` %r = call ptr %fn(${passed.join(", ")})`, ` %rn = icmp eq ptr %r, null`, ` br i1 %rn, label %snull, label %sok`, `snull:`, ` ret ptr null`, `sok:`, ` %j = call ptr @scr_jsval_from_str(ptr %r)`, ` call void @scr_str_release(ptr %r)`, ` ret ptr %j`);
6333
6538
  break;
6539
+ case "dyn":
6540
+ // A checked-dynamic (+1 DOM) result: deep copy into the engine
6541
+ // (the jsMarshal dyn rule); NULL is the throw-path dummy.
6542
+ this.declare(`declare ptr @scr_jsval_from_dyn(ptr)`);
6543
+ this.declare(`declare void @scr_dyn_release(ptr)`);
6544
+ d.push(` %r = call ptr %fn(${passed.join(", ")})`, ` %rn = icmp eq ptr %r, null`, ` br i1 %rn, label %dnull, label %dok`, `dnull:`, ` ret ptr null`, `dok:`, ` %j = call ptr @scr_jsval_from_dyn(ptr %r)`, ` call void @scr_dyn_release(ptr %r)`, ` ret ptr %j`);
6545
+ break;
6334
6546
  case "json": {
6335
6547
  // A JSON-safe composite return: the jsMarshal path — the type-
6336
6548
  // directed serializer, then the engine's JSON parser (deep
@@ -8044,6 +8256,18 @@ class LlEmitter {
8044
8256
  // DataView getters: an omitted littleEndian is big-endian (the JS
8045
8257
  // default). Throw Node's constant RangeError on a bad offset.
8046
8258
  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);
8259
+ case "dvSetUint8":
8260
+ case "dvSetInt8":
8261
+ case "dvSetUint16":
8262
+ case "dvSetInt16":
8263
+ case "dvSetUint32":
8264
+ case "dvSetInt32":
8265
+ case "dvSetFloat32":
8266
+ case "dvSetFloat64":
8267
+ // DataView setters: [offset, value, littleEndian?] — void; throw
8268
+ // the getters' constant RangeError on a bad offset.
8269
+ return call("scr_dataview_set", "void (ptr, double, double, i32, i1 zeroext)", `ptr ${r.name}, double ${args[0].name}, double ${args[1].name}, ` +
8270
+ `i32 ${DV_SET_KIND[method]}, i1 ${args[2]?.name ?? "false"}`, false, true);
8047
8271
  default: {
8048
8272
  const _exhaustive = method;
8049
8273
  void _exhaustive;
@@ -8424,6 +8648,42 @@ class LlEmitter {
8424
8648
  if (USES_TIMERS_LIB_FNS.has(e.fn))
8425
8649
  this.usesTimers = true;
8426
8650
  // The handful with non-generic shapes first.
8651
+ if (e.fn === "error.argTypeThrow") {
8652
+ // Always throws with the runtime-rendered Received tail (the
8653
+ // error.nodeThrow dummy pattern). Borrows all three.
8654
+ const an = this.emitExpr(e.args[0]);
8655
+ const ex = this.emitExpr(e.args[1]);
8656
+ const got = this.emitExpr(e.args[2]);
8657
+ this.declare(`declare void @scr_throw_arg_type(ptr, ptr, ptr)`);
8658
+ B.line(`call void @scr_throw_arg_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`);
8659
+ const ty = this.llType(e.type);
8660
+ if (ty === "void") {
8661
+ this.emitPendingCheck();
8662
+ return { name: "", type: e.type };
8663
+ }
8664
+ const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
8665
+ const out = this.own({ name: dummy, type: e.type });
8666
+ this.emitPendingCheck();
8667
+ return out;
8668
+ }
8669
+ if (e.fn === "error.nodeThrow") {
8670
+ // The compiler-resolved Node-parity throw (always throws — the
8671
+ // typed dummy is abandoned by the pending check's unwind).
8672
+ const kind = this.emitExpr(e.args[0]);
8673
+ const code = this.emitExpr(e.args[1]);
8674
+ const msg = this.emitExpr(e.args[2]);
8675
+ this.declare(`declare void @scr_throw_node_coded(double, ptr, ptr)`);
8676
+ B.line(`call void @scr_throw_node_coded(double ${kind.name}, ptr ${code.name}, ptr ${msg.name})`);
8677
+ const ty = this.llType(e.type);
8678
+ if (ty === "void") {
8679
+ this.emitPendingCheck();
8680
+ return { name: "", type: e.type };
8681
+ }
8682
+ const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
8683
+ const out = this.own({ name: dummy, type: e.type });
8684
+ this.emitPendingCheck();
8685
+ return out;
8686
+ }
8427
8687
  if (e.fn === "global.undefRead") {
8428
8688
  // A declare-d const nothing defines: Node's catchable
8429
8689
  // ReferenceError at the access (always throws — the typed dummy is
@@ -9655,6 +9915,28 @@ class LlEmitter {
9655
9915
  this.emitPendingCheck();
9656
9916
  return out;
9657
9917
  }
9918
+ if (e.fn === "sp.pipeline") {
9919
+ // pipeline(count, s1..sn) settling a void promise: the stream list
9920
+ // rides the callback form's stack array, no callback slot.
9921
+ const countArg = e.args[0];
9922
+ if (countArg.kind !== "numLit")
9923
+ throw new Error(`llvm emitter bug: ${e.fn} count not a literal`);
9924
+ const n = countArg.value;
9925
+ const args = e.args.map((a) => this.emitExpr(a));
9926
+ const arr = B.slot();
9927
+ B.entryAllocas.push(`${arr} = alloca [${n} x ptr]`);
9928
+ for (let i = 0; i < n; i++) {
9929
+ const p = B.tmp();
9930
+ B.line(`${p} = getelementptr inbounds [${n} x ptr], ptr ${arr}, i64 0, i64 ${i}`);
9931
+ B.line(`store ptr ${args[1 + i].name}, ptr ${p}`);
9932
+ }
9933
+ const t = B.tmp();
9934
+ this.declare(`declare ptr @scr_sp_pipeline(double, ptr)`);
9935
+ B.line(`${t} = call ptr @scr_sp_pipeline(double ${f64Lit(n)}, ptr ${arr})`);
9936
+ const out = this.own({ name: t, type: e.type });
9937
+ this.emitPendingCheck();
9938
+ return out;
9939
+ }
9658
9940
  if (e.fn === "stream.pipeline" || e.fn === "stream.pipelineDyn") {
9659
9941
  // pipeline(count, s1..sn, cb): the destination answers +1. The
9660
9942
  // stream list rides a stack array (the C compound literal).