@amritk/nish-x86_64-linux 0.27.2 → 0.29.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 (54) hide show
  1. package/INSTALL.md +16 -8
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +118 -26
  4. package/package.json +1 -1
  5. package/runtime/core/host.d.ts +2 -1
  6. package/runtime/core/host.mjs +4 -0
  7. package/runtime/core.d.ts +47 -0
  8. package/runtime/core.mjs +67 -0
  9. package/runtime/nish.d.ts +176 -0
  10. package/runtime/nish.h +120 -6
  11. package/runtime/nish.mjs +24 -4
  12. package/runtime/runtime-checksum.c +270 -0
  13. package/runtime/runtime-net.c +386 -5
  14. package/runtime/runtime-os.c +59 -8
  15. package/runtime/runtime-parallel.c +81 -5
  16. package/runtime/runtime-sandbox.c +479 -0
  17. package/runtime/runtime-sha256.c +239 -0
  18. package/runtime/runtime-shm.c +541 -0
  19. package/runtime/runtime.c +827 -791
  20. package/scripts/build.sh +79 -28
  21. package/std/README.md +25 -13
  22. package/std/collections.ts +5 -5
  23. package/std/compress/checksum.ts +88 -75
  24. package/std/compress/deflate.ts +224 -0
  25. package/std/compress/gzip.ts +52 -0
  26. package/std/crypto/bcrypt.ts +816 -280
  27. package/std/crypto/hmac.ts +305 -2
  28. package/std/crypto/jwt.ts +1661 -0
  29. package/std/json-equal.ts +335 -0
  30. package/std/json-format.ts +1070 -0
  31. package/std/json-number.ts +428 -0
  32. package/std/json-pointer.ts +245 -0
  33. package/std/json-typed.ts +34 -0
  34. package/std/json-value.ts +510 -17
  35. package/std/json.ts +35 -6
  36. package/std/net/handler.ts +1348 -0
  37. package/std/net/http-client.ts +97 -16
  38. package/std/net/http-compress.ts +301 -16
  39. package/std/net/http1-client.ts +104 -19
  40. package/std/net/http1-server.ts +127 -24
  41. package/std/net/http1.ts +24 -4
  42. package/std/net/outbound.ts +409 -0
  43. package/std/net/postgres.ts +405 -0
  44. package/std/net/prefork.ts +344 -20
  45. package/std/net/websocket.ts +295 -14
  46. package/std/regex.ts +613 -52
  47. package/std/secret.ts +1 -1
  48. package/std/sync/ring.ts +587 -0
  49. package/std/sync/shm.ts +284 -0
  50. package/std/term-io.ts +35 -8
  51. package/std/term-rate.ts +13 -1
  52. package/std/threads.ts +35 -3
  53. package/std/yaml-json.ts +841 -0
  54. /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
package/INSTALL.md CHANGED
@@ -254,7 +254,9 @@ routes are
254
254
  # on the musl host, with its own clang and the runtime from this repository
255
255
  clang app.ll runtime/runtime.c runtime/runtime-os.c runtime/runtime-parallel.c \
256
256
  runtime/runtime-cross.c runtime/runtime-host.c runtime/runtime-net.c \
257
- runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-term.c runtime/runtime-bytes.c -lm -o app
257
+ runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-sha256.c runtime/runtime-term.c \
258
+ runtime/runtime-bytes.c runtime/runtime-checksum.c runtime/runtime-sandbox.c \
259
+ runtime/runtime-shm.c -lm -o app
258
260
  ```
259
261
 
260
262
  `--link`ing inside the container is the mistake to avoid: it shells out to the
@@ -390,7 +392,7 @@ it into `build/seed/`; `NISH_BOOTSTRAP=<path>` names one you already have and
390
392
  skips the download. §2a has what the build does with it.
391
393
 
392
394
  The published package ships `bin/` (the `nish` command, which is a launcher),
393
- `runtime/` (the C runtime — ten translation units, the wasm subset and their
395
+ `runtime/` (the C runtime — fourteen translation units, the wasm subset and their
394
396
  header — and `runtime/sqlite/`, SQLite's vendored source),
395
397
  `scripts/build.sh` (the link pipeline), `std/` (the standard library) and
396
398
  `docs/AI.md` (the rules card). The
@@ -403,12 +405,14 @@ working directory — and the binary runs from inside its own platform package
403
405
  for exactly that reason.
404
406
 
405
407
  Building the IR yourself rather than through `--link` means naming the runtime
406
- on the `clang` line, and it is ten files:
408
+ on the `clang` line, and it is fourteen files:
407
409
 
408
410
  ```bash
