@amritk/nish 0.12.0 → 0.14.0

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 (42) hide show
  1. package/README.md +27 -23
  2. package/bin/launcher.js +45 -41
  3. package/bin/packaging.js +39 -33
  4. package/docs/AI.md +157 -35
  5. package/docs/INSTALL.md +13 -13
  6. package/llms.txt +1 -1
  7. package/package.json +9 -6
  8. package/runtime/nish.d.ts +85 -1
  9. package/runtime/nish.h +62 -11
  10. package/runtime/nish.mjs +38 -3
  11. package/runtime/runtime-host.c +178 -0
  12. package/runtime/{runtime_os.c → runtime-os.c} +16 -1
  13. package/runtime/{runtime_parallel.c → runtime-parallel.c} +112 -4
  14. package/runtime/{runtime_wasm.c → runtime-wasm.c} +6 -1
  15. package/runtime/runtime.c +5 -5
  16. package/runtime/shim.mjs +128 -6
  17. package/scripts/bootstrap.sh +29 -29
  18. package/scripts/build.sh +19 -13
  19. package/scripts/changelog-gen.mjs +260 -192
  20. package/scripts/ci-profile.mjs +80 -68
  21. package/scripts/codes-registry.js +25 -13
  22. package/scripts/gen-diagnostic-codes.mjs +124 -85
  23. package/scripts/nish-compiler.sh +2 -0
  24. package/scripts/platform-package.mjs +26 -26
  25. package/scripts/postinstall.mjs +46 -36
  26. package/scripts/size-report.sh +3 -3
  27. package/scripts/smoke.sh +1 -1
  28. package/std/README.md +42 -2
  29. package/std/collections.ts +194 -188
  30. package/std/crypto/base64url.ts +145 -0
  31. package/std/crypto/ct.ts +64 -0
  32. package/std/crypto/hkdf.ts +118 -0
  33. package/std/crypto/hmac.ts +155 -0
  34. package/std/crypto/sha256.ts +444 -0
  35. package/std/crypto/sha512.ts +510 -0
  36. package/std/crypto/x25519.ts +494 -0
  37. package/std/json.ts +136 -136
  38. package/std/map.ts +9 -7
  39. package/std/pair.ts +2 -2
  40. package/std/testing.ts +67 -67
  41. package/std/text.ts +54 -54
  42. package/std/threads.ts +126 -38
package/runtime/shim.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  * The differential runner (tests/differential/run.js) rewrites an Nish
5
5
  * program into plain JavaScript and runs it under Node with this module as
6
6
  * `__nish`. Every helper here reproduces the *runtime* semantics the compiled
