@threadedstack/wmd-web 0.0.0-stage → 0.1.13
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/README.md +2601 -2
- package/RELEASE.json +123 -0
- package/export-page/coi-sw.js +20 -0
- package/export-page/index.html +17 -0
- package/export-page/run.css +10 -0
- package/export-page/run.js +236 -0
- package/guest/arm64/Image +4 -0
- package/guest/arm64/Image-4k +4 -0
- package/guest/arm64/initramfs.cpio.gz +0 -0
- package/guest/arm64/vm.wasm +0 -0
- package/package.json +36 -4
- package/page/sw.js +381 -0
- package/relay/control.ts +139 -0
- package/relay/engine.ts +787 -0
- package/relay/hosted.ts +660 -0
- package/relay/keys.ts +53 -0
- package/relay/local-tls.ts +121 -0
- package/relay/lock.ts +81 -0
- package/relay/logfile.ts +138 -0
- package/relay/main.ts +642 -0
- package/relay/policy.ts +241 -0
- package/relay/relay.ts +266 -0
- package/relay/token.ts +116 -0
- package/relay/v1.ts +107 -0
- package/relay/wisp.ts +615 -0
- package/src/base.js +325 -0
- package/src/boot.js +2037 -0
- package/src/calib-worker.js +40 -0
- package/src/fs/9p/codec.js +303 -0
- package/src/fs/9p/server.js +521 -0
- package/src/fs/errno.js +43 -0
- package/src/fs/graph.js +380 -0
- package/src/fs/overlay.js +1018 -0
- package/src/fs/pack-write.js +165 -0
- package/src/fs/pack.js +427 -0
- package/src/fs/seed.js +22 -0
- package/src/fs/svc-snapshot.js +65 -0
- package/src/fs/upper-pack.js +112 -0
- package/src/host/base-cache.js +195 -0
- package/src/host/base-file.js +328 -0
- package/src/host/base-io.js +93 -0
- package/src/host/boot-events.js +209 -0
- package/src/host/chan-service.js +300 -0
- package/src/host/chan-services.js +689 -0
- package/src/host/chan.js +655 -0
- package/src/host/ckpt-file.js +395 -0
- package/src/host/ckpt-io.js +207 -0
- package/src/host/ckpt-point.js +160 -0
- package/src/host/ckpt-save.js +64 -0
- package/src/host/ckpt.js +434 -0
- package/src/host/clock.js +111 -0
- package/src/host/ctl.js +778 -0
- package/src/host/errno.js +18 -0
- package/src/host/fs.js +167 -0
- package/src/host/gcnudge.js +83 -0
- package/src/host/i64shim.js +81 -0
- package/src/host/jit-cache.js +341 -0
- package/src/host/jit-hub-core.js +208 -0
- package/src/host/jit-hub.js +93 -0
- package/src/host/jit-snap.js +226 -0
- package/src/host/jit.js +1903 -0
- package/src/host/layout.js +100 -0
- package/src/host/monitor.js +110 -0
- package/src/host/p9mount.js +27 -0
- package/src/host/ram-mem.js +125 -0
- package/src/host/stdio.js +143 -0
- package/src/host/threads.js +109 -0
- package/src/host/wasi.js +335 -0
- package/src/host/wmd-imports.js +71 -0
- package/src/host/yamux.js +387 -0
- package/src/image-files.js +154 -0
- package/src/net/dhcp-dns.js +126 -0
- package/src/net/net-worker.js +172 -0
- package/src/net/packet.js +227 -0
- package/src/net/peer.js +230 -0
- package/src/net/proto.js +189 -0
- package/src/net/relay-client.js +226 -0
- package/src/net/stack.js +867 -0
- package/src/net/switch.js +160 -0
- package/src/net/wisp-client.js +570 -0
- package/src/net/wisp-transport.js +196 -0
- package/src/net/wisp.js +511 -0
- package/src/registry/client.js +176 -0
- package/src/registry/converter.js +68 -0
- package/src/registry/hub.js +221 -0
- package/src/registry/oci.js +76 -0
- package/src/registry/pack-worker.js +70 -0
- package/src/registry/pull.js +258 -0
- package/src/registry/ref.js +110 -0
- package/src/registry/wasi-mem.js +355 -0
- package/src/worker.js +315 -0
- package/wmdpack.wasm +0 -0
package/src/host/wasi.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// The WASI preview1 subset vm.wasm imports (web/docs/host-interface.md §1.2, §2.5),
|
|
2
|
+
// modelled on the reference host runner/src/wasi.rs.
|
|
3
|
+
//
|
|
4
|
+
// One instance of these functions exists per Worker; everything that must be
|
|
5
|
+
// process-wide (fd table, clock base, exit flag) lives in shared buffers.
|
|
6
|
+
|
|
7
|
+
import { E } from './errno.js'
|
|
8
|
+
import { CTL_EXIT, CTL_EXIT_CODE } from './layout.js'
|
|
9
|
+
|
|
10
|
+
const FILETYPE_OFF = 16 // filestat.filetype
|
|
11
|
+
const CHUNK = 1 << 20
|
|
12
|
+
|
|
13
|
+
/** Thrown out of proc_exit to unwind the calling Worker's wasm stack. */
|
|
14
|
+
export class WasiExit extends Error {
|
|
15
|
+
constructor(code) {
|
|
16
|
+
super(`proc_exit(${code})`)
|
|
17
|
+
this.code = code
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const enc = new TextEncoder()
|
|
22
|
+
|
|
23
|
+
/** NUL-terminated strings packed back to back, plus their offsets (args/environ layout). */
|
|
24
|
+
export function encodeStrings(list) {
|
|
25
|
+
const parts = list.map(s => enc.encode(s))
|
|
26
|
+
const size = parts.reduce((n, p) => n + p.length + 1, 0)
|
|
27
|
+
const buf = new Uint8Array(size)
|
|
28
|
+
const offsets = []
|
|
29
|
+
let o = 0
|
|
30
|
+
for (const p of parts) {
|
|
31
|
+
offsets.push(o)
|
|
32
|
+
buf.set(p, o)
|
|
33
|
+
o += p.length + 1 // trailing 0 already there
|
|
34
|
+
}
|
|
35
|
+
return { buf, offsets, size }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {object} o
|
|
40
|
+
* @param {WebAssembly.Memory} o.memory
|
|
41
|
+
* @param {string[]} o.args argv, argv[0] included
|
|
42
|
+
* @param {string[]} o.env "K=V" strings
|
|
43
|
+
* @param {ReturnType<import('./fs.js').createFs>} o.fs
|
|
44
|
+
* @param {ReturnType<import('./clock.js').createClock>} o.clock
|
|
45
|
+
* @param {Int32Array} o.ctl control cells
|
|
46
|
+
* @param {(fd:number, bytes:Uint8Array)=>void} o.write stdout/stderr sink
|
|
47
|
+
* @param {(code:number)=>void} [o.onExit] called once before WasiExit is thrown
|
|
48
|
+
* @param {(ms:number)=>void} [o.sleep] blocking sleep (Atomics.wait in Workers)
|
|
49
|
+
*/
|
|
50
|
+
export function createWasi({ memory, args, env, fs, clock, ctl, write, onExit, sleep }) {
|
|
51
|
+
const argv = encodeStrings(args)
|
|
52
|
+
const envv = encodeStrings(env)
|
|
53
|
+
const sleepCell = new Int32Array(new SharedArrayBuffer(4))
|
|
54
|
+
const doSleep = sleep || (ms => Atomics.wait(sleepCell, 0, 0, ms))
|
|
55
|
+
|
|
56
|
+
let buf = null
|
|
57
|
+
let u8, dv
|
|
58
|
+
const views = () => {
|
|
59
|
+
const b = memory.buffer
|
|
60
|
+
if (b !== buf || b.byteLength !== u8.length) {
|
|
61
|
+
buf = b
|
|
62
|
+
u8 = new Uint8Array(b)
|
|
63
|
+
dv = new DataView(b)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Initialise eagerly so views() can compare lengths.
|
|
67
|
+
buf = memory.buffer
|
|
68
|
+
u8 = new Uint8Array(buf)
|
|
69
|
+
dv = new DataView(buf)
|
|
70
|
+
|
|
71
|
+
const inRange = (ptr, len) => ptr >= 0 && len >= 0 && ptr + len <= u8.length
|
|
72
|
+
const u32 = x => x >>> 0
|
|
73
|
+
|
|
74
|
+
function putStrings(vec, ptrs, bufPtr) {
|
|
75
|
+
views()
|
|
76
|
+
ptrs = u32(ptrs)
|
|
77
|
+
bufPtr = u32(bufPtr)
|
|
78
|
+
if (!inRange(bufPtr, vec.size) || !inRange(ptrs, 4 * vec.offsets.length)) return E.FAULT
|
|
79
|
+
u8.set(vec.buf, bufPtr)
|
|
80
|
+
vec.offsets.forEach((o, i) => dv.setUint32(ptrs + 4 * i, bufPtr + o, true))
|
|
81
|
+
return E.SUCCESS
|
|
82
|
+
}
|
|
83
|
+
function putSizes(vec, countPtr, sizePtr) {
|
|
84
|
+
views()
|
|
85
|
+
countPtr = u32(countPtr)
|
|
86
|
+
sizePtr = u32(sizePtr)
|
|
87
|
+
if (!inRange(countPtr, 4) || !inRange(sizePtr, 4)) return E.FAULT
|
|
88
|
+
dv.setUint32(countPtr, vec.offsets.length, true)
|
|
89
|
+
dv.setUint32(sizePtr, vec.size, true)
|
|
90
|
+
return E.SUCCESS
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Walk an iovec array: calls fn(ptr, len) for each entry; stops early when fn
|
|
94
|
+
// returns false. Returns an errno.
|
|
95
|
+
function iovecs(iovs, n, fn) {
|
|
96
|
+
iovs = u32(iovs)
|
|
97
|
+
if (!inRange(iovs, 8 * n)) return E.FAULT
|
|
98
|
+
for (let i = 0; i < n; i++) {
|
|
99
|
+
const ptr = dv.getUint32(iovs + 8 * i, true)
|
|
100
|
+
const len = dv.getUint32(iovs + 8 * i + 4, true)
|
|
101
|
+
if (!inRange(ptr, len)) return E.FAULT
|
|
102
|
+
if (fn(ptr, len) === false) break
|
|
103
|
+
}
|
|
104
|
+
return E.SUCCESS
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const wasi = {
|
|
108
|
+
args_sizes_get: (c, s) => putSizes(argv, c, s),
|
|
109
|
+
args_get: (p, b) => putStrings(argv, p, b),
|
|
110
|
+
environ_sizes_get: (c, s) => putSizes(envv, c, s),
|
|
111
|
+
environ_get: (p, b) => putStrings(envv, p, b),
|
|
112
|
+
|
|
113
|
+
// W40: no allocation here (clock.js): vm.wasm calls it thousands of times a second.
|
|
114
|
+
clock_time_get(id, _precision, out) {
|
|
115
|
+
// 0 realtime; 1-3 monotonic, process and thread CPU time: ns since start (as the runner)
|
|
116
|
+
if (id < 0 || id > 3) return E.INVAL
|
|
117
|
+
views()
|
|
118
|
+
out = u32(out)
|
|
119
|
+
if (!inRange(out, 8)) return E.FAULT
|
|
120
|
+
clock.writeNs(dv, out, id)
|
|
121
|
+
return E.SUCCESS
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
fd_close: fd => fs.close(fd),
|
|
125
|
+
|
|
126
|
+
fd_fdstat_get(fd, out) {
|
|
127
|
+
const type = fs.fdType(fd)
|
|
128
|
+
if (type < 0) return E.BADF
|
|
129
|
+
views()
|
|
130
|
+
out = u32(out)
|
|
131
|
+
if (!inRange(out, 24)) return E.FAULT
|
|
132
|
+
u8.fill(0, out, out + 24)
|
|
133
|
+
u8[out] = type
|
|
134
|
+
dv.setBigUint64(out + 8, 0xffffffffffffffffn, true)
|
|
135
|
+
dv.setBigUint64(out + 16, 0xffffffffffffffffn, true)
|
|
136
|
+
return E.SUCCESS
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
fd_fdstat_set_flags: () => E.SUCCESS,
|
|
140
|
+
|
|
141
|
+
fd_filestat_get(fd, out) {
|
|
142
|
+
const st = fs.stat(fd)
|
|
143
|
+
if (!st) return E.BADF
|
|
144
|
+
views()
|
|
145
|
+
out = u32(out)
|
|
146
|
+
if (!inRange(out, 64)) return E.FAULT
|
|
147
|
+
u8.fill(0, out, out + 64)
|
|
148
|
+
u8[out + FILETYPE_OFF] = st.type
|
|
149
|
+
dv.setBigUint64(out + 24, 1n, true) // nlink
|
|
150
|
+
dv.setBigUint64(out + 32, BigInt(st.size), true)
|
|
151
|
+
return E.SUCCESS
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
fd_prestat_get(fd, out) {
|
|
155
|
+
const name = fs.prestatName(fd)
|
|
156
|
+
if (name === null) return E.BADF
|
|
157
|
+
views()
|
|
158
|
+
out = u32(out)
|
|
159
|
+
if (!inRange(out, 8)) return E.FAULT
|
|
160
|
+
dv.setUint32(out, 0, true) // pr_type 0 = directory
|
|
161
|
+
dv.setUint32(out + 4, enc.encode(name).length, true)
|
|
162
|
+
return E.SUCCESS
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
fd_prestat_dir_name(fd, path, len) {
|
|
166
|
+
const name = fs.prestatName(fd)
|
|
167
|
+
if (name === null) return E.BADF
|
|
168
|
+
const b = enc.encode(name)
|
|
169
|
+
if (u32(len) < b.length) return E.INVAL
|
|
170
|
+
views()
|
|
171
|
+
path = u32(path)
|
|
172
|
+
if (!inRange(path, b.length)) return E.FAULT
|
|
173
|
+
u8.set(b, path)
|
|
174
|
+
return E.SUCCESS
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
fd_read(fd, iovs, n, nread) {
|
|
178
|
+
views()
|
|
179
|
+
let total = 0
|
|
180
|
+
let err = E.SUCCESS
|
|
181
|
+
const e = iovecs(iovs, n, (ptr, len) => {
|
|
182
|
+
const got = fs.read(fd, u8.subarray(ptr, ptr + len))
|
|
183
|
+
if (got < 0) {
|
|
184
|
+
err = -got
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
total += got
|
|
188
|
+
return got === len // short read: stop
|
|
189
|
+
})
|
|
190
|
+
if (e) return e
|
|
191
|
+
if (err) return err
|
|
192
|
+
nread = u32(nread)
|
|
193
|
+
if (!inRange(nread, 4)) return E.FAULT
|
|
194
|
+
dv.setUint32(nread, total, true)
|
|
195
|
+
return E.SUCCESS
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
fd_seek(fd, offset, whence, out) {
|
|
199
|
+
const r = fs.seek(fd, offset, whence)
|
|
200
|
+
if (r.errno) return r.errno
|
|
201
|
+
views()
|
|
202
|
+
out = u32(out)
|
|
203
|
+
if (!inRange(out, 8)) return E.FAULT
|
|
204
|
+
dv.setBigUint64(out, BigInt(r.pos), true)
|
|
205
|
+
return E.SUCCESS
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
fd_write(fd, iovs, n, nwritten) {
|
|
209
|
+
if (fd !== 1 && fd !== 2) return fs.fdType(fd) < 0 ? E.BADF : E.NOTCAPABLE
|
|
210
|
+
views()
|
|
211
|
+
let total = 0
|
|
212
|
+
const chunks = []
|
|
213
|
+
const e = iovecs(iovs, n, (ptr, len) => {
|
|
214
|
+
for (let o = 0; o < len; o += CHUNK) {
|
|
215
|
+
// slice(): copy out of shared memory before handing the bytes away.
|
|
216
|
+
chunks.push(u8.slice(ptr + o, ptr + Math.min(len, o + CHUNK)))
|
|
217
|
+
}
|
|
218
|
+
total += len
|
|
219
|
+
})
|
|
220
|
+
if (e) return e
|
|
221
|
+
if (chunks.length === 1) write(fd, chunks[0])
|
|
222
|
+
else if (chunks.length > 1) {
|
|
223
|
+
const all = new Uint8Array(total)
|
|
224
|
+
let o = 0
|
|
225
|
+
for (const c of chunks) {
|
|
226
|
+
all.set(c, o)
|
|
227
|
+
o += c.length
|
|
228
|
+
}
|
|
229
|
+
write(fd, all)
|
|
230
|
+
}
|
|
231
|
+
nwritten = u32(nwritten)
|
|
232
|
+
if (!inRange(nwritten, 4)) return E.FAULT
|
|
233
|
+
dv.setUint32(nwritten, total, true)
|
|
234
|
+
return E.SUCCESS
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
path_open(dirfd, _dirflags, path, pathLen, oflags, _rb, _ri, _fdflags, outFd) {
|
|
238
|
+
views()
|
|
239
|
+
path = u32(path)
|
|
240
|
+
pathLen = u32(pathLen)
|
|
241
|
+
if (pathLen > 4096) return E.INVAL
|
|
242
|
+
if (!inRange(path, pathLen)) return E.FAULT
|
|
243
|
+
let p
|
|
244
|
+
try {
|
|
245
|
+
p = new TextDecoder('utf-8', { fatal: true }).decode(u8.slice(path, path + pathLen))
|
|
246
|
+
} catch {
|
|
247
|
+
return E.INVAL
|
|
248
|
+
}
|
|
249
|
+
const r = fs.open(dirfd, p, oflags)
|
|
250
|
+
if (r.errno) return r.errno
|
|
251
|
+
outFd = u32(outFd)
|
|
252
|
+
if (!inRange(outFd, 4)) return E.FAULT
|
|
253
|
+
dv.setUint32(outFd, r.fd, true)
|
|
254
|
+
return E.SUCCESS
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// Clock subscriptions only, like the runner: sleep for the smallest timeout and
|
|
258
|
+
// report that one subscription.
|
|
259
|
+
poll_oneoff(subs, events, nsubs, nevents) {
|
|
260
|
+
views()
|
|
261
|
+
subs = u32(subs)
|
|
262
|
+
if (!inRange(subs, 48 * nsubs)) return E.FAULT
|
|
263
|
+
// W40: Numbers, not BigInts (no garbage per call: D-417, D-419); a timeout
|
|
264
|
+
// only needs to be right to the nanoseconds a sleep can tell apart.
|
|
265
|
+
let best = -1
|
|
266
|
+
let bestTimeout = 0
|
|
267
|
+
for (let i = 0; i < nsubs; i++) {
|
|
268
|
+
const s = subs + 48 * i
|
|
269
|
+
if (u8[s + 8] !== 0) continue // not a clock subscription
|
|
270
|
+
const id = dv.getUint32(s + 16, true)
|
|
271
|
+
let timeout = dv.getUint32(s + 28, true) * 4294967296 + dv.getUint32(s + 24, true)
|
|
272
|
+
const flags = dv.getUint16(s + 40, true)
|
|
273
|
+
if (flags & 1) {
|
|
274
|
+
const now = clock.nowNum(id === 0 ? 0 : 1)
|
|
275
|
+
timeout = timeout > now ? timeout - now : 0
|
|
276
|
+
}
|
|
277
|
+
if (best < 0 || timeout < bestTimeout) {
|
|
278
|
+
best = s
|
|
279
|
+
bestTimeout = timeout
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
nevents = u32(nevents)
|
|
283
|
+
if (!inRange(nevents, 4)) return E.FAULT
|
|
284
|
+
if (best < 0) {
|
|
285
|
+
dv.setUint32(nevents, 0, true)
|
|
286
|
+
return E.NOTSUP
|
|
287
|
+
}
|
|
288
|
+
// the userdata, before the sleep: the subscription memory is the guest's
|
|
289
|
+
const udLo = dv.getUint32(best, true)
|
|
290
|
+
const udHi = dv.getUint32(best + 4, true)
|
|
291
|
+
const ms = bestTimeout / 1e6
|
|
292
|
+
if (ms > 0) doSleep(ms)
|
|
293
|
+
views()
|
|
294
|
+
events = u32(events)
|
|
295
|
+
if (!inRange(events, 32)) return E.FAULT
|
|
296
|
+
u8.fill(0, events, events + 32)
|
|
297
|
+
dv.setUint32(events, udLo, true) // error 0, type 0 (clock)
|
|
298
|
+
dv.setUint32(events + 4, udHi, true)
|
|
299
|
+
dv.setUint32(nevents, 1, true)
|
|
300
|
+
return E.SUCCESS
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
proc_exit(code) {
|
|
304
|
+
code = code & 0xff
|
|
305
|
+
// First caller wins: later exits (other threads racing) keep the first code.
|
|
306
|
+
if (Atomics.compareExchange(ctl, CTL_EXIT, 0, 1) === 0) {
|
|
307
|
+
Atomics.store(ctl, CTL_EXIT_CODE, code)
|
|
308
|
+
Atomics.notify(ctl, CTL_EXIT)
|
|
309
|
+
onExit?.(code)
|
|
310
|
+
}
|
|
311
|
+
throw new WasiExit(code)
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
sched_yield: () => E.SUCCESS,
|
|
315
|
+
|
|
316
|
+
// vm.wasm seeds the guest kernel's pool from this (W18: the DTB's
|
|
317
|
+
// /chosen/rng-seed). getRandomValues refuses views of shared memory and
|
|
318
|
+
// takes at most 64 KiB per call, so fill a private buffer and copy.
|
|
319
|
+
random_get(ptr, len) {
|
|
320
|
+
views()
|
|
321
|
+
ptr = u32(ptr)
|
|
322
|
+
len = u32(len)
|
|
323
|
+
if (!inRange(ptr, len)) return E.FAULT
|
|
324
|
+
const tmp = new Uint8Array(Math.min(len, 65536))
|
|
325
|
+
for (let off = 0; off < len; off += tmp.length) {
|
|
326
|
+
const n = Math.min(tmp.length, len - off)
|
|
327
|
+
const part = n === tmp.length ? tmp : tmp.subarray(0, n)
|
|
328
|
+
crypto.getRandomValues(part)
|
|
329
|
+
u8.set(part, ptr + off)
|
|
330
|
+
}
|
|
331
|
+
return E.SUCCESS
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
return wasi
|
|
335
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The 26 `wmd.*` imports of vm.wasm (web/docs/host-interface.md §1.2, §4.1, §8).
|
|
2
|
+
//
|
|
3
|
+
// P-B1 runs the interpreter with no channels, so only ram_map is ever called. The
|
|
4
|
+
// rest are stubs with the values the guest treats as "not available":
|
|
5
|
+
// ram_map → 1 (RAM left as plain wasm memory; return value ignored, main_wasm.c:562)
|
|
6
|
+
// blk_rw → -38 (no swap disk: the browser never passes --swap, so vm.wasm makes no device, D-497)
|
|
7
|
+
// win_init → 1 (address windows off, win.c:226-253); other win_* → 0 (never called)
|
|
8
|
+
// snapshot → -1 ("continuing without it")
|
|
9
|
+
// ctl_attach → -1 (no runner control socket: the guest starts no ctl thread, W3.3)
|
|
10
|
+
// point → -38 (ENOSYS: no mid-container checkpoint; the guest goes on, W3.3); as the
|
|
11
|
+
// weak defaults in guest/vm/src/dev/chan.c
|
|
12
|
+
// jit_load/jit_compile/jit_known → -1, jit_sync/flush/note/yield → no-op when no door is
|
|
13
|
+
// given; the worker passes the P-B2 JIT door (host/jit.js) as `jit`
|
|
14
|
+
// chan_open → -111 (ECONNREFUSED, "nothing listens"; channels are P-B3)
|
|
15
|
+
// chan_send/recv/wake/shutdown/close → -9 (bad handle: no handle was ever issued)
|
|
16
|
+
//
|
|
17
|
+
// Stub calls are counted in the control block (`counters`, layout.js noteHostCall), not
|
|
18
|
+
// reported one by one: --mode jit calls jit_compile thousands of times a second.
|
|
19
|
+
//
|
|
20
|
+
// `overrides` lets later proofs plug in the JIT door and channels without touching
|
|
21
|
+
// the worker wiring: createWmdImports({ jit: {...}, chan: {...} }).
|
|
22
|
+
|
|
23
|
+
import { noteHostCall } from './layout.js'
|
|
24
|
+
|
|
25
|
+
const EBADF = -9
|
|
26
|
+
const ECONNREFUSED = -111
|
|
27
|
+
const ENOSYS = -38
|
|
28
|
+
|
|
29
|
+
export function createWmdImports({ jit = {}, chan = {}, counters, ckpt = null, ram = null } = {}) {
|
|
30
|
+
const note = counters ? name => noteHostCall(counters, name) : () => {}
|
|
31
|
+
return {
|
|
32
|
+
// channels (P-B3)
|
|
33
|
+
chan_open: chan.open || ((_kind, _port) => (note('chan_open'), ECONNREFUSED)),
|
|
34
|
+
chan_send: chan.send || ((_h, _p, _l) => EBADF),
|
|
35
|
+
chan_recv: chan.recv || ((_h, _p, _l, _t) => EBADF),
|
|
36
|
+
chan_wake: chan.wake || (_h => EBADF),
|
|
37
|
+
chan_shutdown: chan.shutdown || (_h => EBADF),
|
|
38
|
+
chan_close: chan.close || (_h => EBADF),
|
|
39
|
+
|
|
40
|
+
// JIT door (P-B2)
|
|
41
|
+
jit_load: jit.load || ((_p, _l) => (note('jit_load'), -1)),
|
|
42
|
+
jit_compile: jit.compile || ((_p, _l) => (note('jit_compile'), -1)),
|
|
43
|
+
jit_sync: jit.sync || (() => note('jit_sync')),
|
|
44
|
+
jit_known: jit.known || (_p => (note('jit_known'), -1)),
|
|
45
|
+
jit_note: jit.note || ((_p, _n) => note('jit_note')),
|
|
46
|
+
jit_flush: jit.flush || (() => note('jit_flush')),
|
|
47
|
+
jit_evict: jit.evict || ((_m, _k, _p, _c) => -1), // D-514: no door, no budget: the guest releases instead
|
|
48
|
+
jit_yield: jit.yield || (() => {}), // W32: worker.js gives a WebKit JIT thread a yield to its event loop
|
|
49
|
+
|
|
50
|
+
snapshot: (_pp, _pl, _hp, _hl) => (note('snapshot'), -1),
|
|
51
|
+
ram_map: (_off, _len) => 1,
|
|
52
|
+
// RAM outside memory 0 (host/ram-mem.js, the flat layout): without `ram` the guest's RAM is in
|
|
53
|
+
// memory 0 and vm.wasm calls none of these (ram_map2 -1: "this host cannot hold RAM outside memory 0")
|
|
54
|
+
ram_map2: ram?.ram_map2 || ((_off, _lowLen, _total) => -1),
|
|
55
|
+
ram_rw: ram?.ram_rw || ((_off, _ptr, _len, _write) => -1),
|
|
56
|
+
ram_cas: ram?.ram_cas || ((_off, _size, _expect, _desired) => 0n),
|
|
57
|
+
blk_rw: (_op, _off, _ptr, _len) => ENOSYS,
|
|
58
|
+
|
|
59
|
+
// W3.3 checkpoints: no control socket and no checkpoint in the browser
|
|
60
|
+
// P-B9: with `ckpt` (host/ckpt-point.js) the guest starts its ctl thread and takes checkpoints
|
|
61
|
+
ctl_attach: ckpt?.ctl_attach || (_wordAddr => -1),
|
|
62
|
+
point: ckpt?.point || ((_kind, _hp, _hl) => ENOSYS),
|
|
63
|
+
|
|
64
|
+
// address windows: off in the browser
|
|
65
|
+
win_init: (_flag, _gen) => 1,
|
|
66
|
+
win_map: () => 0,
|
|
67
|
+
win_reset: () => 0,
|
|
68
|
+
win_unmap: () => 0,
|
|
69
|
+
win_protect: () => 0,
|
|
70
|
+
}
|
|
71
|
+
}
|