@scriptc/compiler 0.1.4 → 0.1.6

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 (158) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +75 -11
  2. package/dist/backend/c/c-emitter.d.ts +5 -1
  3. package/dist/backend/c/c-emitter.js +22 -9
  4. package/dist/backend/c/c-emitter.js.map +1 -1
  5. package/dist/backend/c/exprs.d.ts +2 -2
  6. package/dist/backend/c/exprs.js +298 -27
  7. package/dist/backend/c/exprs.js.map +1 -1
  8. package/dist/backend/c/stmts.js +5 -3
  9. package/dist/backend/c/stmts.js.map +1 -1
  10. package/dist/backend/c/types.d.ts +2 -3
  11. package/dist/backend/c/types.js +8 -1
  12. package/dist/backend/c/types.js.map +1 -1
  13. package/dist/backend/c/walkers.js +1 -1
  14. package/dist/backend/c/walkers.js.map +1 -1
  15. package/dist/backend/link-plan.d.ts +1 -0
  16. package/dist/backend/link-plan.js +2 -1
  17. package/dist/backend/link-plan.js.map +1 -1
  18. package/dist/backend/llvm/dyn.js +1 -1
  19. package/dist/backend/llvm/dyn.js.map +1 -1
  20. package/dist/backend/llvm/emitter.js +25 -9
  21. package/dist/backend/llvm/emitter.js.map +1 -1
  22. package/dist/backend/llvm/expr-bytes.d.ts +1 -2
  23. package/dist/backend/llvm/expr-bytes.js +22 -17
  24. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  25. package/dist/backend/llvm/expr-containers.d.ts +1 -0
  26. package/dist/backend/llvm/expr-containers.js +58 -4
  27. package/dist/backend/llvm/expr-containers.js.map +1 -1
  28. package/dist/backend/llvm/expr-context.d.ts +6 -2
  29. package/dist/backend/llvm/expr-primitives.js +22 -7
  30. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  31. package/dist/backend/llvm/lib-filesystem.js +20 -0
  32. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  33. package/dist/backend/llvm/lib-network.js +53 -10
  34. package/dist/backend/llvm/lib-network.js.map +1 -1
  35. package/dist/backend/llvm/lib-process.js +4 -3
  36. package/dist/backend/llvm/lib-process.js.map +1 -1
  37. package/dist/backend/llvm/lib-shared.js +65 -0
  38. package/dist/backend/llvm/lib-shared.js.map +1 -1
  39. package/dist/backend/native-toolchain.d.ts +2 -0
  40. package/dist/backend/native-toolchain.js +2 -1
  41. package/dist/backend/native-toolchain.js.map +1 -1
  42. package/dist/backend/targets.d.ts +3 -0
  43. package/dist/backend/targets.js +13 -0
  44. package/dist/backend/targets.js.map +1 -1
  45. package/dist/coverage/surface-manifest.js +12 -5
  46. package/dist/coverage/surface-manifest.js.map +1 -1
  47. package/dist/executable/executable-cache.d.ts +2 -0
  48. package/dist/executable/executable-cache.js +3 -1
  49. package/dist/executable/executable-cache.js.map +1 -1
  50. package/dist/executable/executable-cache.test.js +3 -3
  51. package/dist/executable/executable-cache.test.js.map +1 -1
  52. package/dist/frontend/lowering/array-indexed-mutation.d.ts +8 -0
  53. package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
  54. package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -0
  55. package/dist/frontend/lowering/containers/bytes.d.ts +1 -1
  56. package/dist/frontend/lowering/containers/bytes.js +6 -5
  57. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  58. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +14 -6
  59. package/dist/frontend/lowering/containers/string-and-regexp.js +420 -34
  60. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  61. package/dist/frontend/lowering/expressions/object-literals.js +8 -0
  62. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  63. package/dist/frontend/lowering/indexed-comparison.d.ts +6 -0
  64. package/dist/frontend/lowering/indexed-comparison.js +30 -0
  65. package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
  66. package/dist/frontend/lowering/lower-builtins.d.ts +5 -6
  67. package/dist/frontend/lowering/lower-builtins.js +24 -16
  68. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  69. package/dist/frontend/lowering/lower-calls.d.ts +3 -2
  70. package/dist/frontend/lowering/lower-calls.js +522 -89
  71. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  72. package/dist/frontend/lowering/lower-classes.js +5 -29
  73. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  74. package/dist/frontend/lowering/lower-containers.d.ts +15 -11
  75. package/dist/frontend/lowering/lower-containers.js +588 -146
  76. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  77. package/dist/frontend/lowering/lower-exprs.d.ts +12 -0
  78. package/dist/frontend/lowering/lower-exprs.js +145 -11
  79. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  80. package/dist/frontend/lowering/lower-island.js +17 -6
  81. package/dist/frontend/lowering/lower-island.js.map +1 -1
  82. package/dist/frontend/lowering/lower-server.d.ts +2 -4
  83. package/dist/frontend/lowering/lower-server.js +288 -32
  84. package/dist/frontend/lowering/lower-server.js.map +1 -1
  85. package/dist/frontend/lowering/lower-stmts.js +4 -4
  86. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  87. package/dist/frontend/lowering/lowerer.js +37 -2
  88. package/dist/frontend/lowering/lowerer.js.map +1 -1
  89. package/dist/frontend/lowering/optional-arguments.d.ts +9 -1
  90. package/dist/frontend/lowering/optional-arguments.js +67 -1
  91. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  92. package/dist/frontend/lowering/pairs-snapshot.d.ts +3 -1
  93. package/dist/frontend/lowering/pairs-snapshot.js +12 -4
  94. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  95. package/dist/frontend/lowering/surfaces.d.ts +10 -14
  96. package/dist/frontend/lowering/surfaces.js +50 -28
  97. package/dist/frontend/lowering/surfaces.js.map +1 -1
  98. package/dist/frontend/npm-static-prune.d.ts +6 -0
  99. package/dist/frontend/npm-static-prune.js +203 -0
  100. package/dist/frontend/npm-static-prune.js.map +1 -0
  101. package/dist/frontend/npm-static-prune.test.d.ts +1 -0
  102. package/dist/frontend/npm-static-prune.test.js +48 -0
  103. package/dist/frontend/npm-static-prune.test.js.map +1 -0
  104. package/dist/frontend/npm.js +7 -6
  105. package/dist/frontend/npm.js.map +1 -1
  106. package/dist/frontend/program.js +15 -4
  107. package/dist/frontend/program.js.map +1 -1
  108. package/dist/frontend/provenance.js +8 -3
  109. package/dist/frontend/provenance.js.map +1 -1
  110. package/dist/frontend/type-mapper.js +16 -5
  111. package/dist/frontend/type-mapper.js.map +1 -1
  112. package/dist/index.d.ts +3 -3
  113. package/dist/index.js +13 -41
  114. package/dist/index.js.map +1 -1
  115. package/dist/ir/analysis.d.ts +5 -3
  116. package/dist/ir/analysis.js +25 -11
  117. package/dist/ir/analysis.js.map +1 -1
  118. package/dist/ir/analysis.test.js +50 -2
  119. package/dist/ir/analysis.test.js.map +1 -1
  120. package/dist/ir/constant-tables.d.ts +17 -0
  121. package/dist/ir/constant-tables.js +99 -0
  122. package/dist/ir/constant-tables.js.map +1 -0
  123. package/dist/ir/constant-tables.test.d.ts +1 -0
  124. package/dist/ir/constant-tables.test.js +78 -0
  125. package/dist/ir/constant-tables.test.js.map +1 -0
  126. package/dist/ir/integer-ranges.d.ts +13 -0
  127. package/dist/ir/integer-ranges.js +122 -0
  128. package/dist/ir/integer-ranges.js.map +1 -0
  129. package/dist/ir/integer-ranges.test.d.ts +1 -0
  130. package/dist/ir/integer-ranges.test.js +63 -0
  131. package/dist/ir/integer-ranges.test.js.map +1 -0
  132. package/dist/ir/ir.d.ts +56 -35
  133. package/dist/ir/ir.js +9 -1
  134. package/dist/ir/ir.js.map +1 -1
  135. package/dist/ir/ir.test.js +6 -0
  136. package/dist/ir/ir.test.js.map +1 -1
  137. package/dist/ir/scalar-records.d.ts +8 -0
  138. package/dist/ir/scalar-records.js +216 -0
  139. package/dist/ir/scalar-records.js.map +1 -0
  140. package/dist/ir/scalar-records.test.d.ts +1 -0
  141. package/dist/ir/scalar-records.test.js +142 -0
  142. package/dist/ir/scalar-records.test.js.map +1 -0
  143. package/dist/ir/validate.js +135 -34
  144. package/dist/ir/validate.js.map +1 -1
  145. package/dist/ir/validate.test.d.ts +1 -0
  146. package/dist/ir/validate.test.js +48 -0
  147. package/dist/ir/validate.test.js.map +1 -0
  148. package/dist/library/cache-primitives.d.ts +1 -1
  149. package/dist/library/cache-primitives.js +3 -3
  150. package/dist/library/cache-primitives.js.map +1 -1
  151. package/dist/library/fence-eval.js +11 -4
  152. package/dist/library/fence-eval.js.map +1 -1
  153. package/dist/library/library-cache.js +0 -1
  154. package/dist/library/library-cache.js.map +1 -1
  155. package/dist/library/library-cache.test.js +3 -3
  156. package/dist/library/library-cache.test.js.map +1 -1
  157. package/package.json +17 -17
  158. package/surface-manifest.json +90 -45
