@amritk/nish-x86_64-linux 0.23.1 → 0.25.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.
- package/bin/nish +0 -0
- package/docs/AI.md +50 -23
- package/package.json +1 -1
- package/runtime/browser.mjs +4 -3
- package/runtime/core/arrays.d.ts +0 -2
- package/runtime/core/arrays.mjs +0 -2
- package/runtime/core/host.d.ts +8 -0
- package/runtime/core/host.mjs +50 -2
- package/runtime/core/numbers.mjs +2 -3
- package/runtime/nish.d.ts +100 -4
- package/runtime/nish.h +34 -0
- package/runtime/nish.mjs +23 -5
- package/runtime/node.mjs +57 -6
- package/runtime/runtime-net.c +45 -4
- package/runtime/runtime-os.c +115 -12
- package/runtime/runtime-term.c +2 -2
- package/runtime/runtime.c +1 -1
- package/scripts/build.sh +13 -3
- package/std/README.md +44 -7
- package/std/cli-args.ts +1216 -0
- package/std/cli-flags.ts +585 -0
- package/std/cli.ts +308 -19
- package/std/compress/checksum.ts +290 -0
- package/std/compress/deflate.ts +2790 -0
- package/std/compress/gzip.ts +761 -0
- package/std/compress/zlib.ts +168 -0
- package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
- package/std/crypto/p256.ts +63 -1
- package/std/crypto/p384.ts +6044 -0
- package/std/crypto/pki-roots.ts +322 -0
- package/std/crypto/pki.ts +1207 -0
- package/std/crypto/rsa.ts +663 -0
- package/std/crypto/sha256.ts +2 -3
- package/std/crypto/x25519.ts +3 -4
- package/std/crypto/x509.ts +1054 -58
- package/std/net/dns-config.ts +734 -0
- package/std/net/dns-message.ts +517 -0
- package/std/net/dns-resolver.ts +1015 -0
- package/std/net/dns.ts +478 -0
- package/std/net/http-compress.ts +393 -0
- package/std/net/http1-client.ts +1227 -0
- package/std/net/http1-server.ts +101 -1
- package/std/net/http1.ts +9 -9
- package/std/net/tls/client.ts +1094 -0
- package/std/net/tls/codec.ts +585 -3
- package/std/net/tls/record-client.ts +1192 -0
- package/std/net/tls/verify.ts +221 -0
- package/std/net/tls-tcp.ts +642 -11
- package/std/net/tls.ts +2 -2
- package/std/net/url.ts +559 -4
- package/std/yaml-write.ts +415 -0
package/bin/nish
CHANGED
|
Binary file
|
package/docs/AI.md
CHANGED
|
@@ -90,8 +90,7 @@ so it refuses `-o` and `--link`. It rewrites your files in place, with no
|
|
|
90
90
|
backup, so run it on a committed or backed-up tree. Run it first, then read
|
|
91
91
|
what remains. A warning can carry a fix too, and `--fix` applies it only when the program
|
|
92
92
|
compiles and the warning would be printed: `NL9007`'s inserts the guard that
|
|
93
|
-
proves an index in range (not under `--no-warn-performance`)
|
|
94
|
-
migrates a site off `--unchecked-indexing` (below).
|
|
93
|
+
proves an index in range (not under `--no-warn-performance`).
|
|
95
94
|
|
|
96
95
|
When it compiles, **check the native build against the program's TypeScript
|
|
97
96
|
reading**. Run the project's tests natively; where it has none, write the
|
|
@@ -247,14 +246,9 @@ the site: `uncheckedGet(xs, i)` and `uncheckedSet(xs, i, v)` on an array of
|
|
|
247
246
|
numbers with an `i32` index (out of range is undefined behaviour), and
|
|
248
247
|
`wrappingAdd` / `wrappingSub` / `wrappingMul` on two `i32`s or two `i64`s.
|
|
249
248
|
The sixth export, `readCString`, copies a C string out of a `CPtr`
|
|
250
|
-
([Calling C](#calling-c)). Calling one without the import is NL2456.
|
|
251
|
-
`--
|
|
252
|
-
|
|
253
|
-
`nish --unchecked-indexing --fix` on your files: each index the flag leaves
|
|
254
|
-
unchecked is a `deprecation` warning of its own (NL7002), and where the site
|
|
255
|
-
is a number array with an `i32` index its `fix` rewrites it to `uncheckedGet`
|
|
256
|
-
or `uncheckedSet` and adds the import. A site with no rewrite keeps its
|
|
257
|
-
warning; dropping the flag gives it back its bounds check.
|
|
249
|
+
([Calling C](#calling-c)). Calling one without the import is NL2456. No flag turns the checks off for a
|
|
250
|
+
whole program: `--unchecked-indexing` and `--wrapping` were removed before 1.0
|
|
251
|
+
and are refused with exit 2.
|
|
258
252
|
|
|
259
253
|
```ts nish:ok
|
|
260
254
|
import { uncheckedGet, wrappingAdd, wrappingMul } from "nish:unsafe";
|
|
@@ -280,8 +274,7 @@ where it can prove the result fits — a loop counter under `i < n`, a byte
|
|
|
280
274
|
widened with `toI32`, an `integer<Lo, Hi>`, a value masked with `& c`. Arithmetic
|
|
281
275
|
that is *meant* to wrap — a hash, a PRNG, a checksum — goes in `u32` or `u64`,
|
|
282
276
|
which always wrap and are never checked; convert back with `toI32` (free), or
|
|
283
|
-
call `wrappingAdd` / `wrappingSub` / `wrappingMul` at the site.
|
|
284
|
-
`--wrapping` makes every signed operation of your own package wrap instead.
|
|
277
|
+
call `wrappingAdd` / `wrappingSub` / `wrappingMul` at the site.
|
|
285
278
|
|
|
286
279
|
```ts nish:ok
|
|
287
280
|
// FNV-1a: the multiply overflows on purpose, so the state is a `u32`.
|
|
@@ -2140,13 +2133,20 @@ a `\u` escape `nish/json` decodes) is one unit per byte, and counts its bytes.
|
|
|
2140
2133
|
**Files and the system.** `readFileSync(path)` (exits 1 on failure),
|
|
2141
2134
|
`readFileSyncOrNull(path)` (`string | null`), `readFileBytesSync(path)`
|
|
2142
2135
|
(`u8[] | null`, the bytes as they are on disk), `writeFileSync(path, data)`,
|
|
2143
|
-
`appendFileSync(path, data)`, `
|
|
2136
|
+
`appendFileSync(path, data)`, `writeFileBytesSync(path, bytes)` (a `u8[]` as
|
|
2137
|
+
it is; `i32`: the length, or a negative errno), `renameSync(from, to)` (`0` or
|
|
2138
|
+
a negative errno; atomic on one filesystem, `-18` across two, so write the
|
|
2139
|
+
temporary beside the target), `unlinkSync(path)` (`0` or a negative errno; a
|
|
2140
|
+
directory is refused), `writeBytes(bytes, off, len)` (a `u8[]` range to stdout
|
|
2141
|
+
as it is, the length or a negative errno; the range panics like
|
|
2142
|
+
`netWrite`'s), `mkdirSync(path)` (one level, `boolean`),
|
|
2144
2143
|
`isDirectorySync(path)`, `readdirSync(path)` (`string[] | null`, sorted by
|
|
2145
2144
|
bytes, no `.`/`..`), `spawnSync(argv)`, `spawnSyncTo(argv, outPath, errPath)`,
|
|
2146
2145
|
`getenv(name)` (`string | null` — unset and empty are different answers),
|
|
2147
2146
|
`realpathSync(path)` (`string | null`; symbolic links resolved, absolute, and
|
|
2148
2147
|
`null` when it does not resolve),
|
|
2149
|
-
`monotonicNanos()` (`i64`; elapsed time only),
|
|
2148
|
+
`monotonicNanos()` (`i64`; elapsed time only), `wallClockNanos()` (`i64`,
|
|
2149
|
+
nanoseconds since the Unix epoch; the wall clock, which can step), and, for checking a path before
|
|
2150
2150
|
trusting it, `lstatOwnerModeSync(path)` (an `i64`: the owner's uid in the high
|
|
2151
2151
|
32 bits, `st_mode` in the low 32, `-1` when it does not resolve; a symbolic
|
|
2152
2152
|
link answers for itself), `geteuid()` (`i64`) and `isExecutableSync(path)`
|
|
@@ -2174,6 +2174,12 @@ also watches the signal fd. None of `spawn`, `kill`, `waitPid` may be called in
|
|
|
2174
2174
|
a task or a parallel body; the two host facts may. `availableParallelism()` (`i32`, at least 1, no `--threads` needed) and
|
|
2175
2175
|
`totalMemory()` (`i64` bytes, or `-1`) size a program to the machine. None of
|
|
2176
2176
|
the five exists on a wasm target, and `spawn` and `waitPid` throw under Node.
|
|
2177
|
+
`setNice(n)` sets the nice value (clamped to -20..19; lowering it unprivileged
|
|
2178
|
+
is `-13`) and `setIdlePriority()` moves the program to Linux's `SCHED_IDLE`
|
|
2179
|
+
(`-95` on Darwin); each answers 0 or a negative errno. Call them at the top of
|
|
2180
|
+
`main`, never in a task. An unprivileged process can never leave `SCHED_IDLE`
|
|
2181
|
+
and an idle one can starve on a saturated machine, so keep a normal-priority
|
|
2182
|
+
fallback. Neither exists on wasm; both throw under Node.
|
|
2177
2183
|
|
|
2178
2184
|
**Sockets** (`nish:net`, or the same names as globals). `tcpListen(host, port,
|
|
2179
2185
|
backlog)`, `tcpAccept(fd, peer)`, `netRead(fd, buf, off, len)`,
|
|
@@ -2186,7 +2192,8 @@ and there is no `async` and no callback: your `main` is the loop. A client calls
|
|
|
2186
2192
|
`tcpConnect(addr)`, which answers the descriptor while it is still connecting;
|
|
2187
2193
|
wait for it to be writable, then `connectResult(fd)` is 0 or the failure. Buffers
|
|
2188
2194
|
are `u8[]` only; an address is 18 bytes of one (IPv4 as `::ffff:a.b.c.d`, then
|
|
2189
|
-
the port big-endian); hosts are numeric literals,
|
|
2195
|
+
the port big-endian); hosts are numeric literals; to reach a name, resolve it first with
|
|
2196
|
+
`nish/net/dns` (`DnsClient` on your loop, or the blocking `resolveHost`).
|
|
2190
2197
|
`netRead` / `netWrite` panic on a range outside the buffer.
|
|
2191
2198
|
`tcpListenFlags(host, port, backlog, flags)` is `tcpListen` with flag 1
|
|
2192
2199
|
`SO_REUSEPORT`, so several listeners, in one process or many, share a port (Linux
|
|
@@ -2205,11 +2212,18 @@ loop, `pollAdd(loop, fd, events, token)` / `pollModify(...)` watch `fd` for
|
|
|
2205
2212
|
for `-1`), fills `ready: i32[]` with pairs — `ready[2k]` a token,
|
|
2206
2213
|
`ready[2k + 1]` its events, 4 added for a hang-up or error — and answers how
|
|
2207
2214
|
many, **0 on a timeout or a signal**. It is level-triggered, so what you leave
|
|
2208
|
-
unread is reported again. Add `signalFd()` to the loop to wake on SIGTERM.
|
|
2215
|
+
unread is reported again. Add `signalFd()` to the loop to wake on SIGTERM.
|
|
2216
|
+
`netSetOption(fd, option, value)` sets one socket option from a whitelist:
|
|
2217
|
+
`NET_TCP_NODELAY`, `NET_TCP_QUICKACK` (Linux; `-95` on Darwin),
|
|
2218
|
+
`NET_SO_KEEPALIVE`, `NET_SO_SNDBUF` and `NET_SO_RCVBUF`, `i32` ids exported
|
|
2219
|
+
beside it; any other id is `-22`. Linux drops quick-ACK mode on its own, so set
|
|
2220
|
+
it again after each response. The ids are not module constants: pass them
|
|
2221
|
+
directly. None
|
|
2209
2222
|
exists on a wasm target, and under Node each throws.
|
|
2210
2223
|
|
|
2211
2224
|
**Arena.** `Arena.mark()`, `Arena.used()` and `using a = arena()` — see below.
|
|
2212
|
-
`Arena.release(m)` and `Arena.reset()`
|
|
2225
|
+
`Arena.release(m)` and `Arena.reset()` were removed before 1.0: write
|
|
2226
|
+
`using a = arena()`.
|
|
2213
2227
|
|
|
2214
2228
|
### Map and Set
|
|
2215
2229
|
|
|
@@ -2494,10 +2508,9 @@ anything that runs for a long time, should compile with `--deny-retention`,
|
|
|
2494
2508
|
which makes this warning and the three like it errors.
|
|
2495
2509
|
|
|
2496
2510
|
Everything else is bumped from the arena, which is released when `main`
|
|
2497
|
-
returns.
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
with a `deprecation` warning (NL7001) pointing to `using a = arena()`.
|
|
2511
|
+
returns. To give memory back sooner, scope it with `using a = arena()`;
|
|
2512
|
+
`Arena.release` and `Arena.reset` are gone, and a call to either is an unknown
|
|
2513
|
+
builtin (NL2244).
|
|
2501
2514
|
|
|
2502
2515
|
## Recipes for what is missing
|
|
2503
2516
|
|
|
@@ -2584,6 +2597,21 @@ most 100 pairs. (No block, as for `nish/text`; `tests/link/std_url` is its
|
|
|
2584
2597
|
program, and the user guide's [standard-library
|
|
2585
2598
|
page](guide/using-nish/standard-library.md#nishneturl) its walk-through.)
|
|
2586
2599
|
|
|
2600
|
+
**Gzip a response, or read a gzipped body** — there is no `node:zlib` and no
|
|
2601
|
+
`CompressionStream`: `nish/compress/gzip` (and `zlib`, `deflate`) compress
|
|
2602
|
+
and decompress into buffers you own. Build a `GzipCompressor(DEFLATE_FAST)`
|
|
2603
|
+
once and call `compress(src, off, len, out, at)`, which answers the end or -1
|
|
2604
|
+
when `out` is shorter than `compressBound(len)`; `GzipDecompressor`'s
|
|
2605
|
+
`decompress(..., limit)` never writes more than `limit` bytes and answers a
|
|
2606
|
+
negative `DEFLATE_ERR_*` / `GZIP_ERR_*` code for any malformed stream, so test
|
|
2607
|
+
it before reading `out`. `GzipWriter` and `GzipReader` stream. On HTTP/1.1,
|
|
2608
|
+
`conn.respondEncoded(status, names, values, body, off, len, enc,
|
|
2609
|
+
conn.acceptEncoding())` with an `HttpCompressor` from `nish/net/http-compress`
|
|
2610
|
+
negotiates, compresses and sets the fields. Do not compress a secret beside
|
|
2611
|
+
text the client chose (BREACH). (No block, as for `nish/text`;
|
|
2612
|
+
`tests/link/std_compress` is its program, and the user guide's
|
|
2613
|
+
[compression page](guide/using-nish/compression.md) its walk-through.)
|
|
2614
|
+
|
|
2587
2615
|
**Pass a list to a PostgreSQL statement** — there is no driver that expands
|
|
2588
2616
|
`IN (...)` and no `pg`'s array serialiser: in `nish/net/postgres`, write
|
|
2589
2617
|
`= any($1::int8[])` or `unnest($1::text[])` and bind the list as one
|
|
@@ -2731,8 +2759,7 @@ export const at = (xs: i32[], i: i32): i32 => xs[i];
|
|
|
2731
2759
|
A call into the standard library or a dependency that may panic is refused at
|
|
2732
2760
|
the call (NL2458), because only its own author can prove it: call a function
|
|
2733
2761
|
that cannot panic, or make the call from a module outside the scope.
|
|
2734
|
-
|
|
2735
|
-
still refused. `uncheckedGet` and `uncheckedSet` are not proofs either, but
|
|
2762
|
+
`uncheckedGet` and `uncheckedSet` are not proofs, but
|
|
2736
2763
|
they cannot panic, and their `nish:unsafe` import is the module's visible
|
|
2737
2764
|
opt-in to an index out of range being undefined behaviour, so the scope allows
|
|
2738
2765
|
them (as `"allowed": true` sites) and a call into them is never refused:
|
package/package.json
CHANGED
package/runtime/browser.mjs
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* - `exit(code)` throws a `NishExit` carrying the code, since a page cannot
|
|
11
11
|
* end itself; the caller that started the program catches it, and does
|
|
12
12
|
* not call the module again (docs/security/runtime-browser.md, BROWSER-2).
|
|
13
|
-
* - `monotonicNanos` reads `performance.now()`, `
|
|
14
|
-
* `
|
|
15
|
-
* is the program alone.
|
|
13
|
+
* - `monotonicNanos` reads `performance.now()`, `wallClockNanos` `Date.now()`,
|
|
14
|
+
* `getenv` answers `null`, `isTerminal` answers `false` and `terminalSize`
|
|
15
|
+
* `[-25, 0]`, and `argv()` is the program alone.
|
|
16
16
|
* - Every file-system, process and machine call panics, naming itself and
|
|
17
17
|
* the browser.
|
|
18
18
|
*/
|
|
@@ -69,6 +69,7 @@ installHost({
|
|
|
69
69
|
call(name) {
|
|
70
70
|
if (name === "getenv") return null;
|
|
71
71
|
if (name === "monotonicNanos") return BigInt(Math.round(performance.now() * 1e6));
|
|
72
|
+
if (name === "wallClockNanos") return BigInt(Date.now()) * 1000000n;
|
|
72
73
|
// A page has no terminal, which `nish:term` answers as a pipe does.
|
|
73
74
|
if (name === "isTerminal") return false;
|
|
74
75
|
if (name === "terminalSize") return [-25, 0];
|
package/runtime/core/arrays.d.ts
CHANGED
|
@@ -15,8 +15,6 @@ export declare function arraySetAt<T>(dst: T[], src: readonly T[], offset?: numb
|
|
|
15
15
|
export declare function newArray<T>(n: number | bigint, zero: unknown): T[];
|
|
16
16
|
export declare function arenaUsed(): i64;
|
|
17
17
|
export declare function arenaMark(): i64;
|
|
18
|
-
export declare function arenaRelease(m?: unknown): void;
|
|
19
|
-
export declare function arenaReset(): void;
|
|
20
18
|
/**
|
|
21
19
|
* The protocol `using` reads, declared here as `nish.d.ts` does, so `using a =
|
|
22
20
|
* arena()` type-checks with no `"esnext.disposable"` in a program's `lib`.
|
package/runtime/core/arrays.mjs
CHANGED
|
@@ -145,8 +145,6 @@ export function newArray(n, zero) {
|
|
|
145
145
|
// that is when this file grows a second arena to keep count of.
|
|
146
146
|
export function arenaUsed() { return 0n; }
|
|
147
147
|
export function arenaMark() { return 0n; }
|
|
148
|
-
export function arenaRelease() {}
|
|
149
|
-
export function arenaReset() {}
|
|
150
148
|
/** `using a = arena()`: a disposable whose disposal does nothing, because there is no arena to release. */
|
|
151
149
|
export function arena() {
|
|
152
150
|
return { [Symbol.dispose]() {} };
|
package/runtime/core/host.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare function log(x: unknown): void;
|
|
|
35
35
|
export declare function error(x: unknown): void;
|
|
36
36
|
export declare function write(s: string): void;
|
|
37
37
|
export declare function writeError(s: string): void;
|
|
38
|
+
export declare function writeBytes(bytes: readonly u8[], off: i32, len: i32): i32;
|
|
38
39
|
export declare function panic(message: string): never;
|
|
39
40
|
export declare function exit(code: i32): never;
|
|
40
41
|
export declare function argv(): string[];
|
|
@@ -43,6 +44,9 @@ export declare function readFileSyncOrNull(path: string): string | null;
|
|
|
43
44
|
export declare function readFileBytesSync(path: string): u8[] | null;
|
|
44
45
|
export declare function writeFileSync(path: string, data: string): void;
|
|
45
46
|
export declare function appendFileSync(path: string, data: string): void;
|
|
47
|
+
export declare function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
48
|
+
export declare function renameSync(from: string, to: string): i32;
|
|
49
|
+
export declare function unlinkSync(path: string): i32;
|
|
46
50
|
export declare function mkdirSync(path: string): boolean;
|
|
47
51
|
export declare function isDirectorySync(path: string): boolean;
|
|
48
52
|
export declare function readdirSync(path: string): string[] | null;
|
|
@@ -56,6 +60,7 @@ export declare function kill(pid: i32, sig: i32): i32;
|
|
|
56
60
|
export declare function availableParallelism(): i32;
|
|
57
61
|
export declare function totalMemory(): i64;
|
|
58
62
|
export declare function monotonicNanos(): i64;
|
|
63
|
+
export declare function wallClockNanos(): i64;
|
|
59
64
|
export declare function statMtimeSync(path: string): f64;
|
|
60
65
|
export declare function lstatOwnerModeSync(path: string): i64;
|
|
61
66
|
export declare function geteuid(): i64;
|
|
@@ -69,6 +74,9 @@ export declare function spawn(argv: string[]): i32;
|
|
|
69
74
|
export declare function waitPid(pid: i32, flags: i32): i32[];
|
|
70
75
|
export declare function signalFd(): i32;
|
|
71
76
|
export declare function readSignal(fd: i32): i32;
|
|
77
|
+
/** These two have no reading under Node: each throws, naming itself. */
|
|
78
|
+
export declare function setNice(n: i32): i32;
|
|
79
|
+
export declare function setIdlePriority(): i32;
|
|
72
80
|
/** What each `nish:net` function is under the core: a throw naming `name`. */
|
|
73
81
|
export declare function noNetReading(name: string): never;
|
|
74
82
|
/** What `readStdin`, `waitStdin`, `setRawMode` and `setTerminalRestore` are under the core: a throw naming `name`. */
|
package/runtime/core/host.mjs
CHANGED
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
* its own fails loudly rather than writing nowhere.
|
|
16
16
|
*
|
|
17
17
|
* The functions that have no synchronous reading under Node (`spawn`,
|
|
18
|
-
* `waitPid`, `signalFd`, `readSignal`, the `nish:net` set)
|
|
19
|
-
* asking any
|
|
18
|
+
* `waitPid`, `signalFd`, `readSignal`, the `nish:net` set), and `setNice` and
|
|
19
|
+
* `setIdlePriority`, which have none at all, throw here without asking any
|
|
20
|
+
* host.
|
|
20
21
|
*/
|
|
22
|
+
import { panicSlice, toIndex } from "./checks.mjs";
|
|
21
23
|
|
|
22
24
|
/** What every member of the default host throws: no adapter has been imported. */
|
|
23
25
|
export class NishNoHost extends Error {
|
|
@@ -86,6 +88,18 @@ export function writeError(s) {
|
|
|
86
88
|
host.writeErr(s);
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
/**
|
|
92
|
+
* `writeBytes(bytes, off, len)`: the range checked as the compiled call checks
|
|
93
|
+
* it, `0 <= off <= off + len <= bytes.length`, through the slice panic, then
|
|
94
|
+
* the host's write of those bytes to stdout.
|
|
95
|
+
*/
|
|
96
|
+
export function writeBytes(bytes, off, len) {
|
|
97
|
+
const from = toIndex(off);
|
|
98
|
+
const to = from + toIndex(len);
|
|
99
|
+
if (!(from >= 0 && from <= to && to <= bytes.length)) panicSlice(from, to, bytes.length);
|
|
100
|
+
return host.call("writeBytes", [bytes, from, to]);
|
|
101
|
+
}
|
|
102
|
+
|
|
89
103
|
/** `panic(message)`: the message on stderr, then exit 1. */
|
|
90
104
|
export function panic(message) {
|
|
91
105
|
host.writeErr(`${message}\n`);
|
|
@@ -131,6 +145,18 @@ export function appendFileSync(path, data) {
|
|
|
131
145
|
return host.call("appendFileSync", [path, data]);
|
|
132
146
|
}
|
|
133
147
|
|
|
148
|
+
export function writeFileBytesSync(path, bytes) {
|
|
149
|
+
return host.call("writeFileBytesSync", [path, bytes]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function renameSync(from, to) {
|
|
153
|
+
return host.call("renameSync", [from, to]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function unlinkSync(path) {
|
|
157
|
+
return host.call("unlinkSync", [path]);
|
|
158
|
+
}
|
|
159
|
+
|
|
134
160
|
export function mkdirSync(path) {
|
|
135
161
|
return host.call("mkdirSync", [path]);
|
|
136
162
|
}
|
|
@@ -183,6 +209,10 @@ export function monotonicNanos() {
|
|
|
183
209
|
return host.call("monotonicNanos", []);
|
|
184
210
|
}
|
|
185
211
|
|
|
212
|
+
export function wallClockNanos() {
|
|
213
|
+
return host.call("wallClockNanos", []);
|
|
214
|
+
}
|
|
215
|
+
|
|
186
216
|
export function statMtimeSync(path) {
|
|
187
217
|
return host.call("statMtimeSync", [path]);
|
|
188
218
|
}
|
|
@@ -259,6 +289,24 @@ export function waitPid() {
|
|
|
259
289
|
return noProcessReading("waitPid");
|
|
260
290
|
}
|
|
261
291
|
|
|
292
|
+
/**
|
|
293
|
+
* `setNice(n)` and `setIdlePriority()` have no reading under Node: Node has one
|
|
294
|
+
* of the nice value (`os.setPriority`) but none of `SCHED_IDLE`, and a reading
|
|
295
|
+
* of one without the other would differ from the native build on the same host.
|
|
296
|
+
* Each throws, naming itself, as `runtime/nish.mjs` does.
|
|
297
|
+
*/
|
|
298
|
+
function noSchedulingReading(name) {
|
|
299
|
+
throw new Error(`\`${name}\` has no reading under Node: it changes how the kernel schedules the native program`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function setNice() {
|
|
303
|
+
return noSchedulingReading("setNice");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function setIdlePriority() {
|
|
307
|
+
return noSchedulingReading("setIdlePriority");
|
|
308
|
+
}
|
|
309
|
+
|
|
262
310
|
/**
|
|
263
311
|
* `signalFd()` and `readSignal(fd)` have no faithful reading under Node, and
|
|
264
312
|
* these say so rather than answer something else. Node delivers a signal to
|
package/runtime/core/numbers.mjs
CHANGED
|
@@ -90,7 +90,7 @@ export function ctEq(a, b) {
|
|
|
90
90
|
// ---- Checked arithmetic (wp33 R4) ----------------------------------------------------
|
|
91
91
|
//
|
|
92
92
|
// What `--emit-ts` writes for a signed `+ - *` and unary minus the compiler
|
|
93
|
-
//
|
|
93
|
+
// did not prove in range: the exact
|
|
94
94
|
// answer, or the panic `nish_panic_overflow` prints, in its words. An i32 is
|
|
95
95
|
// exact in a double until it leaves the range, and `Math.imul` is the
|
|
96
96
|
// wrapped product, so a product that differs from the double one overflowed.
|
|
@@ -121,8 +121,7 @@ function checkI64(r, op) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// `wrappingAdd`, `wrappingSub` and `wrappingMul` from `nish:unsafe`: the
|
|
124
|
-
// two's-complement answer at the operands' width, i32 or i64
|
|
125
|
-
// `--wrapping` says.
|
|
124
|
+
// two's-complement answer at the operands' width, i32 or i64.
|
|
126
125
|
export function wrappingAdd(a, b) {
|
|
127
126
|
return typeof a === "bigint" ? BigInt.asIntN(64, a + b) : (a + b) | 0;
|
|
128
127
|
}
|
package/runtime/nish.d.ts
CHANGED
|
@@ -227,6 +227,12 @@ declare function readCString(p: CPtr): string;
|
|
|
227
227
|
declare function write(s: string): void;
|
|
228
228
|
/** `s` to stderr, likewise. */
|
|
229
229
|
declare function writeError(s: string): void;
|
|
230
|
+
/**
|
|
231
|
+
* `bytes[off .. off + len)` to stdout, every byte as it is, partial writes and
|
|
232
|
+
* interruptions retried: the count, or a negative errno. A range outside
|
|
233
|
+
* `bytes` panics, as `dst.set` does. The shape is `netWrite`'s.
|
|
234
|
+
*/
|
|
235
|
+
declare function writeBytes(bytes: readonly u8[], off: i32, len: i32): i32;
|
|
230
236
|
/**
|
|
231
237
|
* `message` and a newline to stderr, then exit 1. Terminates control flow, so
|
|
232
238
|
* it is `never`: that is what lets `tsc` agree that a function ending in a
|
|
@@ -257,6 +263,19 @@ declare function readFileBytesSync(path: string): u8[] | null;
|
|
|
257
263
|
declare function writeFileSync(path: string, data: string): void;
|
|
258
264
|
/** `data` added at the end of the file, with the same refusal of a symbolic link. */
|
|
259
265
|
declare function appendFileSync(path: string, data: string): void;
|
|
266
|
+
/**
|
|
267
|
+
* The file's contents replaced by `bytes`, created at 0644 when missing, with
|
|
268
|
+
* the same refusal of a symbolic link: the byte count, or a negative errno.
|
|
269
|
+
* Never an exit.
|
|
270
|
+
*/
|
|
271
|
+
declare function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
272
|
+
/**
|
|
273
|
+
* `from` moved to `to`, replacing a file there in one step on one file system:
|
|
274
|
+
* 0, or a negative errno (-18 across file systems, -2 for a missing `from`).
|
|
275
|
+
*/
|
|
276
|
+
declare function renameSync(from: string, to: string): i32;
|
|
277
|
+
/** The name `path` removed: 0, or a negative errno. A directory is refused, not removed. */
|
|
278
|
+
declare function unlinkSync(path: string): i32;
|
|
260
279
|
/** One directory, not recursive; whether a directory is there afterwards. */
|
|
261
280
|
declare function mkdirSync(path: string): boolean;
|
|
262
281
|
/** Whether a directory is at `path` right now. One `stat`, and never an exit. */
|
|
@@ -302,6 +321,21 @@ declare function waitPid(pid: i32, flags: i32): i32[];
|
|
|
302
321
|
declare function availableParallelism(): i32;
|
|
303
322
|
/** Bytes of physical memory, or -1 when the host does not say. */
|
|
304
323
|
declare function totalMemory(): i64;
|
|
324
|
+
/**
|
|
325
|
+
* Set this process's nice value to `n`: 0, or a negative errno. The kernel
|
|
326
|
+
* clamps `n` to -20..19, and lowering it below the current value needs the
|
|
327
|
+
* privilege to (-13 without it on Linux). Children started afterwards inherit
|
|
328
|
+
* it. Throws under Node.
|
|
329
|
+
*/
|
|
330
|
+
declare function setNice(n: i32): i32;
|
|
331
|
+
/**
|
|
332
|
+
* Move this process to `SCHED_IDLE`, which runs it only when nothing else wants
|
|
333
|
+
* the CPU: 0, or a negative errno, and -95 where the host has no such policy
|
|
334
|
+
* (Darwin). An unprivileged process can never leave `SCHED_IDLE` again, and an
|
|
335
|
+
* idle process can starve on a saturated machine, so a caller keeps a
|
|
336
|
+
* normal-priority fallback for work that must finish. Throws under Node.
|
|
337
|
+
*/
|
|
338
|
+
declare function setIdlePriority(): i32;
|
|
305
339
|
/** One environment variable, or `null` when it is unset (an empty value is a set variable). */
|
|
306
340
|
declare function getenv(name: string): string | null;
|
|
307
341
|
/**
|
|
@@ -309,6 +343,11 @@ declare function getenv(name: string): string | null;
|
|
|
309
343
|
* origin is arbitrary, so only the difference between two reads is meaningful.
|
|
310
344
|
*/
|
|
311
345
|
declare function monotonicNanos(): i64;
|
|
346
|
+
/**
|
|
347
|
+
* The wall clock in nanoseconds since the Unix epoch, for dating an event. It
|
|
348
|
+
* steps when the system time is set, so a duration is `monotonicNanos`'s.
|
|
349
|
+
*/
|
|
350
|
+
declare function wallClockNanos(): i64;
|
|
312
351
|
/**
|
|
313
352
|
* The modification time of `path` in milliseconds since the epoch, with the
|
|
314
353
|
* sub-millisecond fraction the file system keeps (Node's `mtimeMs`), or NaN
|
|
@@ -381,6 +420,26 @@ declare function pollModify(loop: i32, fd: i32, events: i32, token: i32): i32;
|
|
|
381
420
|
declare function pollRemove(loop: i32, fd: i32): i32;
|
|
382
421
|
/** Wait up to `timeoutMs` (negative: forever); pairs `ready[2k]` token, `ready[2k + 1]` events (4: hang-up or error). */
|
|
383
422
|
declare function pollWait(loop: i32, ready: i32[], timeoutMs: i32): i32;
|
|
423
|
+
/**
|
|
424
|
+
* Set one socket option from a whitelist: `option` is one of the `NET_*` ids
|
|
425
|
+
* below, and any other is -22; there is no raw level and option number. 0, or
|
|
426
|
+
* a negative errno (-9 a closed descriptor, -88 not a socket).
|
|
427
|
+
*/
|
|
428
|
+
declare function netSetOption(fd: i32, option: i32, value: i32): i32;
|
|
429
|
+
/** `TCP_NODELAY`: `value` 1 sends a small write at once, 0 lets Nagle batch it. */
|
|
430
|
+
declare const NET_TCP_NODELAY: i32;
|
|
431
|
+
/**
|
|
432
|
+
* `TCP_QUICKACK` (Linux; -95 elsewhere): `value` 1 acknowledges at once, 0
|
|
433
|
+
* delays. Linux does not keep quick-ACK mode: the kernel leaves it on its own,
|
|
434
|
+
* so a server that wants a mode sets it again after every response.
|
|
435
|
+
*/
|
|
436
|
+
declare const NET_TCP_QUICKACK: i32;
|
|
437
|
+
/** `SO_KEEPALIVE`: `value` 1 probes an idle connection, 0 does not. */
|
|
438
|
+
declare const NET_SO_KEEPALIVE: i32;
|
|
439
|
+
/** `SO_SNDBUF`: the send buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
440
|
+
declare const NET_SO_SNDBUF: i32;
|
|
441
|
+
/** `SO_RCVBUF`: the receive buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
442
|
+
declare const NET_SO_RCVBUF: i32;
|
|
384
443
|
|
|
385
444
|
// ---- `nish:term` ------------------------------------------------------------------
|
|
386
445
|
//
|
|
@@ -441,6 +500,12 @@ declare module "nish:fs" {
|
|
|
441
500
|
export function writeFileSync(path: string, data: string): void;
|
|
442
501
|
/** Refuses a symbolic link as the last component of the path, as the global does. */
|
|
443
502
|
export function appendFileSync(path: string, data: string): void;
|
|
503
|
+
/** The byte count, or a negative errno; refuses a symbolic link as `writeFileSync` does. */
|
|
504
|
+
export function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
505
|
+
/** 0, or a negative errno: -18 across file systems. */
|
|
506
|
+
export function renameSync(from: string, to: string): i32;
|
|
507
|
+
/** 0, or a negative errno: a directory is refused, not removed. */
|
|
508
|
+
export function unlinkSync(path: string): i32;
|
|
444
509
|
/** `true` when the directory was created, `false` when it already existed. */
|
|
445
510
|
export function mkdirSync(path: string): boolean;
|
|
446
511
|
export function isDirectorySync(path: string): boolean;
|
|
@@ -475,11 +540,21 @@ declare module "nish:process" {
|
|
|
475
540
|
export function availableParallelism(): i32;
|
|
476
541
|
/** Bytes of physical memory, or -1. */
|
|
477
542
|
export function totalMemory(): i64;
|
|
543
|
+
/** Set this process's nice value (clamped to -20..19): 0, or a negative errno. */
|
|
544
|
+
export function setNice(n: i32): i32;
|
|
545
|
+
/**
|
|
546
|
+
* Move this process to `SCHED_IDLE`: 0, a negative errno, or -95 off Linux.
|
|
547
|
+
* An unprivileged process can never leave it, and an idle process can starve
|
|
548
|
+
* on a saturated machine, so a caller keeps a normal-priority fallback.
|
|
549
|
+
*/
|
|
550
|
+
export function setIdlePriority(): i32;
|
|
478
551
|
/**
|
|
479
552
|
* A monotonic clock in nanoseconds. The origin is arbitrary, so only the
|
|
480
553
|
* difference between two reads is meaningful.
|
|
481
554
|
*/
|
|
482
555
|
export function monotonicNanos(): i64;
|
|
556
|
+
/** The wall clock in nanoseconds since the Unix epoch; it steps when the system time is set. */
|
|
557
|
+
export function wallClockNanos(): i64;
|
|
483
558
|
/** A descriptor readable when SIGTERM or SIGINT arrives; the same one every call, or -1. */
|
|
484
559
|
export function signalFd(): i32;
|
|
485
560
|
/** Block until SIGTERM or SIGINT arrives: 15 or 2, or -1 for a descriptor that is not `signalFd()`'s. */
|
|
@@ -538,6 +613,26 @@ declare module "nish:net" {
|
|
|
538
613
|
export function pollRemove(loop: i32, fd: i32): i32;
|
|
539
614
|
/** Wait up to `timeoutMs` (negative: forever); pairs `ready[2k]` token, `ready[2k + 1]` events (4: hang-up or error). */
|
|
540
615
|
export function pollWait(loop: i32, ready: i32[], timeoutMs: i32): i32;
|
|
616
|
+
/**
|
|
617
|
+
* Set one socket option from a whitelist: `option` is one of the `NET_*` ids
|
|
618
|
+
* below, and any other is -22; there is no raw level and option number. 0, or
|
|
619
|
+
* a negative errno (-9 a closed descriptor, -88 not a socket).
|
|
620
|
+
*/
|
|
621
|
+
export function netSetOption(fd: i32, option: i32, value: i32): i32;
|
|
622
|
+
/** `TCP_NODELAY`: `value` 1 sends a small write at once, 0 lets Nagle batch it. */
|
|
623
|
+
export const NET_TCP_NODELAY: i32;
|
|
624
|
+
/**
|
|
625
|
+
* `TCP_QUICKACK` (Linux; -95 elsewhere): `value` 1 acknowledges at once, 0
|
|
626
|
+
* delays. Linux does not keep quick-ACK mode: the kernel leaves it on its own,
|
|
627
|
+
* so a server that wants a mode sets it again after every response.
|
|
628
|
+
*/
|
|
629
|
+
export const NET_TCP_QUICKACK: i32;
|
|
630
|
+
/** `SO_KEEPALIVE`: `value` 1 probes an idle connection, 0 does not. */
|
|
631
|
+
export const NET_SO_KEEPALIVE: i32;
|
|
632
|
+
/** `SO_SNDBUF`: the send buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
633
|
+
export const NET_SO_SNDBUF: i32;
|
|
634
|
+
/** `SO_RCVBUF`: the receive buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
635
|
+
export const NET_SO_RCVBUF: i32;
|
|
541
636
|
}
|
|
542
637
|
|
|
543
638
|
declare module "nish:unsafe" {
|
|
@@ -560,6 +655,11 @@ declare module "nish:io" {
|
|
|
560
655
|
export function write(s: string): void;
|
|
561
656
|
/** The same, on stderr. */
|
|
562
657
|
export function writeError(s: string): void;
|
|
658
|
+
/**
|
|
659
|
+
* `bytes[off .. off + len)` to stdout, every byte as it is: the count, or a
|
|
660
|
+
* negative errno. A range outside `bytes` panics, as `dst.set` does.
|
|
661
|
+
*/
|
|
662
|
+
export function writeBytes(bytes: readonly u8[], off: i32, len: i32): i32;
|
|
563
663
|
/** `message` to stderr, then exit 1. A terminator, as `process.exit` is. */
|
|
564
664
|
export function panic(message: string): never;
|
|
565
665
|
}
|
|
@@ -617,10 +717,6 @@ declare module "nish:secret" {
|
|
|
617
717
|
declare const Arena: {
|
|
618
718
|
/** The current bump address. */
|
|
619
719
|
mark(): i64;
|
|
620
|
-
/** Free everything allocated since `m`. */
|
|
621
|
-
release(m: i64): void;
|
|
622
|
-
/** Recycle everything in O(1), keeping the newest chunk. */
|
|
623
|
-
reset(): void;
|
|
624
720
|
/** Bytes bumped in the current chunk. */
|
|
625
721
|
used(): i64;
|
|
626
722
|
};
|
package/runtime/nish.h
CHANGED
|
@@ -396,6 +396,22 @@ nish_array *nish_readdir(const nish_str *path);
|
|
|
396
396
|
* and the bytes live in the arena, so copy what you keep before the next reset
|
|
397
397
|
* or release. */
|
|
398
398
|
nish_array *nish_read_file_bytes(const nish_str *path);
|
|
399
|
+
/* `renameSync(from, to)`, `unlinkSync(path)` and `writeFileBytesSync(path,
|
|
400
|
+
* bytes)`: rename(2), unlink(2), and every element of a `u8[]` written to a
|
|
401
|
+
* file opened as `nish_write_file` opens it (`O_TRUNC`, 0644, a link refused).
|
|
402
|
+
* None exits: each answers 0 (the write, the length written) or a negative
|
|
403
|
+
* errno — -EXDEV for a rename across file systems, which moves nothing, and
|
|
404
|
+
* -EISDIR or -EPERM for a directory handed to `nish_unlink`, which never calls
|
|
405
|
+
* rmdir. A path holding a NUL answers -EINVAL, not -ENOENT, so a program can
|
|
406
|
+
* tell a malformed path from a missing file. */
|
|
407
|
+
int32_t nish_rename(const nish_str *from, const nish_str *to);
|
|
408
|
+
int32_t nish_unlink(const nish_str *path);
|
|
409
|
+
int32_t nish_write_file_bytes(const nish_str *path, const nish_array *bytes);
|
|
410
|
+
/* `writeBytes(bytes, off, len)` (nish:io), in runtime-os.c: the bytes
|
|
411
|
+
* `[off, off + len)` of a `u8[]` on stdout, a short write finished and `EINTR`
|
|
412
|
+
* retried. The caller has range-checked `off` and `len`. Answers `len`, or the
|
|
413
|
+
* negative errno of the write that failed; never exits. */
|
|
414
|
+
int32_t nish_write_bytes(const nish_array *bytes, int64_t off, int64_t len);
|
|
399
415
|
/* `spawnSync(argv)`: run element 0 of `argv` (searched on `PATH`) with `argv`
|
|
400
416
|
* as its argument vector, wait for it, and answer its exit status, or
|
|
401
417
|
* `128 + n` when signal `n` killed it. -1 when `argv` is empty, when the
|
|
@@ -438,6 +454,15 @@ nish_array *nish_wait_pid(int32_t pid, int32_t flags);
|
|
|
438
454
|
/* `totalMemory()`: bytes of physical memory (Linux: `_SC_PHYS_PAGES` times the
|
|
439
455
|
* page size; Darwin: `hw.memsize`), or -1 where the host does not say. */
|
|
440
456
|
int64_t nish_total_memory(void);
|
|
457
|
+
/* `setNice(n)`: this process's nice value set to `n` (`setpriority`), 0 or a
|
|
458
|
+
* negative errno. The kernel clamps `n` to -20..19; lowering it below the
|
|
459
|
+
* current value without the privilege is -EACCES (or -EPERM). -ENOSYS under
|
|
460
|
+
* WASI. */
|
|
461
|
+
int32_t nish_set_nice(int32_t n);
|
|
462
|
+
/* `setIdlePriority()`: this process moved to `SCHED_IDLE` (Linux), 0 or a
|
|
463
|
+
* negative errno; -95 (Linux's EOPNOTSUPP) elsewhere, -ENOSYS under WASI. An
|
|
464
|
+
* unprivileged process cannot leave the policy again. */
|
|
465
|
+
int32_t nish_set_idle_priority(void);
|
|
441
466
|
|
|
442
467
|
/* ---- The environment (WP19 R1) ------------------------------------------
|
|
443
468
|
* `getenv(name)`: the value of environment variable `name`, copied into the
|
|
@@ -485,6 +510,10 @@ const nish_str *nish_arch(void);
|
|
|
485
510
|
* purpose: `readnone` would let LLVM fold the two reads around a measured
|
|
486
511
|
* region into one and measure zero. */
|
|
487
512
|
int64_t nish_monotonic_nanos(void);
|
|
513
|
+
/* `wallClockNanos()`: `CLOCK_REALTIME` in nanoseconds since the epoch, the
|
|
514
|
+
* clock `Date.now` reads at a millisecond's grain. It steps when the system
|
|
515
|
+
* time is set, so it dates an event and measures nothing. */
|
|
516
|
+
int64_t nish_wall_clock_nanos(void);
|
|
488
517
|
|
|
489
518
|
/* ---- The host (WP34 N3), runtime/runtime-host.c --------------------------
|
|
490
519
|
* `Date.now()`: the wall clock, `CLOCK_REALTIME`, in whole milliseconds since
|
|
@@ -542,6 +571,10 @@ bool nish_is_executable(const nish_str *path);
|
|
|
542
571
|
* len)` of `buf`, which the compiled call has range-checked; a write to a
|
|
543
572
|
* gone peer is -32, never SIGPIPE. `nish_net_shutdown(fd, how)`: 0 read, 1
|
|
544
573
|
* write, 2 both. `nish_net_close(fd)`.
|
|
574
|
+
* `nish_net_set_option(fd, option, value)`: one socket option from a
|
|
575
|
+
* whitelist, by the runtime's own id (1 `TCP_NODELAY`, 2 `TCP_QUICKACK`, 3
|
|
576
|
+
* `SO_KEEPALIVE`, 4 `SO_SNDBUF`, 5 `SO_RCVBUF`); any other id is -22, and
|
|
577
|
+
* `TCP_QUICKACK` off Linux is -95.
|
|
545
578
|
* `nish_tcp_connect(addr)`: a new socket connecting to `addr`, answered at
|
|
546
579
|
* once; its `EINPROGRESS` is not a failure, and any other closes it.
|
|
547
580
|
* `nish_connect_result(fd)`: `SO_ERROR` once the socket is writable, 0 when
|
|
@@ -576,6 +609,7 @@ int32_t nish_net_read(int32_t fd, nish_array *buf, int64_t off, int64_t len);
|
|
|
576
609
|
int32_t nish_net_write(int32_t fd, const nish_array *buf, int64_t off, int64_t len);
|
|
577
610
|
int32_t nish_net_shutdown(int32_t fd, int32_t how);
|
|
578
611
|
int32_t nish_net_close(int32_t fd);
|
|
612
|
+
int32_t nish_net_set_option(int32_t fd, int32_t option, int32_t value);
|
|
579
613
|
int32_t nish_tcp_connect(const nish_array *addr);
|
|
580
614
|
int32_t nish_connect_result(int32_t fd);
|
|
581
615
|
int32_t nish_udp_bind(const nish_str *host, int32_t port, int32_t flags);
|