409
411
  clang app.ll runtime/runtime.c runtime/runtime-os.c runtime/runtime-parallel.c \
410
412
  runtime/runtime-cross.c runtime/runtime-host.c runtime/runtime-net.c \
411
- runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-term.c runtime/runtime-bytes.c -lm -o app
413
+ runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-sha256.c runtime/runtime-term.c \
414
+ runtime/runtime-bytes.c runtime/runtime-checksum.c runtime/runtime-sandbox.c \
415
+ runtime/runtime-shm.c -lm -o app
412
416
  ```
413
417
 
414
418
  `runtime.c` is the part every program touches — the arena, strings, arrays,
@@ -421,14 +425,18 @@ the strings and arrays a task answers, a channel sends or a `Mutex` keeps out
421
425
  of the arena they were made in, `runtime-host.c` holds the wall clock,
422
426
  entropy, file times and signals, `runtime-net.c` the sockets of `nish:net`,
423
427
  `runtime-simd.c` and `runtime-aes.c` the vector text search and AES-GCM,
424
- `runtime-term.c` the stdin reads and terminal calls of `nish:term`, and
428
+ `runtime-sha256.c` the SHA-256 block kernel `nish/crypto/sha256` hands its
429
+ blocks to on a CPU with the SHA instructions (`NISH_SHA256=soft` keeps the
430
+ portable path), `runtime-term.c` the stdin reads and terminal calls of `nish:term`,
425
431
  `runtime-bytes.c` the byte-class runs of `nish/bytes`, which `nish/json` steps
426
- over.
432
+ over, `runtime-checksum.c` the CRC-32 kernels of `nish/compress/checksum`,
433
+ `runtime-sandbox.c` the confinement a `--sandbox` build applies, and
434
+ `runtime-shm.c` the shared-memory regions of `nish/sync/shm`.
427
435
  They are separate so that each carries its own measured size ceiling
428
436
  ([docs/wp7-runtime.md](wp7-runtime.md)); nothing in the core calls into the
429
437
  others, so a line that names only `runtime.c` and `runtime-os.c` still links a
430
- program that uses none of the other eight. `scripts/build.sh` compiles the other
431
- eight beside any
438
+ program that uses none of the other nine. `scripts/build.sh` compiles the other
439
+ nine beside any
432
440
  `runtime.c` it is handed, so a build that goes through it — every `--link`,
433
441
  and every `--profile` recipe in these documents — needs to name only the one.
434
442
 
package/bin/nish CHANGED
Binary file
package/docs/AI.md CHANGED
@@ -180,7 +180,7 @@ rejects. This table is the highest-value part of the page.
180
180
  | `keyof T`, `<T = i32>` (a default type argument) | `` Unsupported type `keyof T` (Phase 1 supports …) `` / `a default type argument … is not supported` | name the type; a type argument is always inferred |
181
181
  | `class B extends A` | `` `extends` is not supported: Nish has no inheritance `` | repeat the fields and `implements` an interface |
182
182
  | `static` members, `get x()` / `set x(v)` | `` … `static` members are not supported `` / `` Getters and setters are not supported in class `C` (use a method) `` | module `const`s and plain methods |
183
- | `const [a, b] = xs`, `({ x }: Point) =>`, `(n = 1)`, `(n?: i32)`, `(...ns: i32[])` | `Destructuring is not supported` / `Destructured parameters are not supported` / `Optional/default parameters are not supported` / `Rest parameters are not supported` | one name per binding, every parameter passed; an array for a variable count |
183
+ | `const [a, b] = xs`, `({ x }: Point) =>`, `(n?: i32)`, `(n = 1)`, `f(...xs)` | `Destructuring is not supported` / `Destructured parameters are not supported` / `Optional parameters are not supported` / `` Parameter `n` has a default and no type annotation `` / `Spread arguments are not supported` | one name per binding; `n: i32 = 1` (a constant or an earlier parameter) for a default; `...ns: i32[]` as the last parameter, or pass the array itself |
184
184
  | `function f(): void;` (an overload), `export default function ()` | `Functions must have a body` / `Functions must be named` | one named function, with its body |
185
185
  | a method in an `interface` | `` Interface `I` cannot declare methods (interfaces describe layout only) `` | fields only; a top-level function over the interface |
186
186
  | `abstract class`, `declare class`, `constructor(public x: i32)`, `interface B extends A`, `[k: string]: T` | `Abstract classes are not supported` / `` `declare class` is not supported `` / `Parameter properties … are not supported; declare the field and assign it` / ``Interface inheritance (`extends`) is not supported; list every field`` / `Index signatures are not supported …` | a plain class with its fields declared and assigned in the constructor; every field listed; a `Map` for a keyed table |
@@ -845,10 +845,14 @@ export const main = (): i32 => {
845
845
  - **Call it, or pass it on to another function parameter. Nothing else**:
846
846
  `const g = f`, `return f` and `[f]` are
847
847
  `` `f` is a function parameter and can only be called or passed on as a function argument ``.
848
- - **An arrow captures nothing.** It sees its own parameters and top-level
849
- names; reading a local of the function around it is
850
- `` The arrow reads `k`, which belongs to the function it is written in ``.
851
- Pass the value in as another argument of the template instead.
848
+ - **An arrow may read the `const` locals and parameters of the function it is
849
+ written in**, and write through a captured reference
850
+ (`(x) => { if (x > limit) { hits.push(x) } }`); each capture becomes one more
851
+ argument of the direct call. It may not assign to one, read a `let`
852
+ (`` The arrow reads `k`, a `let` of the function it is written in: ... bind the value to a `const` before the call ``),
853
+ read `this` (bind `const self = this`), or read a function parameter of its
854
+ function. An arrow inside another arrow, or given to a method, a
855
+ constructor, `expose` or `exposeWith`, captures nothing.
852
856
  - **The argument's type must match exactly**: `(x: i32) => i64` for a
853
857
  `(x: i32) => i32` parameter is refused. A generic function cannot be passed
854
858
  by name — write `(x) => identity(x)`.
@@ -858,12 +862,13 @@ export const main = (): i32 => {
858
862
  - **Only a top-level function** may have one: not a method, a constructor, a
859
863
  field, a local, a return type or an alias.
860
864
 
861
- ```ts nish:err NL2335
865
+ ```ts nish:err NL2585
862
866
  const apply = (f: (x: i32) => i32, x: i32): i32 => f(x);
863
867
 
864
868
  export const main = (): i32 => {
865
- const k = 3;
866
- return apply((x) => x + k, 1); // captures `k`: pass it to `apply` instead
869
+ let k = 3;
870
+ k = k + 1;
871
+ return apply((x) => x + k, 1); // reads the `let` `k`: bind `const kNow = k` before the call
867
872
  };
868
873
  ```
