@amritk/nish-x86_64-linux 0.10.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.
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Run an Nish program under Node, unmodified.
3
+ *
4
+ * ```
5
+ * node --experimental-strip-types --import ./runtime/nish.mjs prog.ts
6
+ * ```
7
+ *
8
+ * This is a *different* claim from the one WP13 tests, and a much smaller one.
9
+ * The differential harness rewrites a program from the checker's own type table
10
+ * before Node sees it (docs/wp13-differential.md), which is what lets it
11
+ * reproduce wrapping arithmetic, byte-length strings and bounds checks exactly.
12
+ * Nothing is rewritten here: the source runs as the TypeScript it is, with the
13
+ * type annotations stripped. All this module can do is supply the globals
14
+ * Nish has and Node does not, and fix `console.log`'s formatting.
15
+ *
16
+ * That is enough for a real and useful subset — **f64 mode**, where JavaScript's
17
+ * `+ - * / %` on doubles *are* `fadd/fsub/fmul/fdiv/frem` and `Math.*` are the
18
+ * same IEEE operations, so the arithmetic needs no help. It is not enough for
19
+ * i32 mode, and it never will be: `number` is a wrapping 32-bit integer there
20
+ * and every operator would have to change.
21
+ *
22
+ * `docs/RUN_UNDER_NODE.md` states the overlap and the whole list of what stays
23
+ * divergent. The short version, because it belongs next to the code too:
24
+ *
25
+ * - **i32 mode is out.** Wrapping, truncating `/`, and the divide-by-zero
26
+ * panic are all operator-level and cannot be reached from here.
27
+ * - **`s.length` is UTF-16 units under Node** and UTF-8 bytes natively, and
28
+ * so is every string offset. ASCII agrees; nothing else does.
29
+ * - **`a[i]` is unchecked**: out of range is `undefined` here and an exit-1
30
+ * panic natively. Only a program that indexes out of range can tell.
31
+ * - **Method dispatch is virtual under Node** and static natively, so an
32
+ * override reached through a base-typed value differs (LANGUAGE.md,
33
+ * "Method dispatch is static").
34
+ * - **`orReturn()` does not propagate.** It throws a marker the rewriter's
35
+ * `try`/`catch` turns into an early `return`; unmodified there is no
36
+ * `catch`, so it escapes. `Ok`/`Err`/`isOk`/`isErr`/`value`/`error`/
37
+ * `unwrapOr`/`expect` all work.
38
+ * - **`Number(s)` keeps JavaScript's parsing.** `Number` is a constructor
39
+ * carrying statics (`Number.isNaN` among them) that this module and Node
40
+ * both need, so it is left alone; `parseInt` and `parseFloat` are plain
41
+ * functions and do get Nish's semantics.
42
+ * - **`Arena.*` reports zero.** There is no arena, and a program that prints
43
+ * `Arena.used()` is measuring the native allocator by definition.
44
+ * - **`i64` and `u64` are out**, for the same reason i32 mode is. The shim
45
+ * represents them as BigInt, because that is the only JavaScript type that
46
+ * holds 64 bits and wraps where the native ones wrap. Unrewritten source
47
+ * says `n + 1`, and JavaScript refuses to mix a BigInt with a number, so
48
+ * `toI64`/`toU64`/`f64ToBits` throw at the first arithmetic instead of
49
+ * answering something that is quietly wrong. That is the intended failure:
50
+ * a `TypeError` naming the line beats a number that silently stopped
51
+ * wrapping at 2^53.
52
+ *
53
+ * Everything here delegates to `runtime/shim.mjs`, the module the differential
54
+ * harness already uses, so the two cannot drift apart: a semantic fixed there
55
+ * is fixed here in the same commit.
56
+ */
57
+ import * as shim from "./shim.mjs";
58
+
59
+ /** Install `value` as a global unless the program declared its own. */
60
+ const provide = (name, value) => {
61
+ if (!(name in globalThis)) {
62
+ globalThis[name] = value;
63
+ }
64
+ };
65
+
66
+ // `console.log(x)` is `String(x)` plus a newline on a synchronous write, which
67
+ // is not what Node's console does: it inspects, so `-0` prints `-0` where
68
+ // `String(-0)` is `0`, and a BigInt prints with an `n`. Overwritten rather than
69
+ // `provide`d, because Node always has one.
70
+ globalThis.console = {
71
+ ...globalThis.console,
72
+ log: shim.log,
73
+ error: shim.error,
74
+ };
75
+
76
+ // Streams and files. These are globals in Nish rather than imports from
77
+ // `node:fs`, which is why they have to be installed at all. `readdirSync` is
78
+ // the one whose answer is not simply Node's: the language sorts the listing by
79
+ // UTF-8 bytes, so the shim sorts on the encoded bytes rather than leaving
80
+ // `Array#sort`'s UTF-16 order — the two agree on ASCII names only.
81
+ provide("write", shim.write);
82
+ provide("writeError", shim.writeError);
83
+ provide("panic", shim.panic);
84
+ provide("readFileSync", shim.readFileSync);
85
+ provide("readFileSyncOrNull", shim.readFileSyncOrNull);
86
+ provide("writeFileSync", shim.writeFileSync);
87
+ provide("appendFileSync", shim.appendFileSync);
88
+ provide("mkdirSync", shim.mkdirSync);
89
+ provide("isDirectorySync", shim.isDirectorySync);
90
+ provide("readdirSync", shim.readdirSync);
91
+ provide("realpathSync", shim.realpathSync);
92
+ provide("spawnSync", shim.spawnSync);
93
+ provide("spawnSyncTo", shim.spawnSyncTo);
94
+ provide("getenv", shim.getenv);
95
+
96
+ // The clock. `monotonicNanos()` answers an `i64`, so the value is a BigInt here
97
+ // as it is in the rewritten runner — and unusually for the i64 surface that is
98
+ // enough on its own: `t1 - t0` on two BigInts is the `sub i64` the native build
99
+ // performs, and an elapsed time is nowhere near the width where BigInt's
100
+ // arbitrary precision and the native wrap would part. It is only mixing a
101
+ // reading with a plain `number` that throws, which is the failure the header
102
+ // describes. A printed reading can never match a native run: the two origins are
103
+ // both arbitrary, and only the difference between two reads means anything.
104
+ provide("monotonicNanos", shim.monotonicNanos);
105
+
106
+ // Conversions. In f64 mode `toF64` is the identity and `toI32` is the one that
107
+ // matters (it saturates, where a JavaScript cast would not).
108
+ provide("toI32", shim.toI32);
109
+ provide("toI64", shim.toI64);
110
+ provide("toF64", shim.toF64);
111
+ provide("toF32", (x) => Math.fround(x));
112
+ provide("toU8", (x) => shim.convert(x, "f64", "u8"));
113
+ provide("toU16", (x) => shim.convert(x, "f64", "u16"));
114
+ provide("toU32", (x) => shim.convert(x, "f64", "u32"));
115
+ provide("toU64", (x) => shim.convert(x, "f64", "u64"));
116
+ provide("f64ToBits", shim.f64ToBits);
117
+ provide("bitsToF64", shim.bitsToF64);
118
+
119
+ // `Result`. Everything works but `orReturn`, which needs the caller's control
120
+ // flow and therefore the rewriter; see the header.
121
+ provide("Ok", shim.Ok);
122
+ provide("Err", shim.Err);
123
+
124
+ // The string parsers, whose deviations from JavaScript are documented rules
125
+ // (base-10 `parseInt` that saturates, `parseFloat` that reads a `0x` prefix).
126
+ globalThis.parseInt = shim.parseInt;
127
+ globalThis.parseFloat = shim.parseFloat;
128
+
129
+ // `process.argv[0]` is the program on both sides: the executable natively, this
130
+ // script under Node, which is one index later than Node's own `argv`.
131
+ process.argv = shim.argv();
132
+
133
+ // There is no arena. `mark`/`release`/`reset` are no-ops rather than errors so
134
+ // that a program which manages memory explicitly still runs; `used()` answers
135
+ // zero, which is the honest number for a host that is not bump-allocating.
136
+ // Delegated like everything else here rather than written inline, so that the
137
+ // answers cannot drift from the ones the rewritten runner gives.
138
+ provide("Arena", {
139
+ mark: shim.arenaMark,
140
+ release: shim.arenaRelease,
141
+ reset: shim.arenaReset,
142
+ used: shim.arenaUsed,
143
+ });