7
- * binary has (runtime/runtime.c and runtime/runtime_os.c, the system-call half,
7
+ * binary has (runtime/runtime.c and runtime/runtime-os.c, the system-call half,
8
8
  * plus the intrinsics in docs/wp7-runtime.md) where JavaScript's own semantics
9
9
  * differ:
10
10
  *
@@ -42,6 +42,7 @@
42
42
  * The rewrite rules that call these helpers are listed in docs/wp13-differential.md.
43
43
  */
44
44
  import child_process from "node:child_process";
45
+ import { webcrypto } from "node:crypto";
45
46
  import fs from "node:fs";
46
47
  import os from "node:os";
47
48
 
@@ -91,6 +92,37 @@ export function bitsToF64(b) {
91
92
  return BITS.getFloat64(0);
92
93
  }
93
94
 
95
+ /**
96
+ * `ctSelect` / `ctEq` (WP34 N6). A `u32` is a `number` here and a `u64` a
97
+ * BigInt, so the operands' kind picks the width, and a mix of the two — which
98
+ * the native checker refuses, and which a `u64` written as a bare literal is
99
+ * under an unrewritten run — throws the `TypeError` BigInt arithmetic throws
100
+ * rather than comparing a number with a BigInt and answering zero. JavaScript's
101
+ * `&` reads a `number` as a signed 32-bit integer, so each answer is put back in
102
+ * range with `>>> 0` or `asUintN(64, ...)`. These branch: only the native
103
+ * lowering promises constant time.
104
+ */
105
+ function ctWide(name, first, second, third) {
106
+ const wide = typeof first === "bigint";
107
+ if ((typeof second === "bigint") !== wide || (typeof third === "bigint") !== wide) {
108
+ throw new TypeError(`${name}: cannot mix a u64 (BigInt) with a u32 (number)`);
109
+ }
110
+ return wide;
111
+ }
112
+
113
+ const U64_ONES = (1n << 64n) - 1n;
114
+
115
+ export function ctSelect(mask, a, b) {
116
+ if (ctWide("ctSelect", mask, a, b)) return wrapU64((a & mask) | (b & ~mask));
117
+ return ((a & mask) | (b & ~mask)) >>> 0;
118
+ }
119
+
120
+ export function ctEq(a, b) {
121
+ // `ctEq` has two operands, so the second one stands in for the third.
122
+ if (ctWide("ctEq", a, b, b)) return wrapU64(a ^ b) === 0n ? U64_ONES : 0n;
123
+ return (a ^ b) === 0 ? 0xffffffff : 0;
124
+ }
125
+
94
126
  /** Wrap a BigInt to the i64 range: every i64 `+ - * /` and unary minus goes through here. */
95
127
  export function wrapI64(x) {
96
128
  return BigInt.asIntN(64, x);
@@ -401,6 +433,28 @@ export function updIdx(a, i, f) {
401
433
  return v;
402
434
  }
403
435
 
436
+ /**
437
+ * `dst.set(src, offset)` (WP34 N2): `TypedArray.prototype.set`'s copy on the
438
+ * plain array a `u8[]` is here. The source is copied first, so a self-copy or
439
+ * an overlapping one reads what was there before, as `memmove` does natively;
440
+ * a range past the end fails with the native panic and its words, where a
441
+ * typed array would throw a `RangeError` for the same offsets.
442
+ */
443
+ export function arraySet(dst, src, offset) {
444
+ // `ToIntegerOrInfinity`: NaN is 0, as `llvm.fptosi.sat` makes it natively.
445
+ // `Math.trunc` rather than `toIndex`, which converts a bigint: an `i64` or
446
+ // `u64` offset is a bigint here, and it throws the `TypeError` the typed
447
+ // array and `Array.prototype.fill` throw for one, instead of being rounded
448
+ // to the nearest double past 2^53 (docs/RUN_UNDER_NODE.md).
449
+ const at = offset === undefined ? 0 : Math.trunc(offset) || 0;
450
+ const end = at + src.length;
451
+ if (!(at >= 0 && end <= dst.length)) panicSlice(at, end, dst.length);
452
+ // Two plain arrays overlap only when they are one array, which is the one
453
+ // case that must read the source before writing it.
454
+ const from = src === dst ? src.slice() : src;
455
+ for (let i = 0; i < from.length; i++) dst[at + i] = from[i];
456
+ }
457
+
404
458
  /** `new Array<T>(n)`: `n` zero-filled elements (`0`, `0n`, or `false`). */
405
459
  export function newArray(n, zero) {
406
460
  return new Array(toIndex(n)).fill(zero);
@@ -434,6 +488,15 @@ export function readFileSyncOrNull(path) {
434
488
  }
435
489
  }
436
490
 
491
+ /** `readFileBytesSync(path)` (WP34 N2): the bytes as a plain array of numbers, or null. */
492
+ export function readFileBytesSync(path) {
493
+ try {
494
+ return Array.from(fs.readFileSync(path));
495
+ } catch {
496
+ return null;
497
+ }
498
+ }
499
+
437
500
  export function writeFileSync(path, data) {
438
501
  try {
439
502
  fs.writeFileSync(path, data, "utf8");
@@ -486,10 +549,10 @@ export function isDirectorySync(path) {
486
549
  * the directory cannot be read. Node throws where the runtime answers a value,
487
550
  * so the `catch` is what makes the two agree, and a directory that exists and
488
551
  * is empty answers an empty array on both sides. Node's readdir never yields
489
- * `.` or `..` — the pair `runtime_os.c` skips explicitly — so there is nothing
552
+ * `.` or `..` — the pair `runtime-os.c` skips explicitly — so there is nothing
490
553
  * to filter out here.
491
554
  *
492
- * The sort is the semantic point. `runtime_os.c` orders the names with
555
+ * The sort is the semantic point. `runtime-os.c` orders the names with
493
556
  * `strcmp`, which compares UTF-8 bytes, and `Array#sort` compares UTF-16 code
494
557
  * units. The two agree on ASCII names and part company above the BMP, where a
495
558
  * surrogate pair sorts below `U+E000`..`U+FFFF` in UTF-16 and above them in
@@ -526,7 +589,7 @@ export function realpathSync(path) {
526
589
 
527
590
  /**
528
591
  * `process.platform` / `process.arch` (WP14 §7a). Node's spellings are the
529
- * ones `runtime_os.c` answers with, so on any machine this compiler has a
592
+ * ones `runtime-os.c` answers with, so on any machine this compiler has a
530
593
  * triple for the two runtimes give the same string; elsewhere the native build
531
594
  * says `unknown` where Node names the platform, which is the one place they
532
595
  * part.
@@ -553,7 +616,7 @@ export function getenv(name) {
553
616
  * `spawnSync(argv)` and `spawnSyncTo(argv, out, err)` (WP14 D4), which are one
554
617
  * run with its streams answered differently: the child's exit status, 128 + n
555
618
  * when signal n killed it, -1 for an empty vector or a program that would not
556
- * start. `runtime_os.c` puts one `static nish_spawn_impl` behind both builtins
619
+ * start. `runtime-os.c` puts one `static nish_spawn_impl` behind both builtins
557
620
  * for the same reason this module puts one function behind both helpers — the
558
621
  * argument vector, the wait and the signal convention are written once and
559
622
  * cannot drift between the two.
@@ -606,7 +669,7 @@ export function spawnSyncTo(argv, out, err) {
606
669
  /**
607
670
  * `monotonicNanos()`: `process.hrtime.bigint()`, a monotonic clock in
608
671
  * nanoseconds (`CLOCK_MONOTONIC` on every platform that has it, which is the
609
- * one `runtime_os.c` reads). The value is a BigInt because that is how an `i64`
672
+ * one `runtime-os.c` reads). The value is a BigInt because that is how an `i64`
610
673
  * is held on this side.
611
674
  *
612
675
  * No rewrite can make a *reading* agree with a native run: both origins are
@@ -618,6 +681,65 @@ export function monotonicNanos() {
618
681
  return process.hrtime.bigint();
619
682
  }
620
683
 
684
+ // ---- The host (WP34 N3) ------------------------------------------------------
685
+
686
+ /**
687
+ * `statMtimeSync(path)`: Node's `mtimeMs` for the path, or NaN when it cannot
688
+ * be stat'd, which is the native answer too. `mtimeMs` is the same arithmetic
689
+ * `runtime-host.c` does, so the two print the same digits, fraction and all.
690
+ */
691
+ export function statMtimeSync(path) {
692
+ const st = fs.statSync(path, { throwIfNoEntry: false });
693
+ if (st === undefined) {
694
+ return Number.NaN;
695
+ }
696
+ return st.mtimeMs;
697
+ }
698
+
699
+ /**
700
+ * Node's own fill, taken before `runtime/nish.mjs` puts the one below in its
701
+ * place on the same object: `webcrypto` is the global `crypto`.
702
+ */
703
+ const webRandom = webcrypto.getRandomValues.bind(webcrypto);
704
+
705
+ /**
706
+ * `crypto.getRandomValues(bytes)` for the plain array a `u8[]` is here. Node's
707
+ * own takes only a typed array, so the bytes are drawn into one and copied
708
+ * across. More than 65,536 fails with the native panic and its words, where
709
+ * Node would throw a `QuotaExceededError`: the exit status is 1 either way. A
710
+ * typed array goes straight through.
711
+ */
712
+ export function getRandomValues(bytes) {
713
+ if (!Array.isArray(bytes)) {
714
+ return webRandom(bytes);
715
+ }
716
+ if (bytes.length > 65536) {
717
+ panic(`crypto.getRandomValues: ${bytes.length} bytes asked for, and one call fills at most 65536`);
718
+ }
719
+ const drawn = webRandom(new Uint8Array(bytes.length));
720
+ for (let i = 0; i < drawn.length; i++) {
721
+ bytes[i] = drawn[i];
722
+ }
723
+ return bytes;
724
+ }
725
+
726
+ /**
727
+ * `signalFd()` and `readSignal(fd)` have no faithful reading under Node, and
728
+ * these say so rather than answer something else. Node delivers a signal to
729
+ * its event loop (`process.on("SIGTERM")`), and a blocking read keeps the loop
730
+ * from ever running, so no synchronous function here can learn that one
731
+ * arrived. docs/wp33-round-trip.md §3.5 has the row and the translation.
732
+ */
733
+ export function signalFd() {
734
+ throw new Error(
735
+ "signalFd has no synchronous reading under Node: a signal reaches the event loop, which a blocking readSignal never returns to (docs/wp33-round-trip.md)"
736
+ );
737
+ }
738
+
739
+ export function readSignal() {
740
+ return signalFd();
741
+ }
742
+
621
743
  /** `process.argv`: index 0 is the program (the script here, the executable natively), then the arguments. */
622
744
  export function argv() {
623
745
  return process.argv.slice(1);
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Build the self-hosted compiler: `self/`, compiled by `self/`.
2
+ # Build the self-hosted compiler: `src/`, compiled by `src/`.
3
3
  #
4
4
  # scripts/bootstrap.sh [-o <exe>] [--stages 1|2|3] [--profile speed|size|debug]
5
5
  # [--work <dir>] [--verify] [--quiet]
@@ -8,9 +8,9 @@
8
8
  # parameter rather than a fixture (docs/wp19-stage0-retirement.md §3, G3):
9
9
  #
10
10
  # seed whatever compiles stage1 NISH_BOOTSTRAP, or build/seed
11
- # stage1 self/, built by the seed
12
- # stage2 self/, built by stage1 the default output
13
- # stage3 self/, built by stage2 --verify only
11
+ # stage1 src/, built by the seed
12
+ # stage2 src/, built by stage1 the default output
13
+ # stage3 src/, built by stage2 --verify only
14
14
  #
15
15
  # NISH_BOOTSTRAP=<path> names the seed, the way GOROOT_BOOTSTRAP names the Go
16
16
  # that builds Go. It is either a released `nish`, executed directly, or a Node
@@ -31,14 +31,14 @@
31
31
  #
32
32
  # --verify runs the equalities the proof is made of, byte for byte:
33
33
  #
34
- # IR(seed, self/) == IR(stage1, self/) reported, never asserted
35
- # IR(stage1, self/) == IR(stage2, self/) the fixed point: self-hosted
34
+ # IR(seed, src/) == IR(stage1, src/) reported, never asserted
35
+ # IR(stage1, src/) == IR(stage2, src/) the fixed point: self-hosted
36
36
  # stage3 == stage2 as files, on ELF and on Mach-O
37
37
  # alike; see below for what the
38
38
  # Mach-O half took
39
39
  #
40
40
  # The last two are properties of the working tree and of nothing else: whatever
41
- # built stage1, the compiler `self/` describes has to agree with itself and
41
+ # built stage1, the compiler `src/` describes has to agree with itself and
42
42
  # then reproduce itself. Both are asserted whatever the seed is.
43
43
  #
44
44
  # The third is a raw byte comparison, and on Mach-O two links of the same input
@@ -59,22 +59,22 @@
59
59
  # measurement, byte offsets and all.
60
60
  #
61
61
  # The first one is reported and not asserted. With a released seed it is one
62
- # implementation at two points in time: the IR HEAD emits for `self/` is the
62
+ # implementation at two points in time: the IR HEAD emits for `src/` is the
63
63
  # IR the last release emitted for it. Nothing in that sentence is about
64
64
  # bootstrapping. It is a freeze on codegen between releases, and it fails on
65
65
  # exactly the changes a release cycle exists to carry: the first improvement to
66
- # land broke it, when a flow-sensitive bounds analysis proved 46 of `self/`'s
66
+ # land broke it, when a flow-sensitive bounds analysis proved 46 of `src/`'s
67
67
  # 1,206 index checks redundant and 20 of 56 modules "differed" because the
68
68
  # optimisation worked. So the difference is reported, and the report is
69
69
  # information about this release rather than a verdict on the bootstrap.
70
70
  #
71
- # While `src/` existed, a stage0 seed made the same comparison the second half
71
+ # While stage0's `src/` existed, a stage0 seed made the same comparison the second half
72
72
  # of Wheeler's diverse double-compiling, and it was asserted there
73
- # (docs/wp19-stage0-retirement.md §1, G6). That claim went with `src/`.
73
+ # (docs/wp19-stage0-retirement.md §1, G6). That claim went with stage0's `src/`.
74
74
  #
75
75
  # What the seeded run buys is not that equality. The rolling freeze — "a
76
- # construct added in 0.N cannot be used by `self/` until 0.(N+1)" — is enforced
77
- # by stage1 being built at all: a `self/` that reaches for something the seed
76
+ # construct added in 0.N cannot be used by `src/` until 0.(N+1)" — is enforced
77
+ # by stage1 being built at all: a `src/` that reaches for something the seed
78
78
  # has never heard of does not compile, does not link, and never gets as far as
79
79
  # a comparison. That failure is loud here whatever the seed is, and it is the
80
80
  # whole of what the seeded run proves about the freeze
@@ -115,7 +115,7 @@ usage: scripts/bootstrap.sh [-o <exe>] [--stages 1|2|3] [--profile speed|size|de
115
115
 
116
116
  Builds the self-hosted compiler. The seed builds stage1, stage1 builds stage2
117
117
  (the default output), stage2 builds stage3. --verify compares the IR each stage
118
- emits for self/ and the stage2/stage3 binaries, byte for byte. The result is
118
+ emits for src/ and the stage2/stage3 binaries, byte for byte. The result is
119
119
  the command line itself: -o, --link, --profile and the rest.
120
120
 
121
121
  IR(seed) == IR(stage1) asks whether codegen has changed since the seed was
@@ -156,10 +156,10 @@ build_to=$stages
156
156
  [ "$verify" -eq 1 ] && build_to=3
157
157
 
158
158
  # The published npm package ships bin/, runtime/, scripts/ and std/ but not
159
- # self/ -- and, since 0.6.0, no compiler of its own at all -- so say which file
159
+ # src/ -- and, since 0.6.0, no compiler of its own at all -- so say which file
160
160
  # is missing rather than failing inside the compiler.
161
- if [ ! -f self/compile.ts ]; then
162
- echo "bootstrap: self/compile.ts is missing; run this from a checkout of the repository" >&2
161
+ if [ ! -f src/compile.ts ]; then
162
+ echo "bootstrap: src/compile.ts is missing; run this from a checkout of the repository" >&2
163
163
  exit 3
164
164
  fi
165
165
 
@@ -211,7 +211,7 @@ else
211
211
  [ -r "$seed" ] || seed_die "is not readable"
212
212
  command -v node >/dev/null 2>&1 || seed_die "needs node on PATH, which is not there"
213
213
  fi
214
- # A seed that cannot answer `--version` cannot compile self/ either — a
214
+ # A seed that cannot answer `--version` cannot compile src/ either — a
215
215
  # binary built for another platform, a .js that is not a compiler — and
216
216
  # finding that out here names the variable and the path the caller set, where
217
217
  # finding it out in the stage1 link names a temporary file three stages deep.
@@ -223,7 +223,7 @@ fi
223
223
 
224
224
  say() { [ "$quiet" -eq 1 ] || printf '%s\n' "$*"; }
225
225
 
226
- # `a` and `b` hold one `.ll` per module of `self/`. Both the module set and
226
+ # `a` and `b` hold one `.ll` per module of `src/`. Both the module set and
227
227
  # every byte of every module must match: a stage that emitted one module fewer
228
228
  # has not agreed about the rest.
229
229
  compare_ir() {
@@ -260,11 +260,11 @@ survey_ir() {
260
260
  done
261
261
  if [ "$differing" -eq 0 ]; then
262
262
  say " note: IR(seed) vs IR(stage1): all $total modules identical."
263
- say " Not asserted: codegen simply has not moved for self/ since"
263
+ say " Not asserted: codegen simply has not moved for src/ since"
264
264
  say " the seed was built."
265
265
  else
266
266
  say " note: IR(seed) vs IR(stage1): $differing of $total modules differ."
267
- say " Not a bootstrap failure. Codegen has moved for self/ since the"
267
+ say " Not a bootstrap failure. Codegen has moved for src/ since the"
268
268
  say " seed was built, which is what a release carries. See the header"
269
269
  say " of this script and docs/wp19-stage0-retirement.md §3, G3."
270
270
  fi
@@ -295,8 +295,8 @@ survey_ir() {
295
295
  link_stage() {
296
296
  local by="$1" name="$2"
297
297
  rm -rf "$work/stage" "$work/stage.modules"
298
- "$by" self/compile.ts --link "$work/stage" --profile "$profile" >/dev/null
299
- rm -rf "$work/$name" "$work/$name.modules"
298
+ "$by" src/compile.ts --link "$work/stage" --profile "$profile" >/dev/null
299
+ rm -rf "${work:?}/${name:?}" "${work:?}/${name:?}.modules"
300
300
  mv "$work/stage" "$work/$name"
301
301
  mv "$work/stage.modules" "$work/$name.modules"
302
302
  }
@@ -310,28 +310,28 @@ outdir=$(dirname "$out")
310
310
  # `scripts/build.sh` itself (docs/wp14-selfhost.md §7a). That leaves each
311
311
  # stage's IR in `<exe>.modules/`, which is where the equalities read it, and
312
312
  # means the chain exercises the same driver a user does.
313
- say "stage1: self/ compiled by $seed_label"
313
+ say "stage1: src/ compiled by $seed_label"
314
314
  rm -rf "$work/stage1.modules"
315
315
  # This is the step the rolling freeze is enforced by, so it says so when it
316
316
  # fails rather than leaving a reader with the compiler's own diagnostic and no
317
317
  # idea which rule it just met.
318
- if ! run_seed self/compile.ts --link "$work/stage1" --profile "$profile" >/dev/null; then
319
- echo "bootstrap: $seed_label could not build stage1 from self/ (its output is above)" >&2
318
+ if ! run_seed src/compile.ts --link "$work/stage1" --profile "$profile" >/dev/null; then
319
+ echo "bootstrap: $seed_label could not build stage1 from src/ (its output is above)" >&2
320
320
  echo "bootstrap: if it refused the source, that is the freeze doing its job: a construct" >&2
321
- echo "bootstrap: added in 0.N cannot be used by self/ until 0.(N+1) (docs/wp12-release.md," >&2
321
+ echo "bootstrap: added in 0.N cannot be used by src/ until 0.(N+1) (docs/wp12-release.md," >&2
322
322
  echo "bootstrap: \"The bootstrap seed\"; docs/wp19-stage0-retirement.md §3, G3)" >&2
323
323
  exit 1
324
324
  fi
325
325
 
326
326
  if [ "$build_to" -ge 2 ]; then
327
- say "stage2: self/ compiled by stage1"
327
+ say "stage2: src/ compiled by stage1"
328
328
  link_stage "$work/stage1" stage2
329
329
  # The seed equality, reported and not asserted.
330
330
  [ "$verify" -eq 1 ] && survey_ir "$work/stage1.modules" "$work/stage2.modules"
331
331
  fi
332
332
 
333
333
  if [ "$build_to" -ge 3 ]; then
334
- say "stage3: self/ compiled by stage2"
334
+ say "stage3: src/ compiled by stage2"
335
335
  link_stage "$work/stage2" stage3
336
336
  [ "$verify" -eq 1 ] && compare_ir "$work/stage2.modules" "$work/stage3.modules" "IR(stage1) == IR(stage2)"
337
337
  # `stage3 == stage2`, in scripts/verify-binaries.sh: byte-identical, which
package/scripts/build.sh CHANGED
@@ -3,11 +3,12 @@
3
3
  #
4
4
  # scripts/build.sh <module.ll> [more .ll/.c files...] -o <out> [--profile debug|speed|size|wasm]
5
5
  #
6
- # The C runtime is three translation units and is named as one: an input
7
- # <dir>/runtime.c also compiles <dir>/runtime_os.c, the half that wraps the
6
+ # The C runtime is four translation units and is named as one: an input
7
+ # <dir>/runtime.c also compiles <dir>/runtime-os.c, the half that wraps the
8
8
  # system calls (files, directories, subprocesses, the environment, the clock),
9
- # and <dir>/runtime_parallel.c, the half that divides a range of work across
10
- # threads. Each of those files says why they are compiled and measured apart.
9
+ # <dir>/runtime-parallel.c, the half that divides a range of work across
10
+ # threads, and <dir>/runtime-host.c, the wall clock, entropy, file times and
11
+ # signals. Each of those files says why they are compiled and measured apart.
11
12
  #
12
13
  # Profiles:
13
14
  # debug clang defaults: no optimisation, symbols kept. The "before" number.
@@ -15,7 +16,7 @@
15
16
  # size -Oz + LTO + section GC + strip + no unwind tables. Rust
16
17
  # `opt-level="z"`, `panic="abort"`, `strip=true` equivalent.
17
18
  # wasm wasm32 freestanding module exporting every non-internal function;
18
- # load it from Node. Add runtime/runtime_wasm.c to the inputs when a
19
+ # load it from Node. Add runtime/runtime-wasm.c to the inputs when a
19
20
  # function uses arrays (the arena and the array cold paths, no libc);
20
21
  # strings and I/O still need a WASI runtime and are not available.
21
22
  # wasm wasm32 freestanding module exporting every non-internal function
@@ -83,13 +84,13 @@ done
83
84
  [ ${#inputs[@]} -gt 0 ] || { echo "error: no input files" >&2; exit 2; }
84
85
  [ -n "$out" ] || { echo "error: -o <out> is required" >&2; exit 2; }
85
86
 
86
- # The runtime is three translation units, and a caller names one: whoever passes
87
- # <dir>/runtime.c gets <dir>/runtime_os.c and <dir>/runtime_parallel.c compiled
88
- # beside it. They were one file until the operating-system half was split out
87
+ # The runtime is four translation units, and a caller names one: whoever passes
88
+ # <dir>/runtime.c gets <dir>/runtime-os.c, <dir>/runtime-parallel.c and
89
+ # <dir>/runtime-host.c compiled beside it. They were one file until the operating-system half was split out
89
90
  # for its own size budget, and the parallel half followed for the same reason
90
91
  # (each file's header comment says why), and a link line is where those splits
91
92
  # would otherwise leak: `nish --link` builds its command line in
92
- # self/compile.ts, the published package's recipe in every document and
93
+ # src/compile.ts, the published package's recipe in every document and
93
94
  # README names runtime.c, and a user's own clang line does too. Pairing them
94
95
  # here keeps every one of those correct, and keeps "the runtime" one thing to
95
96
  # name from the outside. A caller that names one itself is left alone, because
@@ -97,7 +98,7 @@ done
97
98
  for i in ${inputs[@]+"${inputs[@]}"}; do
98
99
  case "$i" in
99
100
  */runtime.c|runtime.c)
100
- for half in runtime_os.c runtime_parallel.c; do
101
+ for half in runtime-os.c runtime-parallel.c runtime-host.c; do
101
102
  side="${i%runtime.c}$half"
102
103
  have=0
103
104
  for j in "${inputs[@]}"; do
@@ -133,8 +134,12 @@ case "$(uname -s)" in
133
134
  # of one input to one output path, and differing on a link to another. That
134
135
  # is what scripts/bootstrap.sh links every comparable stage at one path for,
135
136
  # and it is why `stage3 == stage2` holds on Mach-O with the load command in.
136
- gc=(-Wl,-dead_strip); strip_flag=(-Wl,-x) ;;
137
+ # shellcheck disable=SC2054 # -Wl,<flag> is one argument: the comma is the linker's
138
+ gc=(-Wl,-dead_strip)
139
+ # shellcheck disable=SC2054
140
+ strip_flag=(-Wl,-x) ;;
137
141
  *)
142
+ # shellcheck disable=SC2054 # -Wl,<flag> is one argument: the comma is the linker's
138
143
  gc=(-Wl,--gc-sections -Wl,--as-needed -Wl,-O2 -Wl,--build-id=none); strip_flag=(-s)
139
144
  elf=(-fno-plt)
140
145
  # GNU ld needs the gold plugin for LTO; prefer lld when clang can find it.
@@ -158,7 +163,7 @@ if [ "$debug" = 1 ]; then common+=(-g); strip_flag=(); fi
158
163
  # own command line instead of using `common`; it is empty on every ordinary
159
164
  # build, hence the bash 3.2 expansion spelling explained below.
160
165
  #
161
- # -pthread is for runtime_parallel.c, the translation unit that divides a range
166
+ # -pthread is for runtime-parallel.c, the translation unit that divides a range
162
167
  # of work across threads: it is compiled in either configuration and only spawns
163
168
  # under this macro, so this is the build where the flag has to be on the command
164
169
  # line. On a current glibc the library half is already inside libc and the link
@@ -167,7 +172,7 @@ if [ "$debug" = 1 ]; then common+=(-g); strip_flag=(); fi
167
172
  #
168
173
  # It is deliberately in `common` and not in `tls`: `tls` is the wasm and wasi
169
174
  # command lines, which have no threads to link against, and where
170
- # runtime_parallel.c compiles to its sequential fallback because it tests
175
+ # runtime-parallel.c compiles to its sequential fallback because it tests
171
176
  # __wasi__ and __wasm__ as well as the macro.
172
177
  tls=()
173
178
  if [ "$threads" = 1 ]; then
@@ -265,6 +270,7 @@ case "$profile" in
265
270
  shared=(-shared -fPIC)
266
271
  # macOS: the napi_* symbols come from the node binary at load time, so the
267
272
  # linker must not insist on resolving them. ELF shared objects allow this.
273
+ # shellcheck disable=SC2054 # -Wl,<flag> is one argument: the comma is the linker's
268
274
  case "$(uname -s)" in Darwin) shared+=(-Wl,-undefined,dynamic_lookup) ;; esac
269
275
  # runtime/nish.h is the public ABI header the generated shim includes.
270
276
  runtime_inc="$(cd "$(dirname "$0")/../runtime" && pwd)"