@@ -1,12 +1,12 @@
1
1
  import { InternalCompilerError } from "../../errors.js";
2
- import { arrayOf, BOOL, BYTES_U8, bytesOf, canMarshalFuncIntoIsland, CHILDSTREAM_T, CHILDWRITER_T, DYN, F64, islandPromisePayloadTag, isClassOwnEnumerableFieldName, isDynTypedRefType, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, RUNTIME_ERROR_CLASSES, STRING, typeEquals, typeKey } from "../../ir/ir.js";
2
+ import { arrayOf, BOOL, BYTES_U8, bytesOf, canMarshalFuncIntoIsland, CHILDSTREAM_T, CHILDWRITER_T, DYN, F64, islandPromisePayloadTag, isClassOwnEnumerableFieldName, isDynTypedRefType, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, NETSOCKET_T, RUNTIME_ERROR_CLASSES, STRING, typeEquals, typeKey } from "../../ir/ir.js";
3
3
  import { boxAccess, BYTES_NUM_KIND_C, BYTES_NUM_VAR_C, bytesElemKindC, cDecl, cFnPtrCast, cNumberLiteral, cStringLiteral, cType, DV_GET_KIND_C, DV_SET_KIND_C, elemAccess, mapKeyAccess, mapKeyKindC, mapValKindC, releaseCallC, retainCallC, vAdapters } from "./types.js";
4
4
  import { mangleClassNew, mangleClassRetain, mangleClassStruct, mangleField, mangleFnClosure, mangleFunction, mangleGlobal, mangleLocal, mangleRecordClone, mangleRecordNew, mangleRecordStruct, mangleVtStruct } from "../mangle.js";
5
5
  import { OVERFLOW_MEMBER } from "./shapes.js";
6
6
  import { dynDestrCheckHelper, dynIterNHelper, dynKeyGetHelper } from "./walkers.js";
7
7
  import { collectFfiRetainedOps, parseFfiCallbackKey } from "../ffi-callbacks.js";
8
8
  import { genResultThunkFor } from "./async.js";