869
874
 
@@ -976,6 +981,11 @@ export const main = (): i32 => {
976
981
  (NL2388): `using s = scope()`.
977
982
  The scope is only ever the receiver of a `spawn` statement: never pass it,
978
983
  store it or return it.
984
+ - **A task that recurses deeply gets a bigger stack from `scopeWithStack`.**
985
+ `using s = scopeWithStack(64 * 1024 * 1024)` is a scope whose tasks each run
986
+ on a new thread with a 64 MiB stack; a thread's default is 8 MiB. Ask for at
987
+ least 131072 bytes: a number below it is refused (NL2584). Under Node the size
988
+ is ignored, so run Node with `--stack-size` too.
979
989
  - **The task is a top-level function named in the `spawn`, or an arrow written
980
990
  there.** `s.spawn(entry, arg)` with no destination runs an entry that
981
991
  answers `void`; `s.spawn(() => work(a, b))` runs an arrow that reads the
@@ -1893,32 +1903,52 @@ export const main = (): i32 => {
1893
1903
  `push` inside the body extends the loop. `for...in`, `for await` and
1894
1904
  `for (x of xs)` over an existing `x` are each refused by name.
1895
1905
  - `switch` takes an **integer or enum** discriminant; every `case` label is an
1896
- integer constant expression of that type. **There is no implicit
1897
- fallthrough** — a clause with statements ends in `break`, `return`,
1898
- `continue` or `process.exit`, unless it is the last. An *empty* clause does
1899
- fall through, which is how `case 1: case 2:` shares a body. A clause cannot
1900
- declare a variable directly; wrap its body in a block.
1901
- - `break` / `continue` are unlabelled only; labeled statements are forbidden.
1906
+ integer constant expression of that type. A clause that does not end in
1907
+ `break`, `return`, `continue` or `process.exit` **falls through** into the
1908
+ next, as in TypeScript; an *empty* clause is how `case 1: case 2:` shares a
1909
+ body. A clause reached by falling into it is not narrowed by its label. A
1910
+ clause cannot declare a variable directly; wrap its body in a block.
1911
+ - **Labels** go on a loop, a block or a `switch`: `break outer` leaves the
1912
+ statement named `outer` from any depth, `continue outer` starts the next
1913
+ turn of the loop named `outer`. A label on any other statement is refused,
1914
+ and a jump cannot leave an arrow for a label outside it.
1902
1915
  - `process.exit(code)` and `panic(msg)` **terminate control flow**, so a
1903
1916
  non-`void` function may end with either.
1904
1917
  - **Unreachable code is an error**, not a warning: anything after a `return`,
1905
1918
  `break`, `continue`, `process.exit`, an `if` whose branches all return, or an
1906
1919
  infinite loop.
1907
1920
 
1908
- ```ts nish:err NL2166
1909
- const classify = (n: i32): string => {
1921
+ ```ts nish:ok
1922
+ const firstPair = (limit: i32, target: i32): i32 => {
1923
+ outer: for (let i: i32 = 0; i < limit; i++) {
1924
+ for (let j: i32 = 0; j < limit; j++) {
1925
+ if (i * j === target) {
1926
+ return i * 100 + j;
1927
+ }
1928
+ if (j > i) {
1929
+ continue outer; // the next `i`
1930
+ }
1931
+ }
1932
+ }
1933
+ return -1;
1934
+ };
1935
+
1936
+ const classify = (n: i32): i32 => {
1937
+ let score: i32 = 0;
1910
1938
  switch (n) {
1911
1939
  case 0:
1912
- console.log("zero"); // no `break`: a clause with statements must end in one
1940
+ score += 10; // no `break`: falls into `case 1`
1913
1941
  case 1:
1914
- return "one";
1942
+ score += 1;
1943
+ break;
1915
1944
  default:
1916
- return "many";
1945
+ score = -1;
1917
1946
  }
1947
+ return score;
1918
1948
  };
1919
1949
 
1920
1950
  export const main = (): i32 => {
1921
- console.log(classify(0));
1951
+ console.log(`${firstPair(10, 12)} ${classify(0)} ${classify(1)}`); // 304 11 1
1922
1952
  return 0;
1923
1953
  };
1924
1954
  ```
@@ -2181,6 +2211,10 @@ also watches the signal fd. None of `spawn`, `kill`, `waitPid` may be called in
2181
2211
  a task or a parallel body; the two host facts may. `availableParallelism()` (`i32`, at least 1, no `--threads` needed) and
2182
2212
  `totalMemory()` (`i64` bytes, or `-1`) size a program to the machine. None of
2183
2213
  the five exists on a wasm target, and `spawn` and `waitPid` throw under Node.
2214
+ `spawnWithFds(argv, fds)` is `spawn` that hands the child `fds[i]` as
2215
+ descriptor `3 + i` (systemd's `LISTEN_FDS` numbering); every other descriptor
2216
+ still closes at exec. More than 64, or a negative one, is `-22`; it needs
2217
+ `process.spawn` under `--sandbox`, refuses a wasm target and throws under Node.
2184
2218
  `setNice(n)` sets the nice value, and it is lower-only even as root: `n` takes
2185
2219
  0..19 (`-22` outside it) and a value below the current one is `-13`, with no
2186
2220
  system call either way. `setIdlePriority()` moves the program to Linux's `SCHED_IDLE`
@@ -2230,8 +2264,42 @@ unread is reported again. Add `signalFd()` to the loop to wake on SIGTERM.
2230
2264
  `NET_SO_KEEPALIVE`, `NET_SO_SNDBUF` and `NET_SO_RCVBUF`, `i32` ids exported
2231
2265
  beside it; any other id is `-22`. Linux drops quick-ACK mode on its own, so set
2232
2266
  it again after each response. The ids are not module constants: pass them
2233
- directly. None
2234
- exists on a wasm target, and under Node each throws.
2267
+ directly. `netMss(fd)` answers a connected TCP socket's send MSS, the payload
2268
+ bytes one segment carries, and `-1` for anything else (never an errno), so size
2269
+ writes by it with a fallback. None but `netMss` exists on a wasm target (there
2270
+ it is always `-1`), and under Node each throws.
2271
+
2272
+ Unix-domain stream sockets: `unixListen(path, backlog, flags)` listens on a
2273
+ socket file left at 0600 (`unixListenMode(path, backlog, flags, mode)` for
2274
+ another mode), `unixConnect(path)` and `unixAccept(fd)` answer descriptors
2275
+ that `netRead`, `netWrite`, `netShutdown`, `netClose` and the loop take as
2276
+ they are, and `unixSocketPair(NET_UNIX_STREAM, fds)` fills `fds: i32[]` with
2277
+ two connected ends (`NET_UNIX_SEQPACKET` keeps message boundaries; Linux
2278
+ only, `-95` on Darwin). A path starting with `@` is Linux's abstract
2279
+ namespace and makes no file (`-95` on Darwin); write `./@name` for a socket
2280
+ file so named. A path that exists is `-98`; flag `NET_UNIX_UNLINK_STALE`
2281
+ first removes a socket file nobody listens on (Linux; on Darwin it is `-95`
2282
+ at a socket file, since a full backlog and a dead file both refuse there).
2283
+ `NET_UNIX_UNLINK_STALE`, `NET_UNIX_STREAM` and `NET_TCP_NODELAY` are all `1`,
2284
+ so pass the constant each call names. A path as written is at most 107
2285
+ bytes (103 on Darwin) and an abstract name at most 107 after its `@`; longer
2286
+ is `-36`. A full backlog makes `unixConnect`
2287
+ `-11` on Linux and `-111` on Darwin. Under `--sandbox`, a listener on a path
2288
+ also needs `fs.write` (`-13` without it); an abstract name does not, and a
2289
+ connect to a path is not confined. `sendFd(sock, fd, buf, off, len)` sends
2290
+ `buf[off, off + len)` with descriptor `fd` attached (`len` 0 is `-22`) and
2291
+ answers the bytes sent, which on a stream socket can be short: the descriptor
2292
+ went with the first bytes, so send the rest with `netWrite`; `recvFd(sock, buf, off, len, fd)` reads into the
2293
+ range and sets `fd[0]` to the descriptor that came with it, or `-1`; the
2294
+ descriptor arrives close-on-exec, its status flags (`O_NONBLOCK` among them)
2295
+ the open file's, which the sender shares, any beyond the first are closed,
2296
+ and `fd` shorter than 1 is `-22`. `peerCredentials(sock, out)` writes
2297
+ `[pid, uid, gid]` of the process that made the connection, as it was at
2298
+ connect time, into an `out` of length 3 or more (`-22` shorter, `-107` for a
2299
+ listener or a socket never connected); on Darwin the
2300
+ pid can be `-1`. All three answer `-88` or `-95` for a socket that is not
2301
+ Unix-domain. The Unix calls are refused on a wasm target, and under Node each
2302
+ throws.
2235
2303
 
2236
2304
  **Arena.** `Arena.mark()`, `Arena.used()` and `using a = arena()` — see below.
2237
2305
  `Arena.release(m)` and `Arena.reset()` were removed before 1.0: write
@@ -2261,6 +2329,10 @@ console.log(`${seen.size} ${counts.size} ${seen.has("b")}`);
2261
2329
  - A key is a string, any number type, a `boolean`, an enum or a class instance
2262
2330
  (by identity). An interface, an array, a nullable type or a `Result` is not a
2263
2331
  key; a value is anything but `void` or an interface.
2332
+ - A pair of `i32`s is one `i64` key, not a string: `(toI64(a) << toI64(32)) |
2333
+ toI64(toU32(b))`. Every operand is an `i64` so it also runs
2334
+ under Node, where `i64` is a `BigInt`; it is exact for every pair. A `+` in
2335
+ place of `|` overflows at `a = -2^31` with a negative `b`.
2264
2336
  - A module that declares its own `Map` or `Set` keeps it, but then no other
2265
2337
  module of the program may name the global one.
2266
2338
 
@@ -2619,7 +2691,8 @@ negative `DEFLATE_ERR_*` / `GZIP_ERR_*` code for any malformed stream, so test
2619
2691
  it before reading `out`. `GzipWriter` and `GzipReader` stream. On HTTP/1.1,
2620
2692
  `conn.respondEncoded(status, names, values, body, off, len, enc,
2621
2693
  conn.acceptEncoding())` with an `HttpCompressor` from `nish/net/http-compress`
2622
- negotiates, compresses and sets the fields. Do not compress a secret beside
2694
+ negotiates, compresses when it pays (`minLength`, `minSavingPercent`,
2695
+ `segmentMode`; `enc.decision` says why not) and sets the fields. Do not compress a secret beside
2623
2696
  text the client chose (BREACH). (No block, as for `nish/text`;
2624
2697
  `tests/link/std_compress` is its program, and the user guide's
2625
2698
  [compression page](guide/using-nish/compression.md) its walk-through.)
@@ -2665,7 +2738,7 @@ or `PG_LOST` to `fail(run)` (on a spent handle these do nothing and count in
2665
2738
  `stray`), and after each call `next(run)` until
2666
2739
  `PG_BATCH_NONE`: `PG_BATCH_ROW` (the connection's row answers request `tag`),
2667
2740
  then one of `PG_BATCH_DONE`, `PG_BATCH_MISSING` or `PG_BATCH_FAILED` per
2668
- request. It reads no clock: when to take is the loop's choice. (No block;
2741
+ request. It reads no clock: when to take is the loop's choice, or a `PgBatchClock`'s (`PgBatchClockConfig`: `minCount`, `factor` × the moving average of batch sizes, `maxWaitUs`, the idle bypass `lightCount`; defaults are a fixed window), which `takeDue(conn, clock, nowUs, pool.inFlight())` consults after `clock.arrived(nowUs)` per add; `PgBatchTurns` wakes one collecting connection only when its batch is due. (No block;
2669
2742
  `tests/postgres/live`'s `batch` scenario runs it against a server, and the user
2670
2743
  guide's [PostgreSQL page](guide/using-nish/postgres.md#batching-keyed-reads)
2671
2744
  its walk-through.)
@@ -2709,8 +2782,11 @@ export const main = (): i32 => {
2709
2782
  };
2710
2783
  ```
2711
2784
 
2712
- **Optional parameters** — there are none. Write two functions with different
2713
- names, or take the value and document the sentinel.
2785
+ **Optional parameters** — write a default instead: `(n: i32 = 1)`. A default
2786
+ is a literal, an enum member or an earlier parameter, and the call site fills
2787
+ it in. `x?: T` is refused. A variable count of one type is a rest parameter,
2788
+ `(...ns: i32[])`, last in the list; a call never spreads (`f(...xs)`), it passes
2789
+ `xs` to an array parameter.
2714
2790
 
2715
2791
  **A calendar, regex, JSON, threads of your own** — not in the language (the
2716
2792
  wall clock is `Date.now()`, and that is all of `Date`). Say so rather than
@@ -2822,6 +2898,22 @@ const settings = (): string => readFileSync("settings.txt");
2822
2898
  export const main = (): i32 => settings().length;
2823
2899
  ```
2824
2900
 
2901
+ The policy judges the source; `--sandbox` (Linux, `--link` and `nish run`)
2902
+ makes the kernel hold the running binary to the same set. Before any C
2903
+ constructor runs, it withholds every file, process and socket operation `main`
2904
+ does not reach, C code it links included. A file operation it refuses fails
2905
+ like a missing permission; a refused process or socket call ends the program
2906
+ with `SIGSYS` (status 159); a kernel that cannot apply it exits 126 before
2907
+ `main` unless built with `--sandbox=best-effort`. A darwin target, a library
2908
+ with no `main` and `--emit-napi` refuse the flag
2909
+ ([LANGUAGE.md](LANGUAGE.md#the-sandbox)). Only under `--sandbox` may an
2910
+ `--allow` list scope `fs.read` or `fs.write` to a directory
2911
+ (`--allow fs.read=/srv/data`): the directory must exist at build time, and
2912
+ the kernel grants the capability beneath it alone — except that a `main`
2913
+ reaching `process.spawn` can still read every file beneath `/`, so that
2914
+ `exec` can load a program. Any other scope, a scope
2915
+ in `--deny` and a scope without `--sandbox` are usage errors, exit 2.
2916
+
2825
2917
  ## Before you say it compiles
2826
2918
 
2827
2919
  Run it. `nish file.ts --json` is one command and it is the only proof.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/nish-x86_64-linux",
3
- "version": "0.27.2",
3
+ "version": "0.29.0",
4
4
  "description": "The nish native compiler for Linux on x86_64",
5
5
  "license": "MIT",
6
6
  "os": [
@@ -73,8 +73,9 @@ export declare function isTerminal(fd: i32): boolean;
73
73
  export declare function terminalSize(): i32[];
74
74
  /** `crypto.getRandomValues(bytes)` for a `u8[]`; a typed array goes straight through. */
75
75
  export declare function getRandomValues<T extends u8[] | Uint8Array>(bytes: T): T;
76
- /** These four have no synchronous reading: each throws, naming itself, and is typed as `runtime/nish.d.ts` types it. */
76
+ /** These five have no synchronous reading: each throws, naming itself, and is typed as `runtime/nish.d.ts` types it. */
77
77
  export declare function spawn(argv: string[]): i32;
78
+ export declare function spawnWithFds(argv: string[], fds: readonly i32[]): i32;
78
79
  export declare function waitPid(pid: i32, flags: i32): i32[];
79
80
  export declare function signalFd(): i32;
80
81
  export declare function readSignal(fd: i32): i32;
@@ -309,6 +309,10 @@ export function spawn() {
309
309
  return noProcessReading("spawn");
310
310
  }
311
311
 
312
+ export function spawnWithFds() {
313
+ return noProcessReading("spawnWithFds");
314
+ }
315
+
312
316
  export function waitPid() {
313
317
  return noProcessReading("waitPid");
314
318
  }
package/runtime/core.d.ts CHANGED
@@ -7,3 +7,50 @@ export * from "./core/host.js";
7
7
  export * from "./core/numbers.js";
8
8
  export * from "./core/result.js";
9
9
  export * from "./core/strings.js";
10
+
11
+ // `nish:simd`: what `--emit-ts` writes for a program's `import ... from "nish:simd"`.
12
+ import type { f32, f64, i32, u16, u32, u8 } from "./core/numbers.js";
13
+
14
+ /**
15
+ * A sixteen-byte vector of lanes. Opaque: TypeScript refuses `+` on one, as
16
+ * `nish` does, but lets `===` and `<` through, which `nish` refuses. Under
17
+ * Node it is a typed array made once and never written after.
18
+ */
19
+ export interface u8x16 { readonly __nishSimd: "u8x16"; readonly __lane: u8 }
20
+ export interface u16x8 { readonly __nishSimd: "u16x8"; readonly __lane: u16 }
21
+ export interface i32x4 { readonly __nishSimd: "i32x4"; readonly __lane: i32 }
22
+ export interface u32x4 { readonly __nishSimd: "u32x4"; readonly __lane: u32 }
23
+ export interface f32x4 { readonly __nishSimd: "f32x4"; readonly __lane: f32 }
24
+ export interface f64x2 { readonly __nishSimd: "f64x2"; readonly __lane: f64 }
25
+ /** Any of the six. */
26
+ export type SimdVector = u8x16 | u16x8 | i32x4 | u32x4 | f32x4 | f64x2;
27
+ /** One value per lane, lane 0 first. */
28
+ export declare function u8x16(
29
+ a0: u8, a1: u8, a2: u8, a3: u8, a4: u8, a5: u8, a6: u8, a7: u8,
30
+ a8: u8, a9: u8, a10: u8, a11: u8, a12: u8, a13: u8, a14: u8, a15: u8
31
+ ): u8x16;
32
+ export declare function u16x8(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16): u16x8;
33
+ export declare function i32x4(a0: i32, a1: i32, a2: i32, a3: i32): i32x4;
34
+ export declare function u32x4(a0: u32, a1: u32, a2: u32, a3: u32): u32x4;
35
+ export declare function f32x4(a0: f32, a1: f32, a2: f32, a3: f32): f32x4;
36
+ export declare function f64x2(a0: f64, a1: f64): f64x2;
37
+ /** `x` in every lane. */
38
+ export declare function splatU8x16(x: u8): u8x16;
39
+ export declare function splatU16x8(x: u16): u16x8;
40
+ export declare function splatI32x4(x: i32): i32x4;
41
+ export declare function splatU32x4(x: u32): u32x4;
42
+ export declare function splatF32x4(x: f32): f32x4;
43
+ export declare function splatF64x2(x: f64): f64x2;
44
+ /** `xs[i]` to `xs[i + N - 1]`, bounds-checked once for the whole vector. */
45
+ export declare function loadU8x16(xs: readonly u8[], i: i32): u8x16;
46
+ export declare function loadU16x8(xs: readonly u16[], i: i32): u16x8;
47
+ export declare function loadI32x4(xs: readonly i32[], i: i32): i32x4;
48
+ export declare function loadU32x4(xs: readonly u32[], i: i32): u32x4;
49
+ export declare function loadF32x4(xs: readonly f32[], i: i32): f32x4;
50
+ export declare function loadF64x2(xs: readonly f64[], i: i32): f64x2;
51
+ /** The lanes of `v` into `xs[i]` to `xs[i + N - 1]`, checked once. */
52
+ export declare function store<V extends SimdVector>(v: V, xs: V["__lane"][], i: i32): void;
53
+ /** Lane `k`, an integer literal in range. */
54
+ export declare function extract<V extends SimdVector>(v: V, k: i32): V["__lane"];
55
+ /** A new vector: `v` with lane `k` set to `x`. */
56
+ export declare function replace<V extends SimdVector>(v: V, k: i32, x: V["__lane"]): V;
package/runtime/core.mjs CHANGED
@@ -48,8 +48,75 @@
48
48
  *
49
49
  * The rewrite rules that call these helpers are listed in docs/wp13-differential.md.
50
50
  */
51
+ import { panicIndex, toIndex } from "./core/checks.mjs";
52
+
51
53
  export * from "./core/arrays.mjs";
52
54
  export * from "./core/host.mjs";
53
55
  export * from "./core/numbers.mjs";
54
56
  export * from "./core/result.mjs";
55
57
  export * from "./core/strings.mjs";
58
+
59
+ // ---- `nish:simd` ------------------------------------------------------------
60
+ // A vector is a fresh typed array of its lanes, made once and never written
61
+ // after: `replace` copies, so a vector held in two places stays one value, as
62
+ // the native `<N x T>` register is. The array type does the lane arithmetic a
63
+ // native lane does: a `u8` lane wraps through `Uint8Array`, an `f32` lane rounds
64
+ // through `Float32Array`. `load…` and `store` check the whole vector once, and
65
+ // panic with the native words: the first index, and the array's length.
66
+
67
+ const simdMake = (Lanes) => (...lanes) => new Lanes(lanes);
68
+ const simdSplat = (Lanes) => (x) => new Lanes(16 / Lanes.BYTES_PER_ELEMENT).fill(x);
69
+ // The first index of `count` lanes at `i` in `xs`, or the native panic naming
70
+ // a lane that is out of range: the first when it is, else the last. A negative
71
+ // index prints as the unsigned 64-bit number the native compare saw.
72
+ const simdAt = (xs, i, count) => {
73
+ const k = toIndex(i);
74
+ if (k < 0) panicIndex(BigInt.asUintN(64, BigInt(k)), xs.length);
75
+ if (k >= xs.length) panicIndex(k, xs.length);
76
+ if (k + count > xs.length) panicIndex(k + count - 1, xs.length);
77
+ return k;
78
+ };
79
+ const simdLoad = (Lanes) => (xs, i) => {
80
+ const count = 16 / Lanes.BYTES_PER_ELEMENT;
81
+ const k = simdAt(xs, i, count);
82
+ const out = new Lanes(count);
83
+ for (let j = 0; j < count; j++) out[j] = xs[k + j];
84
+ return out;
85
+ };
86
+
87
+ export const u8x16 = simdMake(Uint8Array);
88
+ export const u16x8 = simdMake(Uint16Array);
89
+ export const i32x4 = simdMake(Int32Array);
90
+ export const u32x4 = simdMake(Uint32Array);
91
+ export const f32x4 = simdMake(Float32Array);
92
+ export const f64x2 = simdMake(Float64Array);
93
+ export const splatU8x16 = simdSplat(Uint8Array);
94
+ export const splatU16x8 = simdSplat(Uint16Array);
95
+ export const splatI32x4 = simdSplat(Int32Array);
96
+ export const splatU32x4 = simdSplat(Uint32Array);
97
+ export const splatF32x4 = simdSplat(Float32Array);
98
+ export const splatF64x2 = simdSplat(Float64Array);
99
+ export const loadU8x16 = simdLoad(Uint8Array);
100
+ export const loadU16x8 = simdLoad(Uint16Array);
101
+ export const loadI32x4 = simdLoad(Int32Array);
102
+ export const loadU32x4 = simdLoad(Uint32Array);
103
+ export const loadF32x4 = simdLoad(Float32Array);
104
+ export const loadF64x2 = simdLoad(Float64Array);
105
+
106
+ /** `store(v, xs, i)`: the lanes into `xs[i]` to `xs[i + N - 1]`, checked once. */
107
+ export function store(v, xs, i) {
108
+ const k = simdAt(xs, i, v.length);
109
+ for (let j = 0; j < v.length; j++) xs[k + j] = v[j];
110
+ }
111
+
112
+ /** `extract(v, k)`: lane `k`, a constant the checker held in range. */
113
+ export function extract(v, k) {
114
+ return v[k];
115
+ }
116
+
117
+ /** `replace(v, k, x)`: a new vector, `v` with lane `k` set to `x`. */
118
+ export function replace(v, k, x) {
119
+ const out = v.slice();
120
+ out[k] = x;
121
+ return out;
122
+ }