small-python 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +52 -2
- package/index.d.ts +81 -0
- package/index.js +611 -0
- package/licenses/python.txt +277 -0
- package/licenses/quickjs.txt +20 -0
- package/licenses/unicode.txt +39 -0
- package/package.json +40 -4
- package/spy-worker.wasm +0 -0
- package/wire.js +230 -0
- package/worker.js +88 -0
package/worker.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// The Worker side of a Runtime: instantiates spy-worker.wasm and runs its
|
|
2
|
+
// main loop, which blocks in read() until the main thread fills the inbox.
|
|
3
|
+
// The module reaches nothing but these imports (javascript.md section 2).
|
|
4
|
+
|
|
5
|
+
const node = typeof process === "object" && process.versions?.node;
|
|
6
|
+
const port = node ? (await import("node:worker_threads")).parentPort : self;
|
|
7
|
+
const post = (message, transfer) => port.postMessage(message, transfer);
|
|
8
|
+
|
|
9
|
+
class Exit extends Error {
|
|
10
|
+
constructor(code) { super(`exit ${code}`); this.code = code; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function start({ module, pages, maximum, inbox }) {
|
|
14
|
+
const memory = new WebAssembly.Memory({ initial: pages, maximum });
|
|
15
|
+
const control = new Int32Array(inbox, 0, 2), data = new Uint8Array(inbox, 8);
|
|
16
|
+
let position = 0;
|
|
17
|
+
const bytes = () => new Uint8Array(memory.buffer);
|
|
18
|
+
const view = () => new DataView(memory.buffer);
|
|
19
|
+
|
|
20
|
+
// Main to worker: wait for a chunk, copy what fits, hand the inbox back
|
|
21
|
+
// once the chunk is used up.
|
|
22
|
+
function read(pointer, capacity) {
|
|
23
|
+
while (Atomics.load(control, 0) === 0) Atomics.wait(control, 0, 0);
|
|
24
|
+
const n = Math.min(capacity, control[1] - position);
|
|
25
|
+
bytes().set(data.subarray(position, position + n), pointer);
|
|
26
|
+
position += n;
|
|
27
|
+
if (position === control[1]) {
|
|
28
|
+
position = 0;
|
|
29
|
+
Atomics.store(control, 0, 0);
|
|
30
|
+
Atomics.notify(control, 0);
|
|
31
|
+
}
|
|
32
|
+
return n;
|
|
33
|
+
}
|
|
34
|
+
// Worker to main: post a copy.
|
|
35
|
+
function write(pointer, length) {
|
|
36
|
+
const chunk = bytes().slice(pointer, pointer + length);
|
|
37
|
+
post(chunk, [chunk.buffer]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// WASI: diagnostics on stderr, clocks, exit; everything else is refused.
|
|
41
|
+
const ENOSYS = 52, EBADF = 8, ESPIPE = 70;
|
|
42
|
+
const decoder = new TextDecoder();
|
|
43
|
+
const wasi = {
|
|
44
|
+
args_sizes_get(count, size) { view().setUint32(count, 0, true); view().setUint32(size, 0, true); return 0; },
|
|
45
|
+
args_get() { return 0; },
|
|
46
|
+
clock_time_get(id, precision, out) {
|
|
47
|
+
const ns = id === 0 ? BigInt(Date.now()) * 1000000n : BigInt(Math.round(performance.now() * 1e6));
|
|
48
|
+
view().setBigUint64(out, ns, true);
|
|
49
|
+
return 0;
|
|
50
|
+
},
|
|
51
|
+
fd_write(fd, iovs, count, written) {
|
|
52
|
+
if (fd !== 1 && fd !== 2) return EBADF;
|
|
53
|
+
let total = 0, text = "";
|
|
54
|
+
for (let i = 0; i < count; i++) {
|
|
55
|
+
const at = view().getUint32(iovs + 8 * i, true), n = view().getUint32(iovs + 8 * i + 4, true);
|
|
56
|
+
text += decoder.decode(bytes().subarray(at, at + n), { stream: true });
|
|
57
|
+
total += n;
|
|
58
|
+
}
|
|
59
|
+
if (text) console.error(text.replace(/\n$/, ""));
|
|
60
|
+
view().setUint32(written, total, true);
|
|
61
|
+
return 0;
|
|
62
|
+
},
|
|
63
|
+
fd_fdstat_get(fd, out) {
|
|
64
|
+
if (fd > 2) return EBADF;
|
|
65
|
+
bytes().fill(0, out, out + 24);
|
|
66
|
+
view().setUint8(out, 2);
|
|
67
|
+
return 0;
|
|
68
|
+
},
|
|
69
|
+
fd_seek() { return ESPIPE; },
|
|
70
|
+
fd_close(fd) { return fd > 2 ? EBADF : 0; },
|
|
71
|
+
proc_exit(code) { throw new Exit(code); },
|
|
72
|
+
};
|
|
73
|
+
const imports = { env: { memory }, spy: { read, write }, wasi_snapshot_preview1: {} };
|
|
74
|
+
for (const { module: name, name: field, kind } of WebAssembly.Module.imports(module))
|
|
75
|
+
if (name === "wasi_snapshot_preview1" && kind === "function")
|
|
76
|
+
imports.wasi_snapshot_preview1[field] = wasi[field] ?? (() => ENOSYS);
|
|
77
|
+
|
|
78
|
+
const instance = new WebAssembly.Instance(module, imports);
|
|
79
|
+
try {
|
|
80
|
+
instance.exports._start();
|
|
81
|
+
post({ exit: 0 });
|
|
82
|
+
} catch (e) {
|
|
83
|
+
post(e instanceof Exit ? { exit: e.code } : { trap: String(e) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (node) port.once("message", start);
|
|
88
|
+
else port.addEventListener("message", (event) => start(event.data), { once: true });
|