9
- import { isStableBytesOperand, matchStringSelfConcat, newValueMayThrow, streamTypedRefEligible, undefinedArmTag } from "../../ir/analysis.js";
9
+ import { isStableReceiverOperand, matchStringSelfConcat, newValueMayThrow, streamTypedRefEligible, undefinedArmTag } from "../../ir/analysis.js";
10
10
  function streamTypedRefCommitAdapter(emitter, t, snapshot, defs) {
11
11
  if (t.kind === "bytes") {
12
12
  const commit = `${snapshot}_commit`;
@@ -385,13 +385,13 @@ function dynPromiseAdapter(emitter, inner) {
385
385
  emitter.walkerDefs.push(`${sig} { /* checked-dynamic promise exit ${key} */`, ` ScrDyn *sc_d = (ScrDyn *)scr_promise_payload_ref(sc_src);`, ` ${cDecl(inner, "sc_v")} = ${emitter.dynCheckHelper(inner)}(sc_d, NULL);`, ` scr_dyn_release(sc_d);`, ` if (scr_exc_pending()) {`, ` scr_promise_reject_pending(sc_dst);`, ` return;`, ` }`, ` ${fulfill}`, `}`, ``);
386
386
  return sym;
387
387
  }
388
- /** Evaluate a bytes receiver as a borrow when it is a direct, unboxed
388
+ /** Evaluate a receiver as a borrow when it is a direct, unboxed
389
389
  * binding and all later operands are stable. The binding's scope/global
390
390
  * owner then keeps the value alive, avoiding retain/release traffic around
391
391
  * every indexed access. Any uncertain shape falls back to an owned temp. */
392
- export function emitBytesReceiver(emitter, receiver, following) {
392
+ export function emitStableReceiver(emitter, receiver, following) {
393
393
  if (receiver.kind === "varRef" &&
394
- following.every((operand) => isStableBytesOperand(operand, receiver.localId))) {
394
+ following.every((operand) => isStableReceiverOperand(operand, receiver.localId))) {
395
395
  const local = emitter.currentLocals.get(receiver.localId);
396
396
  if (local && !local.boxed) {
397
397
  return emitter.newBorrowedTemp(receiver.type, mangleLocal(receiver.localId));
@@ -604,11 +604,36 @@ function emitLiteralExpr(emitter, e) {
604
604
  }
605
605
  }
606
606
  }
607
+ /** Unsigned operations avoid signed overflow and implementation-defined
608
+ * right shifts/conversions. Reinterpret the final bits numerically. */
609
+ function emitIntegerBits(emitter, e, left, right) {
610
+ const a = `sc_bits_${emitter.tempCounter++}`;
611
+ const out = `sc_bits_${emitter.tempCounter++}`;
612
+ emitter.line(`uint32_t ${a} = (uint32_t)(int64_t)${left};`);
613
+ let operation;
614
+ if (e.kind === "unary")
615
+ operation = `~${a}`;
616
+ else {
617
+ const b = `sc_bits_${emitter.tempCounter++}`;
618
+ const shift = e.op === "<<" || e.op === ">>" || e.op === ">>>";
619
+ emitter.line(`uint32_t ${b} = (uint32_t)(int64_t)${right}${shift ? " & 31u" : ""};`);
620
+ operation = e.op === ">>" ? `(${a} >> ${b}) | ((${a} & 0x80000000u) ? ~(UINT32_MAX >> ${b}) : 0u)`
621
+ : `${a} ${e.op === ">>>" ? ">>" : e.op} ${b}`;
622
+ }
623
+ emitter.line(`uint32_t ${out} = ${operation};`);
624
+ return emitter.newTemp(e.type, e.op === ">>>" ? `(double)${out}` : `(double)${out} - ((${out} & 0x80000000u) ? 4294967296.0 : 0.0)`);
625
+ }
607
626
  function emitOperatorExpr(emitter, e) {
608
627
  switch (e.kind) {
609
628
  case "bin": {
610
629
  const l = emitter.emitExpr(e.left);
611
630
  const r = emitter.emitExpr(e.right);
631
+ if ((e.op === "+" || e.op === "-") && emitter.integerRanges.get(e)) {
632
+ return emitter.newTemp(e.type, `(double)((int64_t)${l.name} ${e.op} (int64_t)${r.name})`);
633
+ }
634
+ if (["&", "|", "^", "<<", ">>", ">>>"].includes(e.op) && emitter.integerRanges.get(e.left) && emitter.integerRanges.get(e.right)) {
635
+ return emitIntegerBits(emitter, e, l.name, r.name);
636
+ }
612
637
  switch (e.op) {
613
638
  case "%":
614
639
  return emitter.newTemp(e.type, `fmod(${l.name}, ${r.name})`);
@@ -639,7 +664,8 @@ function emitOperatorExpr(emitter, e) {
639
664
  case "unary": {
640
665
  const v = emitter.emitExpr(e.operand);
641
666
  if (e.op === "~")
642
- return emitter.newTemp(e.type, `scr_bit_not(${v.name})`);
667
+ return emitter.integerRanges.get(e.operand)
668
+ ? emitIntegerBits(emitter, e, v.name) : emitter.newTemp(e.type, `scr_bit_not(${v.name})`);
643
669
  return emitter.newTemp(e.type, `${e.op}${v.name}`);
644
670
  }
645
671
  case "incDec": {
@@ -1233,9 +1259,13 @@ function emitStringExpr(emitter, e) {
1233
1259
  }
1234
1260
  return emitter.newTemp(e.type, `scr_str_includes(${r.name}, ${args[0].name})`);
1235
1261
  case "startsWith":
1236
- return emitter.newTemp(e.type, `scr_str_starts_with(${r.name}, ${args[0].name})`);
1262
+ return emitter.newTemp(e.type, args[1]
1263
+ ? `scr_str_starts_with_from(${r.name}, ${args[0].name}, ${args[1].name})`
1264
+ : `scr_str_starts_with(${r.name}, ${args[0].name})`);
1237
1265
  case "endsWith":
1238
- return emitter.newTemp(e.type, `scr_str_ends_with(${r.name}, ${args[0].name})`);
1266
+ return emitter.newTemp(e.type, args[1]
1267
+ ? `scr_str_ends_with_from(${r.name}, ${args[0].name}, ${args[1].name})`
1268
+ : `scr_str_ends_with(${r.name}, ${args[0].name})`);
1239
1269
  case "slice":
1240
1270
  return emitter.newTemp(e.type, `scr_str_slice(${r.name}, ${args[0]?.name ?? "0"}, ${args[1]?.name ?? "INFINITY"})`);
1241
1271
  case "substring":
@@ -1447,7 +1477,11 @@ function emitContainerExpr(emitter, e) {
1447
1477
  return emitter.newTemp(e.type, `scr_arr_state(${arr.name}, ${idx.name})`);
1448
1478
  }
1449
1479
  case "arrIntrinsic": {
1450
- const r = emitter.emitExpr(e.receiver);
1480
+ // getNumber/indexEq produce scalars without invoking user code. A
1481
+ // stable binding can own its receiver until that lookup finishes.
1482
+ const r = e.method === "getNumber" || e.method === "indexEq"
1483
+ ? emitStableReceiver(emitter, e.receiver, e.args)
1484
+ : emitter.emitExpr(e.receiver);
1451
1485
  if (e.receiver.type.kind !== "array")
1452
1486
  throw new InternalCompilerError("emitter bug: arrIntrinsic on non-array");
1453
1487
  const acc = elemAccess(e.receiver.type.elem);
@@ -1455,10 +1489,21 @@ function emitContainerExpr(emitter, e) {
1455
1489
  switch (method) {
1456
1490
  case "length":
1457
1491
  return emitter.newTemp(e.type, `scr_arr_len(${r.name})`);
1492
+ case "getNumber": {
1493
+ const index = emitter.emitExpr(e.args[0]);
1494
+ const table = e.receiver.kind === "varRef" ? emitter.constantNumericTables.get(e.receiver.localId) : undefined;
1495
+ return emitter.newTemp(e.type, `${table ? `${table.symbol}_get` : "scr_arr_get_number"}(${r.name}, ${index.name})`);
1496
+ }
1458
1497
  case "nextPresent": {
1459
1498
  const start = emitter.emitExpr(e.args[0]);
1460
1499
  return emitter.newTemp(e.type, `scr_arr_next_present(${r.name}, ${start.name})`);
1461
1500
  }
1501
+ case "indexEq": {
1502
+ const index = emitter.emitExpr(e.args[0]);
1503
+ const other = emitStableReceiver(emitter, e.args[1], [e.args[2]]);
1504
+ const otherIndex = emitter.emitExpr(e.args[2]);
1505
+ return emitter.newTemp(e.type, `scr_arr_index_eq(${r.name}, ${index.name}, ${other.name}, ${otherIndex.name})`);
1506
+ }
1462
1507
  case "push": {
1463
1508
  // Variadic like JS: every argument evaluates first (left to
1464
1509
  // right — an argument reading the array sees the pre-push
@@ -1605,6 +1650,17 @@ function emitContainerExpr(emitter, e) {
1605
1650
  const cnt = e.args[1] ? emitter.emitExpr(e.args[1]).name : "INFINITY";
1606
1651
  return emitter.newTemp(e.type, `scr_arr_splice(${r.name}, ${start.name}, ${cnt})`);
1607
1652
  }
1653
+ case "spliceInsert": {
1654
+ const start = emitter.emitExpr(e.args[0]);
1655
+ const count = emitter.emitExpr(e.args[1]);
1656
+ const items = emitter.emitExpr(e.args[2]);
1657
+ return emitter.newTemp(e.type, `scr_arr_splice_insert(${r.name}, ${start.name}, ${count.name}, ${items.name})`);
1658
+ }
1659
+ case "flatCopy":
1660
+ case "flatOne": {
1661
+ const out = emitter.emitExpr(e.args[0]);
1662
+ return emitter.newTemp(e.type, `scr_arr_flat_copy(${r.name}, ${out.name}, ${method === "flatOne" ? "true" : "false"})`);
1663
+ }
1608
1664
  default: {
1609
1665
  const _exhaustive = method;
1610
1666
  void _exhaustive;
@@ -1677,7 +1733,7 @@ function emitContainerExpr(emitter, e) {
1677
1733
  const method = e.method;
1678
1734
  const directElementAccess = method === "length" || method === "byteLength" || method === "get";
1679
1735
  const r = directElementAccess
1680
- ? emitBytesReceiver(emitter, e.receiver, e.args)
1736
+ ? emitStableReceiver(emitter, e.receiver, e.args)
1681
1737
  : emitter.emitExpr(e.receiver);
1682
1738
  const integerIndex = method === "get" ? emitter.integerLoopIndex(e.args[0]) : null;
1683
1739
  const args = integerIndex === null ? e.args.map((a) => emitter.emitExpr(a)) : [];
@@ -1688,7 +1744,7 @@ function emitContainerExpr(emitter, e) {
1688
1744
  if (e.receiver.type.kind !== "bytes") {
1689
1745
  throw new InternalCompilerError("emitter bug: bytesIntrinsic byteLength on non-bytes");
1690
1746
  }
1691
- return emitter.newTemp(e.type, `(double)(${r.name}->len * ${e.receiver.type.elem === "u8" ? "1" : "4"})`);
1747
+ return emitter.newTemp(e.type, `(double)(${r.name}->len * ${e.receiver.type.elem === "u8" ? "1" : e.receiver.type.elem === "f64" ? "8" : "4"})`);
1692
1748
  case "get":
1693
1749
  // Any invalid index traps (the array runtime's discipline).
1694
1750
  if (e.receiver.type.kind !== "bytes") {
@@ -4010,6 +4066,8 @@ function emitDynamicLibCall(state) {
4010
4066
  case "dyn.typeof":
4011
4067
  // Bare typeof on a dyn value: the dyn kind's JS answer (+1).
4012
4068
  return finish(`scr_dyn_typeof(${arg(0)})`);
4069
+ case "dyn.objectTag":
4070
+ return finish(`scr_dyn_object_tag(${arg(0)})`);
4013
4071
  case "dyn.toString":
4014
4072
  // Receiver-kind-dispatched toString (+1); throws Node's
4015
4073
  // TypeError on undefined/null and the "is not a function"
@@ -4764,6 +4822,8 @@ function emitPrimitiveLibCall(state) {
4764
4822
  return finish(`scr_math_max_arr(${arg(0)})`);
4765
4823
  case "math.minArr":
4766
4824
  return finish(`scr_math_min_arr(${arg(0)})`);
4825
+ case "math.hypotArr":
4826
+ return finish(`scr_math_hypot_arr(${arg(0)})`);
4767
4827
  // Math.floor/trunc/ceil — the C functions ARE the JS operations
4768
4828
  // (math.h is always included). Borrow nothing; no throw.
4769
4829
  case "math.floor":
@@ -4772,6 +4832,38 @@ function emitPrimitiveLibCall(state) {
4772
4832
  return finish(`trunc(${arg(0)})`);
4773
4833
  case "math.ceil":
4774
4834
  return finish(`ceil(${arg(0)})`);
4835
+ case "math.sin":
4836
+ return finish(`sin(${arg(0)})`);
4837
+ case "math.cos":
4838
+ return finish(`cos(${arg(0)})`);
4839
+ case "math.tan":
4840
+ return finish(`tan(${arg(0)})`);
4841
+ case "math.asin":
4842
+ return finish(`asin(${arg(0)})`);
4843
+ case "math.acos":
4844
+ return finish(`acos(${arg(0)})`);
4845
+ case "math.atan":
4846
+ return finish(`atan(${arg(0)})`);
4847
+ case "math.cbrt":
4848
+ return finish(`cbrt(${arg(0)})`);
4849
+ case "math.sign":
4850
+ // Each argument was evaluated into a temp. Returning that temp
4851
+ // for unordered/zero inputs preserves NaN and the sign of zero.
4852
+ return finish(`(${arg(0)} > 0.0 ? 1.0 : (${arg(0)} < 0.0 ? -1.0 : ${arg(0)}))`);
4853
+ case "math.exp":
4854
+ return finish(`exp(${arg(0)})`);
4855
+ case "math.sqrt":
4856
+ return finish(`sqrt(${arg(0)})`);
4857
+ case "math.log":
4858
+ return finish(`log(${arg(0)})`);
4859
+ case "math.log2":
4860
+ return finish(`log2(${arg(0)})`);
4861
+ case "math.log10":
4862
+ return finish(`log10(${arg(0)})`);
4863
+ case "math.atan2":
4864
+ return finish(`atan2(${arg(0)}, ${arg(1)})`);
4865
+ case "math.pow":
4866
+ return finish(`scr_math_pow(${arg(0)}, ${arg(1)})`);
4775
4867
  // Math.abs — C fabs IS the JS operation. Math.round — the JS
4776
4868
  // half-toward-+Infinity rule (scr_lib.c; C round() differs on
4777
4869
  // halves and naive floor(x+0.5) drifts at the epsilon boundary).
@@ -5015,6 +5107,8 @@ function emitPrimitiveLibCall(state) {
5015
5107
  : `scr_str_from_char_code(${arg(0)})`);
5016
5108
  case "string.lastIndexOf":
5017
5109
  return finish(`scr_str_last_index_of(${arg(0)}, ${arg(1)})`);
5110
+ case "string.lastIndexOfFrom":
5111
+ return finish(`scr_str_last_index_of_from(${arg(0)}, ${arg(1)}, ${arg(2)})`);
5018
5112
  case "string.raw":
5019
5113
  // Raw literals + pre-stringified substitutions; +1 string.
5020
5114
  return finish(`scr_str_raw(${arg(0)}, ${arg(1)})`);
@@ -5521,6 +5615,8 @@ function emitNetworkLibCall(state) {
5521
5615
  }
5522
5616
  case "net.serverPort":
5523
5617
  return finish(`scr_net_server_port(${arg(0)})`);
5618
+ case "net.serverListening":
5619
+ return finish(`scr_net_server_listening(${arg(0)})`);
5524
5620
  case "net.serverAddress": {
5525
5621
  // The AddressInfo record from the three runtime reads (the
5526
5622
  // dgram.address materialization; none of these throw).
@@ -5598,9 +5694,7 @@ function emitNetworkLibCall(state) {
5598
5694
  const cb = args[1];
5599
5695
  emitter.moveTemp(cb);
5600
5696
  const adapter = cbT.params.length === 0 ? "scr_net_conn_thunk0" : "scr_net_conn_thunk_sock";
5601
- const entry = e.fn === "net.serverOnConnection"
5602
- ? "scr_net_server_on_connection"
5603
- : "scr_net_server_on_secure_connection";
5697
+ const entry = e.fn === "net.serverOnConnection" ? "scr_net_server_on_connection" : "scr_net_server_on_secure_connection";
5604
5698
  emitter.line(`${entry}(${arg(0)}, ${cb.name}, &${adapter}, ${arg(2)});${emitter.srcComment(e.loc)}`);
5605
5699
  return { name: "", type: e.type };
5606
5700
  }
@@ -6058,10 +6152,22 @@ function emitHttpLibCall(state) {
6058
6152
  case "http.resSetHeader":
6059
6153
  emitter.line(`scr_http_res_set_header(${arg(0)}, ${arg(1)}, ${arg(2)});${emitter.srcComment(e.loc)}`);
6060
6154
  return { name: "", type: e.type };
6155
+ case "http.validateHeaderName":
6156
+ emitter.line(`scr_http_validate_header_name(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6157
+ return { name: "", type: e.type };
6158
+ case "http.validateHeaderValue":
6159
+ emitter.line(`scr_http_validate_header_value(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6160
+ return { name: "", type: e.type };
6061
6161
  case "http.resWriteHead":
6062
6162
  return finish(`scr_http_res_write_head(${arg(0)}, ${arg(1)})`);
6063
6163
  case "http.resWriteHeadN":
6064
6164
  return finish(`scr_http_res_write_head_n(${arg(0)}, ${arg(1)}, ${arg(2)}, ${arg(3)})`);
6165
+ case "http.resWriteContinue":
6166
+ return finish(`scr_http_res_write_continue(${arg(0)})`);
6167
+ case "http.resWriteProcessing":
6168
+ return finish(`scr_http_res_write_processing(${arg(0)})`);
6169
+ case "http.resWriteEarlyHints":
6170
+ return finish(`scr_http_res_write_early_hints(${arg(0)}, ${arg(1)})`);
6065
6171
  case "http.resWrite":
6066
6172
  return finish(`scr_http_res_write_str(${arg(0)}, ${arg(1)})`);
6067
6173
  case "http.resWriteBytes":
@@ -6078,6 +6184,8 @@ function emitHttpLibCall(state) {
6078
6184
  return finish(`scr_http_res_end_dynv(${arg(0)}, ${arg(1)})`);
6079
6185
  case "http.resHeadersSent":
6080
6186
  return finish(`scr_http_res_headers_sent(${arg(0)})`);
6187
+ case "http.resWritableEnded":
6188
+ return finish(`scr_http_res_writable_ended(${arg(0)})`);
6081
6189
  case "http.resFlushHeaders":
6082
6190
  return finish(`scr_http_res_flush_headers(${arg(0)})`);
6083
6191
  case "http.resAddTrailers":
@@ -6245,6 +6353,32 @@ function emitHttpLibCall(state) {
6245
6353
  case "http.serverAllowMissingHostHeader":
6246
6354
  emitter.line(`scr_http_server_allow_missing_host_header(${arg(0)});${emitter.srcComment(e.loc)}`);
6247
6355
  return { name: "", type: e.type };
6356
+ case "http.serverSetTimeout":
6357
+ emitter.line(`scr_net_server_set_timeout_plain(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6358
+ return { name: "", type: e.type };
6359
+ case "http.serverCloseAllConnections":
6360
+ case "http.serverCloseIdleConnections": {
6361
+ const entry = e.fn === "http.serverCloseAllConnections"
6362
+ ? "scr_net_server_close_all_connections" : "scr_net_server_close_idle_connections";
6363
+ emitter.line(`${entry}(${arg(0)});${emitter.srcComment(e.loc)}`);
6364
+ return { name: "", type: e.type };
6365
+ }
6366
+ case "http.serverOnTimeout":
6367
+ case "http.serverSetTimeoutCb": {
6368
+ const cbIndex = e.fn === "http.serverSetTimeoutCb" ? 2 : 1;
6369
+ const cbT = e.args[cbIndex].type;
6370
+ if (cbT.kind !== "func")
6371
+ throw new InternalCompilerError(`emitter bug: ${e.fn} callback not a func`);
6372
+ const cb = args[cbIndex];
6373
+ emitter.moveTemp(cb);
6374
+ const adapter = cbT.params.length === 0 ? "scr_net_conn_thunk0" : "scr_net_conn_thunk_sock";
6375
+ const entry = e.fn === "http.serverOnTimeout" ? "scr_net_server_on_timeout" : "scr_net_server_set_timeout_cb";
6376
+ const callArgs = e.fn === "http.serverOnTimeout"
6377
+ ? `${arg(0)}, ${cb.name}, &${adapter}, ${arg(2)}`
6378
+ : `${arg(0)}, ${arg(1)}, ${cb.name}, &${adapter}`;
6379
+ emitter.line(`${entry}(${callArgs});${emitter.srcComment(e.loc)}`);
6380
+ return { name: "", type: e.type };
6381
+ }
6248
6382
  case "http.serverTimeoutGet":
6249
6383
  return finish(`scr_net_server_timeout_get(${arg(0)}, ${arg(1)})`);
6250
6384
  case "http.serverTimeoutSet":
@@ -6257,27 +6391,82 @@ function emitHttpLibCall(state) {
6257
6391
  case "http.resStatusSet":
6258
6392
  emitter.line(`scr_http_res_status_set(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6259
6393
  return { name: "", type: e.type };
6260
- case "http.resStatusMsgGet":
6261
- return finish(`scr_http_res_status_msg_get(${arg(0)})`);
6394
+ case "http.resStatusMsgGet": {
6395
+ if (e.type.kind !== "union")
6396
+ throw new InternalCompilerError("emitter bug: http.resStatusMsgGet result is not a union");
6397
+ const def = emitter.unionsById.get(e.type.unionId);
6398
+ const strTag = def ? def.arms.findIndex((a) => a.kind === "string") : -1;
6399
+ const undefTag = undefinedArmTag(e.type, emitter.unionsById);
6400
+ if (strTag < 0 || undefTag < 0)
6401
+ throw new InternalCompilerError("emitter bug: http.resStatusMsgGet union lacks its arms");
6402
+ const msg = emitter.newTemp(STRING, `scr_http_res_status_msg_get(${arg(0)})`);
6403
+ emitter.moveTemp(msg);
6404
+ const present = `scr_union_new_ref(${strTag}, ${msg.name}, &scr_str_retain_v, &scr_str_release_v, NULL)`;
6405
+ return emitter.newTemp(e.type, `${msg.name} ? ${present} : ${emitter.unitInstanceRef(e.type.unionId, undefTag)}`);
6406
+ }
6262
6407
  case "http.resStatusMsgSet":
6263
6408
  emitter.line(`scr_http_res_status_msg_set(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6264
6409
  return { name: "", type: e.type };
6265
- case "http.resGetHeader": {
6410
+ case "http.resRequest":
6411
+ return finish(`scr_http_res_request(${arg(0)})`);
6412
+ case "http.resSocket": {
6413
+ if (e.type.kind !== "union")
6414
+ throw new InternalCompilerError("emitter bug: http.resSocket result is not a union");
6415
+ const def = emitter.unionsById.get(e.type.unionId);
6416
+ const socketTag = def ? def.arms.findIndex((a) => a.kind === "netSocket") : -1;
6417
+ const nullTag = def ? def.arms.findIndex((a) => a.kind === "nullT") : -1;
6418
+ if (socketTag < 0 || nullTag < 0)
6419
+ throw new InternalCompilerError("emitter bug: http.resSocket union lacks its arms");
6420
+ const sock = emitter.newTemp(NETSOCKET_T, `scr_http_res_socket(${arg(0)})`);
6421
+ emitter.moveTemp(sock);
6422
+ const present = `scr_union_new_ref(${socketTag}, ${sock.name}, &scr_net_sock_retain_v, &scr_net_sock_release_v, NULL)`;
6423
+ return emitter.newTemp(e.type, `${sock.name} ? ${present} : ${emitter.unitInstanceRef(e.type.unionId, nullTag)}`);
6424
+ }
6425
+ case "http.resWritableFinished":
6426
+ return finish(`scr_http_res_writable_finished(${arg(0)})`);
6427
+ case "http.resSendDateGet":
6428
+ return finish(`scr_http_res_send_date(${arg(0)})`);
6429
+ case "http.resSendDateSet":
6430
+ emitter.line(`scr_http_res_set_send_date(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6431
+ return { name: "", type: e.type };
6432
+ case "http.resStrictContentLengthGet":
6433
+ return finish(`scr_http_res_strict_content_length(${arg(0)})`);
6434
+ case "http.resStrictContentLengthSet":
6435
+ emitter.line(`scr_http_res_set_strict_content_length(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6436
+ return { name: "", type: e.type };
6437
+ case "http.resSetTimeout":
6438
+ emitter.line(`scr_http_res_set_timeout_plain(${arg(0)}, ${arg(1)});${emitter.srcComment(e.loc)}`);
6439
+ return { name: "", type: e.type };
6440
+ case "http.resSetTimeoutCb": {
6441
+ const cb = args[2];
6442
+ emitter.moveTemp(cb);
6443
+ emitter.line(`scr_http_res_set_timeout(${arg(0)}, ${arg(1)}, ${cb.name});${emitter.srcComment(e.loc)}`);
6444
+ return { name: "", type: e.type };
6445
+ }
6446
+ case "http.resGetHeader":
6447
+ case "http.clientGetHeader": {
6266
6448
  // string|undefined, exactly the http.reqHeader emission.
6267
6449
  if (e.type.kind !== "union")
6268
- throw new InternalCompilerError("emitter bug: http.resGetHeader result is not a union");
6450
+ throw new InternalCompilerError(`emitter bug: ${e.fn} result is not a union`);
6269
6451
  const def = emitter.unionsById.get(e.type.unionId);
6270
6452
  const strTag = def ? def.arms.findIndex((a) => a.kind === "string") : -1;
6271
6453
  const undefTag = undefinedArmTag(e.type, emitter.unionsById);
6272
6454
  if (strTag < 0 || undefTag < 0) {
6273
6455
  throw new InternalCompilerError("emitter bug: http.resGetHeader union lacks its arms");
6274
6456
  }
6275
- const s = emitter.newTemp(STRING, `scr_http_res_get_header(${arg(0)}, ${arg(1)})`);
6457
+ const entry = e.fn === "http.resGetHeader" ? "scr_http_res_get_header" : "scr_http_client_get_header";
6458
+ const s = emitter.newTemp(STRING, `${entry}(${arg(0)}, ${arg(1)})`);
6276
6459
  emitter.moveTemp(s); // moves into the box when present; NULL otherwise
6277
6460
  const present = `scr_union_new_ref(${strTag}, ${s.name}, &scr_str_retain_v, &scr_str_release_v, NULL)`;
6278
6461
  const absent = emitter.unitInstanceRef(e.type.unionId, undefTag);
6279
6462
  return emitter.newTemp(e.type, `${s.name} ? ${present} : ${absent}`);
6280
6463
  }
6464
+ case "http.resGetHeaderNames":
6465
+ return finish(`scr_http_res_get_header_names(${arg(0)})`);
6466
+ case "http.resGetRawHeaderNames":
6467
+ return finish(`scr_http_res_get_raw_header_names(${arg(0)})`);
6468
+ case "http.resGetHeaders":
6469
+ return finish(`scr_http_res_get_headers(${arg(0)})`);
6281
6470
  case "http.resHasHeader":
6282
6471
  return finish(`scr_http_res_has_header_named(${arg(0)}, ${arg(1)})`);
6283
6472
  case "http.resRemoveHeader":
@@ -6295,6 +6484,14 @@ function emitHttpLibCall(state) {
6295
6484
  return finish(`scr_http_res_write_head_dyn(${arg(0)}, ${arg(1)}, ${arg(2)})`);
6296
6485
  case "http.reqSetEncoding":
6297
6486
  return finish(`scr_http_req_set_encoding(${arg(0)}, ${arg(1)})`);
6487
+ case "http.reqSetTimeout":
6488
+ return finish(`scr_http_req_set_timeout_plain(${arg(0)}, ${arg(1)})`);
6489
+ case "http.reqSetTimeoutCb": {
6490
+ const cb = args[2];
6491
+ emitter.moveTemp(cb);
6492
+ emitter.line(`scr_http_req_set_timeout(${arg(0)}, ${arg(1)}, ${cb.name});${emitter.srcComment(e.loc)}`);
6493
+ return { name: "", type: e.type };
6494
+ }
6298
6495
  // node:http, the client slice (scr_http.c over the net client).
6299
6496
  case "http.request":
6300
6497
  case "http.requestCb": {
@@ -6464,6 +6661,18 @@ function emitHttpLibCall(state) {
6464
6661
  }
6465
6662
  case "http.clientWrite":
6466
6663
  return finish(`scr_http_client_write_str(${arg(0)}, ${arg(1)})`);
6664
+ case "http.clientSetHeader":
6665
+ return finish(`scr_http_client_set_header(${arg(0)}, ${arg(1)}, ${arg(2)})`);
6666
+ case "http.clientHasHeader":
6667
+ return finish(`scr_http_client_has_header_named(${arg(0)}, ${arg(1)})`);
6668
+ case "http.clientRemoveHeader":
6669
+ return finish(`scr_http_client_remove_header(${arg(0)}, ${arg(1)})`);
6670
+ case "http.clientGetHeaderNames":
6671
+ return finish(`scr_http_client_get_header_names(${arg(0)})`);
6672
+ case "http.clientGetRawHeaderNames":
6673
+ return finish(`scr_http_client_get_raw_header_names(${arg(0)})`);
6674
+ case "http.clientGetHeaders":
6675
+ return finish(`scr_http_client_get_headers(${arg(0)})`);
6467
6676
  case "http.clientWriteBytes":
6468
6677
  return finish(`scr_http_client_write_bytes(${arg(0)}, ${arg(1)})`);
6469
6678
  case "http.clientEnd":
@@ -6487,9 +6696,48 @@ function emitHttpLibCall(state) {
6487
6696
  return finish(`scr_http_client_uncork(${arg(0)})`);
6488
6697
  case "http.clientWritableCorked":
6489
6698
  return finish(`scr_http_client_writable_corked(${arg(0)})`);
6699
+ case "http.clientMethod":
6700
+ return finish(`scr_http_client_method(${arg(0)})`);
6701
+ case "http.clientPath":
6702
+ return finish(`scr_http_client_path(${arg(0)})`);
6703
+ case "http.clientHost":
6704
+ return finish(`scr_http_client_host(${arg(0)})`);
6705
+ case "http.clientProtocol":
6706
+ return finish(`scr_http_client_protocol(${arg(0)})`);
6707
+ case "http.clientHeadersSent":
6708
+ return finish(`scr_http_client_headers_sent(${arg(0)})`);
6709
+ case "http.clientWritableEnded":
6710
+ return finish(`scr_http_client_writable_ended(${arg(0)})`);
6711
+ case "http.clientWritableFinished":
6712
+ return finish(`scr_http_client_writable_finished(${arg(0)})`);
6713
+ case "http.clientSocket":
6714
+ return finish(`scr_http_client_socket(${arg(0)})`);
6715
+ case "http.clientReusedSocket":
6716
+ return finish(`scr_http_client_reused_socket(${arg(0)})`);
6717
+ case "http.clientSetNoDelay":
6718
+ return finish(`scr_http_client_set_nodelay(${arg(0)}, ${arg(1)})`);
6719
+ case "http.clientSetSocketKeepAlive":
6720
+ return finish(`scr_http_client_set_socket_keepalive(${arg(0)}, ${arg(1)}, ${arg(2)})`);
6721
+ case "http.clientSetTimeout":
6722
+ return finish(`scr_http_client_set_timeout(${arg(0)}, ${arg(1)})`);
6723
+ case "http.clientSetTimeoutCb": {
6724
+ const cb = args[2];
6725
+ emitter.moveTemp(cb);
6726
+ emitter.line(`scr_http_client_set_timeout_cb(${arg(0)}, ${arg(1)}, ${cb.name});${emitter.srcComment(e.loc)}`);
6727
+ return { name: "", type: e.type };
6728
+ }
6729
+ case "http.statusCodes":
6730
+ return finish("scr_http_status_codes()");
6731
+ case "http.methods":
6732
+ return finish("scr_http_methods()");
6490
6733
  case "http.clientDestroy":
6491
6734
  emitter.line(`scr_http_client_destroy(${arg(0)});${emitter.srcComment(e.loc)}`);
6492
6735
  return { name: "", type: e.type };
6736
+ case "http.clientAbort":
6737
+ emitter.line(`scr_http_client_abort(${arg(0)});${emitter.srcComment(e.loc)}`);
6738
+ return { name: "", type: e.type };
6739
+ case "http.clientAborted":
6740
+ return finish(`scr_http_client_aborted(${arg(0)})`);
6493
6741
  case "http.clientDestroyed":
6494
6742
  return finish(`scr_http_client_destroyed(${arg(0)})`);
6495
6743
  case "http.clientOnResponse": {
@@ -6502,6 +6750,16 @@ function emitHttpLibCall(state) {
6502
6750
  emitter.line(`scr_http_client_on_response(${arg(0)}, ${cb.name}, &${adapter}, ${arg(2)});${emitter.srcComment(e.loc)}`);
6503
6751
  return { name: "", type: e.type };
6504
6752
  }
6753
+ case "http.clientOnSocket": {
6754
+ const cbT = e.args[1].type;
6755
+ if (cbT.kind !== "func")
6756
+ throw new InternalCompilerError("emitter bug: http.clientOnSocket callback not a func");
6757
+ const cb = args[1];
6758
+ emitter.moveTemp(cb);
6759
+ const adapter = cbT.params.length === 0 ? "scr_net_conn_thunk0" : "scr_net_conn_thunk_sock";
6760
+ emitter.line(`scr_http_client_on_socket(${arg(0)}, ${cb.name}, &${adapter}, ${arg(2)});${emitter.srcComment(e.loc)}`);
6761
+ return { name: "", type: e.type };
6762
+ }
6505
6763
  case "http.clientOnError": {
6506
6764
  const cbT = e.args[1].type;
6507
6765
  if (cbT.kind !== "func")
@@ -6513,10 +6771,14 @@ function emitHttpLibCall(state) {
6513
6771
  return { name: "", type: e.type };
6514
6772
  }
6515
6773
  case "http.clientOnTimeout":
6516
- case "http.clientOnClose": {
6774
+ case "http.clientOnClose":
6775
+ case "http.clientOnFinish":
6776
+ case "http.clientOnAbort": {
6517
6777
  const cb = args[1];
6518
6778
  emitter.moveTemp(cb);
6519
- const fn = e.fn === "http.clientOnTimeout" ? "scr_http_client_on_timeout" : "scr_http_client_on_close";
6779
+ const fn = e.fn === "http.clientOnTimeout" ? "scr_http_client_on_timeout"
6780
+ : e.fn === "http.clientOnClose" ? "scr_http_client_on_close"
6781
+ : e.fn === "http.clientOnAbort" ? "scr_http_client_on_abort" : "scr_http_client_on_finish";
6520
6782
  emitter.line(`${fn}(${arg(0)}, ${cb.name}, ${arg(2)});${emitter.srcComment(e.loc)}`);
6521
6783
  return { name: "", type: e.type };
6522
6784
  }
@@ -7438,22 +7700,23 @@ function emitProcessLibCall(state) {
7438
7700
  return finish(`scr_stdin_next_chunk()`);
7439
7701
  case "process.isTTY":
7440
7702
  return finish(`scr_process_is_tty(${arg(0)})`);
7441
- case "process.columns": {
7442
- // ioctl(TIOCGWINSZ): a non-negative width wraps the f64 arm;
7703
+ case "process.columns":
7704
+ case "process.rows": {
7705
+ // ioctl(TIOCGWINSZ): a non-negative dimension wraps the f64 arm;
7443
7706
  // a non-TTY stream (or an ioctl refusal) comes back negative
7444
7707
  // and yields the interned undefined-arm instance — Node's
7445
- // missing `.columns`. Type-directed union construction, like
7708
+ // missing geometry. Type-directed union construction, like
7446
7709
  // process.envGet.
7447
7710
  if (e.type.kind !== "union") {
7448
- throw new InternalCompilerError("emitter bug: process.columns result is not a union");
7711
+ throw new InternalCompilerError(`emitter bug: ${e.fn} result is not a union`);
7449
7712
  }
7450
7713
  const def = emitter.unionsById.get(e.type.unionId);
7451
7714
  const f64Tag = def ? def.arms.findIndex((a) => a.kind === "f64") : -1;
7452
7715
  const undefTag = undefinedArmTag(e.type, emitter.unionsById);
7453
7716
  if (f64Tag < 0 || undefTag < 0) {
7454
- throw new InternalCompilerError("emitter bug: process.columns union lacks its arms");
7717
+ throw new InternalCompilerError(`emitter bug: ${e.fn} union lacks its arms`);
7455
7718
  }
7456
- const w = emitter.newTemp(F64, `scr_process_columns(${arg(0)})`);
7719
+ const w = emitter.newTemp(F64, `scr_process_${e.fn === "process.rows" ? "rows" : "columns"}(${arg(0)})`);
7457
7720
  const present = `scr_union_new_f64(${f64Tag}, ${w.name})`;
7458
7721
  const absent = emitter.unitInstanceRef(e.type.unionId, undefTag);
7459
7722
  return emitter.newTemp(e.type, `${w.name} >= 0 ? ${present} : ${absent}`);
@@ -8291,6 +8554,14 @@ function emitIoLibCall(state) {
8291
8554
  }
8292
8555
  }
8293
8556
  function emitLibCallExpr(emitter, e) {
8557
+ const packed = e.args[0];
8558
+ if (e.fn === "string.fromCharCode" && packed?.kind === "arrayLit" &&
8559
+ packed.elems.length === 1 && !packed.spreads?.length) {
8560
+ // The literal is only the variadic ABI pack, never an observable
8561
+ // array. Evaluate the scalar once before bypassing that allocation.
8562
+ const code = emitter.emitExpr(packed.elems[0]);
8563
+ return emitter.newTemp(e.type, `scr_str_from_char_code_one(${code.name})`);
8564
+ }
8294
8565
  // Standard-library call. Args are BORROWED (owned temps of the
8295
8566
  // current frame, released at statement end); refcounted results come
8296
8567
  // back +1 (process.argv: +1 on the runtime's ONE interned array —