@scelar/nodepod 1.0.2 → 1.0.4
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/dist/__sw__.js +642 -642
- package/dist/__tests__/bench/integration.bench.d.ts +1 -0
- package/dist/__tests__/bench/memory-volume.bench.d.ts +1 -0
- package/dist/__tests__/bench/polyfills.bench.d.ts +1 -0
- package/dist/__tests__/bench/script-engine.bench.d.ts +1 -0
- package/dist/__tests__/bench/shell.bench.d.ts +1 -0
- package/dist/__tests__/bench/syntax-transforms.bench.d.ts +1 -0
- package/dist/__tests__/bench/version-resolver.bench.d.ts +1 -0
- package/dist/__tests__/buffer.test.d.ts +1 -0
- package/dist/__tests__/byte-encoding.test.d.ts +1 -0
- package/dist/__tests__/digest.test.d.ts +1 -0
- package/dist/__tests__/events.test.d.ts +1 -0
- package/dist/__tests__/memory-volume.test.d.ts +1 -0
- package/dist/__tests__/path.test.d.ts +1 -0
- package/dist/__tests__/process.test.d.ts +1 -0
- package/dist/__tests__/script-engine.test.d.ts +1 -0
- package/dist/__tests__/shell-builtins.test.d.ts +1 -0
- package/dist/__tests__/shell-interpreter.test.d.ts +1 -0
- package/dist/__tests__/shell-parser.test.d.ts +1 -0
- package/dist/__tests__/stream.test.d.ts +1 -0
- package/dist/__tests__/syntax-transforms.test.d.ts +1 -0
- package/dist/__tests__/version-resolver.test.d.ts +1 -0
- package/dist/{child_process-Dopvyd-E.js → child_process-53fMkug_.js} +4 -4
- package/dist/child_process-53fMkug_.js.map +1 -0
- package/dist/{child_process-B38qoN6R.cjs → child_process-lxSKECHq.cjs} +5 -5
- package/dist/child_process-lxSKECHq.cjs.map +1 -0
- package/dist/{index--Qr8LVpQ.js → index-B8lyh_ti.js} +1316 -559
- package/dist/index-B8lyh_ti.js.map +1 -0
- package/dist/{index-cnitc68U.cjs → index-C-TQIrdG.cjs} +1422 -612
- package/dist/index-C-TQIrdG.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/memory-volume.d.ts +1 -1
- package/dist/polyfills/wasi.d.ts +45 -4
- package/dist/script-engine.d.ts +2 -0
- package/dist/sdk/nodepod.d.ts +4 -3
- package/dist/sdk/types.d.ts +6 -0
- package/dist/syntax-transforms.d.ts +1 -0
- package/dist/threading/process-manager.d.ts +1 -1
- package/dist/threading/worker-protocol.d.ts +1 -1
- package/package.json +5 -3
- package/src/__tests__/bench/integration.bench.ts +117 -0
- package/src/__tests__/bench/memory-volume.bench.ts +115 -0
- package/src/__tests__/bench/polyfills.bench.ts +147 -0
- package/src/__tests__/bench/script-engine.bench.ts +104 -0
- package/src/__tests__/bench/shell.bench.ts +101 -0
- package/src/__tests__/bench/syntax-transforms.bench.ts +82 -0
- package/src/__tests__/bench/version-resolver.bench.ts +95 -0
- package/src/__tests__/buffer.test.ts +273 -0
- package/src/__tests__/byte-encoding.test.ts +98 -0
- package/src/__tests__/digest.test.ts +44 -0
- package/src/__tests__/events.test.ts +245 -0
- package/src/__tests__/memory-volume.test.ts +443 -0
- package/src/__tests__/path.test.ts +181 -0
- package/src/__tests__/process.test.ts +129 -0
- package/src/__tests__/script-engine.test.ts +229 -0
- package/src/__tests__/shell-builtins.test.ts +357 -0
- package/src/__tests__/shell-interpreter.test.ts +157 -0
- package/src/__tests__/shell-parser.test.ts +204 -0
- package/src/__tests__/stream.test.ts +142 -0
- package/src/__tests__/syntax-transforms.test.ts +158 -0
- package/src/__tests__/version-resolver.test.ts +184 -0
- package/src/constants/cdn-urls.ts +18 -18
- package/src/helpers/byte-encoding.ts +51 -39
- package/src/index.ts +192 -192
- package/src/memory-volume.ts +968 -941
- package/src/module-transformer.ts +368 -368
- package/src/packages/installer.ts +396 -396
- package/src/packages/version-resolver.ts +12 -2
- package/src/polyfills/buffer.ts +633 -628
- package/src/polyfills/child_process.ts +2288 -2288
- package/src/polyfills/esbuild.ts +854 -854
- package/src/polyfills/events.ts +282 -276
- package/src/polyfills/fs.ts +2888 -2888
- package/src/polyfills/http.ts +1450 -1449
- package/src/polyfills/process.ts +721 -690
- package/src/polyfills/readline.ts +692 -692
- package/src/polyfills/stream.ts +1620 -1620
- package/src/polyfills/tty.ts +71 -71
- package/src/polyfills/wasi.ts +1284 -22
- package/src/request-proxy.ts +716 -716
- package/src/script-engine.ts +465 -146
- package/src/sdk/nodepod.ts +525 -509
- package/src/sdk/types.ts +7 -0
- package/src/syntax-transforms.ts +543 -561
- package/src/threading/offload-worker.ts +383 -383
- package/src/threading/offload.ts +271 -271
- package/src/threading/process-manager.ts +956 -956
- package/src/threading/process-worker-entry.ts +858 -854
- package/src/threading/worker-protocol.ts +1 -1
- package/dist/child_process-B38qoN6R.cjs.map +0 -1
- package/dist/child_process-Dopvyd-E.js.map +0 -1
- package/dist/index--Qr8LVpQ.js.map +0 -1
- package/dist/index-cnitc68U.cjs.map +0 -1
package/src/polyfills/wasi.ts
CHANGED
|
@@ -1,44 +1,1306 @@
|
|
|
1
|
-
|
|
1
|
+
const ERRNO_SUCCESS = 0;
|
|
2
|
+
const ERRNO_2BIG = 1;
|
|
3
|
+
const ERRNO_ACCES = 2;
|
|
4
|
+
const ERRNO_BADF = 8;
|
|
5
|
+
const ERRNO_EXIST = 20;
|
|
6
|
+
const ERRNO_FAULT = 21;
|
|
7
|
+
const ERRNO_INVAL = 28;
|
|
8
|
+
const ERRNO_IO = 29;
|
|
9
|
+
const ERRNO_ISDIR = 31;
|
|
10
|
+
const ERRNO_NOENT = 44;
|
|
11
|
+
const ERRNO_NOSYS = 52;
|
|
12
|
+
const ERRNO_NOTDIR = 54;
|
|
13
|
+
const ERRNO_NOTEMPTY = 55;
|
|
14
|
+
const ERRNO_PERM = 63;
|
|
15
|
+
const ERRNO_PIPE = 64;
|
|
16
|
+
const ERRNO_SPIPE = 70;
|
|
2
17
|
|
|
18
|
+
const CLOCKID_REALTIME = 0;
|
|
19
|
+
const CLOCKID_MONOTONIC = 1;
|
|
20
|
+
const CLOCKID_PROCESS_CPUTIME_ID = 2;
|
|
21
|
+
const CLOCKID_THREAD_CPUTIME_ID = 3;
|
|
22
|
+
|
|
23
|
+
const FILETYPE_UNKNOWN = 0;
|
|
24
|
+
const FILETYPE_DIRECTORY = 3;
|
|
25
|
+
const FILETYPE_REGULAR_FILE = 4;
|
|
26
|
+
const FILETYPE_SYMBOLIC_LINK = 7;
|
|
27
|
+
|
|
28
|
+
const FDFLAGS_APPEND = 0x0001;
|
|
29
|
+
|
|
30
|
+
const FSTFLAGS_ATIM = 0x0001;
|
|
31
|
+
const FSTFLAGS_ATIM_NOW = 0x0002;
|
|
32
|
+
const FSTFLAGS_MTIM = 0x0004;
|
|
33
|
+
const FSTFLAGS_MTIM_NOW = 0x0008;
|
|
34
|
+
|
|
35
|
+
const OFLAGS_CREAT = 0x0001;
|
|
36
|
+
const OFLAGS_DIRECTORY = 0x0002;
|
|
37
|
+
const OFLAGS_EXCL = 0x0004;
|
|
38
|
+
const OFLAGS_TRUNC = 0x0008;
|
|
39
|
+
|
|
40
|
+
const WHENCE_SET = 0;
|
|
41
|
+
const WHENCE_CUR = 1;
|
|
42
|
+
const WHENCE_END = 2;
|
|
43
|
+
|
|
44
|
+
const PREOPENTYPE_DIR = 0;
|
|
45
|
+
|
|
46
|
+
const RIGHTS_FD_READ = 0x0000000000000002n;
|
|
47
|
+
const RIGHTS_FD_WRITE = 0x0000000000000040n;
|
|
48
|
+
const RIGHTS_FD_SEEK = 0x0000000000000004n;
|
|
49
|
+
const RIGHTS_FD_TELL = 0x0000000000000020n;
|
|
50
|
+
const RIGHTS_FD_READDIR = 0x0000000000004000n;
|
|
51
|
+
const RIGHTS_PATH_OPEN = 0x0000000000002000n;
|
|
52
|
+
const RIGHTS_PATH_CREATE_DIRECTORY = 0x0000000000000200n;
|
|
53
|
+
const RIGHTS_PATH_CREATE_FILE = 0x0000000000000400n;
|
|
54
|
+
const RIGHTS_PATH_UNLINK_FILE = 0x0000000004000000n;
|
|
55
|
+
const RIGHTS_PATH_REMOVE_DIRECTORY = 0x0000000002000000n;
|
|
56
|
+
const RIGHTS_PATH_RENAME_SOURCE = 0x0000000000010000n;
|
|
57
|
+
const RIGHTS_PATH_RENAME_TARGET = 0x0000000000020000n;
|
|
58
|
+
const RIGHTS_PATH_FILESTAT_GET = 0x0000000000040000n;
|
|
59
|
+
const RIGHTS_PATH_SYMLINK = 0x0000000001000000n;
|
|
60
|
+
const RIGHTS_PATH_READLINK = 0x0000000000008000n;
|
|
61
|
+
const RIGHTS_FD_FILESTAT_GET = 0x0000000000200000n;
|
|
62
|
+
const RIGHTS_FD_FILESTAT_SET_SIZE = 0x0000000000400000n;
|
|
63
|
+
|
|
64
|
+
const RIGHTS_ALL = 0x1fffffffn;
|
|
65
|
+
const RIGHTS_DIR_BASE =
|
|
66
|
+
RIGHTS_FD_READ |
|
|
67
|
+
RIGHTS_FD_READDIR |
|
|
68
|
+
RIGHTS_PATH_OPEN |
|
|
69
|
+
RIGHTS_PATH_CREATE_DIRECTORY |
|
|
70
|
+
RIGHTS_PATH_CREATE_FILE |
|
|
71
|
+
RIGHTS_PATH_UNLINK_FILE |
|
|
72
|
+
RIGHTS_PATH_REMOVE_DIRECTORY |
|
|
73
|
+
RIGHTS_PATH_RENAME_SOURCE |
|
|
74
|
+
RIGHTS_PATH_RENAME_TARGET |
|
|
75
|
+
RIGHTS_PATH_FILESTAT_GET |
|
|
76
|
+
RIGHTS_PATH_SYMLINK |
|
|
77
|
+
RIGHTS_PATH_READLINK |
|
|
78
|
+
RIGHTS_FD_FILESTAT_GET;
|
|
79
|
+
const RIGHTS_FILE_BASE =
|
|
80
|
+
RIGHTS_FD_READ |
|
|
81
|
+
RIGHTS_FD_WRITE |
|
|
82
|
+
RIGHTS_FD_SEEK |
|
|
83
|
+
RIGHTS_FD_TELL |
|
|
84
|
+
RIGHTS_FD_FILESTAT_GET |
|
|
85
|
+
RIGHTS_FD_FILESTAT_SET_SIZE;
|
|
86
|
+
|
|
87
|
+
const EVENTTYPE_CLOCK = 0;
|
|
88
|
+
const EVENTTYPE_FD_READ = 1;
|
|
89
|
+
const EVENTTYPE_FD_WRITE = 2;
|
|
90
|
+
|
|
91
|
+
/* ------------------------------------------------------------------ */
|
|
92
|
+
/* Filesystem interface (subset of our MemoryVolume / fs bridge) */
|
|
93
|
+
/* ------------------------------------------------------------------ */
|
|
94
|
+
|
|
95
|
+
interface WasiFileStat {
|
|
96
|
+
isFile(): boolean;
|
|
97
|
+
isDirectory(): boolean;
|
|
98
|
+
isSymbolicLink(): boolean;
|
|
99
|
+
size: number;
|
|
100
|
+
mtimeMs: number;
|
|
101
|
+
atimeMs: number;
|
|
102
|
+
ctimeMs: number;
|
|
103
|
+
ino?: number;
|
|
104
|
+
nlink?: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface WasiFS {
|
|
108
|
+
readFileSync(p: string): Uint8Array;
|
|
109
|
+
writeFileSync(p: string, data: string | Uint8Array): void;
|
|
110
|
+
mkdirSync(p: string, options?: { recursive?: boolean }): void;
|
|
111
|
+
statSync(p: string): WasiFileStat;
|
|
112
|
+
readdirSync(p: string): string[];
|
|
113
|
+
unlinkSync(p: string): void;
|
|
114
|
+
rmdirSync(p: string): void;
|
|
115
|
+
renameSync(from: string, to: string): void;
|
|
116
|
+
existsSync(p: string): boolean;
|
|
117
|
+
symlinkSync?(target: string, linkPath: string): void;
|
|
118
|
+
readlinkSync?(p: string): string;
|
|
119
|
+
}
|
|
3
120
|
|
|
4
121
|
/* ------------------------------------------------------------------ */
|
|
5
|
-
/*
|
|
122
|
+
/* File descriptor table */
|
|
6
123
|
/* ------------------------------------------------------------------ */
|
|
7
124
|
|
|
125
|
+
const enum FdKind {
|
|
126
|
+
Stdin,
|
|
127
|
+
Stdout,
|
|
128
|
+
Stderr,
|
|
129
|
+
PreopenDir,
|
|
130
|
+
File,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface FdEntry {
|
|
134
|
+
kind: FdKind;
|
|
135
|
+
path: string; // real path (for dirs and files)
|
|
136
|
+
rights: bigint;
|
|
137
|
+
// file-specific
|
|
138
|
+
data?: Uint8Array;
|
|
139
|
+
offset?: number;
|
|
140
|
+
dirty?: boolean; // needs flush on close
|
|
141
|
+
flags?: number; // O_APPEND etc
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ------------------------------------------------------------------ */
|
|
145
|
+
/* ExitStatus */
|
|
146
|
+
/* ---------------------------------------------------------------- */
|
|
147
|
+
export class ExitStatus extends Error {
|
|
148
|
+
readonly code: number;
|
|
149
|
+
constructor(code: number) {
|
|
150
|
+
super(`WASI exit(${code})`);
|
|
151
|
+
this.code = code;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* ------------------------------------------------------------------ */
|
|
156
|
+
/* syscall wrapper */
|
|
157
|
+
/* ---------------------------------------------------------------- */
|
|
158
|
+
function syscall(target: Function): Function {
|
|
159
|
+
return function (this: unknown, ...args: unknown[]): number {
|
|
160
|
+
try {
|
|
161
|
+
return target.apply(this, args);
|
|
162
|
+
} catch (err: any) {
|
|
163
|
+
if (err instanceof ExitStatus) throw err;
|
|
164
|
+
// map common fs errors to WASI errno
|
|
165
|
+
const code = err?.code;
|
|
166
|
+
if (code === "ENOENT") return ERRNO_NOENT;
|
|
167
|
+
if (code === "EEXIST") return ERRNO_EXIST;
|
|
168
|
+
if (code === "EISDIR") return ERRNO_ISDIR;
|
|
169
|
+
if (code === "ENOTDIR") return ERRNO_NOTDIR;
|
|
170
|
+
if (code === "ENOTEMPTY") return ERRNO_NOTEMPTY;
|
|
171
|
+
if (code === "EACCES" || code === "EPERM") return ERRNO_ACCES;
|
|
172
|
+
return ERRNO_IO;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Path helpers */
|
|
178
|
+
|
|
179
|
+
function joinPath(base: string, rel: string): string {
|
|
180
|
+
if (rel.startsWith("/")) return normalizePath(rel);
|
|
181
|
+
const combined = base.endsWith("/") ? base + rel : base + "/" + rel;
|
|
182
|
+
return normalizePath(combined);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function normalizePath(p: string): string {
|
|
186
|
+
const parts = p.split("/");
|
|
187
|
+
const out: string[] = [];
|
|
188
|
+
for (const seg of parts) {
|
|
189
|
+
if (seg === "." || seg === "") continue;
|
|
190
|
+
if (seg === "..") {
|
|
191
|
+
out.pop();
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
out.push(seg);
|
|
195
|
+
}
|
|
196
|
+
return "/" + out.join("/");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Text encoder / decoder (cached)*/
|
|
200
|
+
const encoder = new TextEncoder();
|
|
201
|
+
const decoder = new TextDecoder();
|
|
202
|
+
|
|
203
|
+
/* WASI class (matches Nodejs `wasi` module API) */
|
|
204
|
+
|
|
205
|
+
export interface WASIOptions {
|
|
206
|
+
version?: "preview1" | "unstable";
|
|
207
|
+
args?: string[];
|
|
208
|
+
env?: Record<string, string>;
|
|
209
|
+
preopens?: Record<string, string>;
|
|
210
|
+
returnOnExit?: boolean;
|
|
211
|
+
stdin?: number;
|
|
212
|
+
stdout?: number;
|
|
213
|
+
stderr?: number;
|
|
214
|
+
// extensions for our environment
|
|
215
|
+
fs?: WasiFS;
|
|
216
|
+
}
|
|
217
|
+
|
|
8
218
|
export interface WASI {
|
|
9
219
|
readonly wasiImport: Record<string, Function>;
|
|
10
|
-
start(
|
|
11
|
-
initialize(
|
|
220
|
+
start(instance: object): number;
|
|
221
|
+
initialize(instance: object): void;
|
|
12
222
|
getImportObject(): Record<string, Record<string, Function>>;
|
|
13
223
|
}
|
|
14
224
|
|
|
15
225
|
interface WASIConstructor {
|
|
16
|
-
new (
|
|
17
|
-
(this: any,
|
|
226
|
+
new (options?: WASIOptions): WASI;
|
|
227
|
+
(this: any, options?: WASIOptions): void;
|
|
18
228
|
prototype: any;
|
|
19
229
|
}
|
|
20
230
|
|
|
21
|
-
export const WASI = function WASI(this: any,
|
|
22
|
-
if (!this)
|
|
23
|
-
|
|
24
|
-
|
|
231
|
+
export const WASI = function WASI(this: any, options?: WASIOptions) {
|
|
232
|
+
if (!(this instanceof WASI)) {
|
|
233
|
+
throw new TypeError(
|
|
234
|
+
"Class constructor WASI cannot be invoked without 'new'",
|
|
235
|
+
);
|
|
236
|
+
}
|
|
25
237
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
238
|
+
const opts = options ?? {};
|
|
239
|
+
const args = opts.args ?? [];
|
|
240
|
+
const envVars = opts.env ?? {};
|
|
241
|
+
const preopens = opts.preopens ?? {};
|
|
242
|
+
const returnOnExit = opts.returnOnExit ?? false;
|
|
243
|
+
const fs = opts.fs ?? null;
|
|
29
244
|
|
|
30
|
-
|
|
31
|
-
throw new Error("WASI is not supported in the browser environment");
|
|
32
|
-
};
|
|
245
|
+
/* file descriptor table */
|
|
33
246
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
247
|
+
const fds = new Map<number, FdEntry>();
|
|
248
|
+
let nextFd = 3;
|
|
37
249
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
250
|
+
// fd 0 = stdin, fd 1 = stdout, fd 2 = stderr
|
|
251
|
+
fds.set(0, { kind: FdKind.Stdin, path: "", rights: RIGHTS_FD_READ });
|
|
252
|
+
fds.set(1, { kind: FdKind.Stdout, path: "", rights: RIGHTS_FD_WRITE });
|
|
253
|
+
fds.set(2, { kind: FdKind.Stderr, path: "", rights: RIGHTS_FD_WRITE });
|
|
254
|
+
|
|
255
|
+
// preopened directories
|
|
256
|
+
const preopenEntries: Array<{
|
|
257
|
+
fd: number;
|
|
258
|
+
virtualPath: string;
|
|
259
|
+
realPath: string;
|
|
260
|
+
}> = [];
|
|
261
|
+
for (const [virtualPath, realPath] of Object.entries(preopens)) {
|
|
262
|
+
const fd = nextFd++;
|
|
263
|
+
fds.set(fd, {
|
|
264
|
+
kind: FdKind.PreopenDir,
|
|
265
|
+
path: realPath,
|
|
266
|
+
rights: RIGHTS_DIR_BASE,
|
|
267
|
+
});
|
|
268
|
+
preopenEntries.push({ fd, virtualPath, realPath });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* wasm memory ref */
|
|
272
|
+
|
|
273
|
+
let memory: WebAssembly.Memory | null = null;
|
|
274
|
+
let instance: WebAssembly.Instance | null = null;
|
|
275
|
+
|
|
276
|
+
const getMemory = (): WebAssembly.Memory => {
|
|
277
|
+
if (memory) return memory;
|
|
278
|
+
if (instance) {
|
|
279
|
+
memory = instance.exports.memory as WebAssembly.Memory;
|
|
280
|
+
if (memory) return memory;
|
|
281
|
+
}
|
|
282
|
+
throw new Error("WASI: WebAssembly.Memory not available");
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const view = () => new DataView(getMemory().buffer);
|
|
286
|
+
const bytes = () => new Uint8Array(getMemory().buffer);
|
|
287
|
+
|
|
288
|
+
/* stdout / stderr text buffers */
|
|
289
|
+
|
|
290
|
+
let stdoutBuf = "";
|
|
291
|
+
let stderrBuf = "";
|
|
292
|
+
|
|
293
|
+
const flushLine = (fd: number, buf: string): string => {
|
|
294
|
+
const nl = buf.lastIndexOf("\n");
|
|
295
|
+
if (nl < 0) return buf;
|
|
296
|
+
const lines = buf.substring(0, nl);
|
|
297
|
+
if (fd === 1) console.log(lines);
|
|
298
|
+
else console.error(lines);
|
|
299
|
+
return buf.substring(nl + 1);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/* helpers */
|
|
303
|
+
|
|
304
|
+
const readString = (ptr: number, len: number): string => {
|
|
305
|
+
return decoder.decode(new Uint8Array(getMemory().buffer, ptr, len));
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const writeString = (ptr: number, str: string): number => {
|
|
309
|
+
const encoded = encoder.encode(str + "\0");
|
|
310
|
+
bytes().set(encoded, ptr);
|
|
311
|
+
return encoded.length;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const flushFile = (entry: FdEntry): void => {
|
|
315
|
+
if (entry.dirty && fs && entry.path && entry.data) {
|
|
316
|
+
fs.writeFileSync(entry.path, entry.data);
|
|
317
|
+
entry.dirty = false;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
/* Build the wasi_snapshot_preview1 import object */
|
|
322
|
+
const wasiImport: Record<string, Function> = {
|
|
323
|
+
/* args */
|
|
324
|
+
args_get: syscall((argv_ptr: number, argv_buf_ptr: number): number => {
|
|
325
|
+
const dv = view();
|
|
326
|
+
const mem = bytes();
|
|
327
|
+
for (const arg of args) {
|
|
328
|
+
dv.setUint32(argv_ptr, argv_buf_ptr, true);
|
|
329
|
+
argv_ptr += 4;
|
|
330
|
+
const encoded = encoder.encode(arg + "\0");
|
|
331
|
+
mem.set(encoded, argv_buf_ptr);
|
|
332
|
+
argv_buf_ptr += encoded.length;
|
|
333
|
+
}
|
|
334
|
+
return ERRNO_SUCCESS;
|
|
335
|
+
}),
|
|
336
|
+
|
|
337
|
+
args_sizes_get: syscall(
|
|
338
|
+
(argc_out: number, argv_buf_size_out: number): number => {
|
|
339
|
+
const dv = view();
|
|
340
|
+
dv.setUint32(argc_out, args.length, true);
|
|
341
|
+
let bufSize = 0;
|
|
342
|
+
for (const arg of args) bufSize += encoder.encode(arg + "\0").length;
|
|
343
|
+
dv.setUint32(argv_buf_size_out, bufSize, true);
|
|
344
|
+
return ERRNO_SUCCESS;
|
|
345
|
+
},
|
|
346
|
+
),
|
|
347
|
+
|
|
348
|
+
/* environ */
|
|
349
|
+
|
|
350
|
+
environ_get: syscall(
|
|
351
|
+
(environ_ptr: number, environ_buf_ptr: number): number => {
|
|
352
|
+
const entries = Object.entries(envVars);
|
|
353
|
+
const dv = view();
|
|
354
|
+
const mem = bytes();
|
|
355
|
+
for (const [key, value] of entries) {
|
|
356
|
+
dv.setUint32(environ_ptr, environ_buf_ptr, true);
|
|
357
|
+
environ_ptr += 4;
|
|
358
|
+
const encoded = encoder.encode(`${key}=${value}\0`);
|
|
359
|
+
mem.set(encoded, environ_buf_ptr);
|
|
360
|
+
environ_buf_ptr += encoded.length;
|
|
361
|
+
}
|
|
362
|
+
return ERRNO_SUCCESS;
|
|
363
|
+
},
|
|
364
|
+
),
|
|
365
|
+
|
|
366
|
+
environ_sizes_get: syscall(
|
|
367
|
+
(environc_out: number, environ_buf_size_out: number): number => {
|
|
368
|
+
const entries = Object.entries(envVars);
|
|
369
|
+
const dv = view();
|
|
370
|
+
dv.setUint32(environc_out, entries.length, true);
|
|
371
|
+
let bufSize = 0;
|
|
372
|
+
for (const [key, value] of entries)
|
|
373
|
+
bufSize += encoder.encode(`${key}=${value}\0`).length;
|
|
374
|
+
dv.setUint32(environ_buf_size_out, bufSize, true);
|
|
375
|
+
return ERRNO_SUCCESS;
|
|
376
|
+
},
|
|
377
|
+
),
|
|
378
|
+
|
|
379
|
+
/* clock */
|
|
380
|
+
|
|
381
|
+
clock_res_get: syscall((id: number, resolution_out: number): number => {
|
|
382
|
+
const dv = view();
|
|
383
|
+
switch (id) {
|
|
384
|
+
case CLOCKID_REALTIME:
|
|
385
|
+
dv.setBigUint64(resolution_out, BigInt(1e6), true);
|
|
386
|
+
break;
|
|
387
|
+
case CLOCKID_MONOTONIC:
|
|
388
|
+
case CLOCKID_PROCESS_CPUTIME_ID:
|
|
389
|
+
case CLOCKID_THREAD_CPUTIME_ID:
|
|
390
|
+
dv.setBigUint64(resolution_out, BigInt(1e3), true);
|
|
391
|
+
break;
|
|
392
|
+
default:
|
|
393
|
+
return ERRNO_INVAL;
|
|
394
|
+
}
|
|
395
|
+
return ERRNO_SUCCESS;
|
|
396
|
+
}),
|
|
397
|
+
|
|
398
|
+
clock_time_get: syscall(
|
|
399
|
+
(id: number, _precision: bigint, time_out: number): number => {
|
|
400
|
+
const dv = view();
|
|
401
|
+
switch (id) {
|
|
402
|
+
case CLOCKID_REALTIME: {
|
|
403
|
+
const time = BigInt(Date.now()) * BigInt(1e6);
|
|
404
|
+
dv.setBigUint64(time_out, time, true);
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case CLOCKID_MONOTONIC:
|
|
408
|
+
case CLOCKID_PROCESS_CPUTIME_ID:
|
|
409
|
+
case CLOCKID_THREAD_CPUTIME_ID: {
|
|
410
|
+
const t = performance.now();
|
|
411
|
+
const s = Math.trunc(t);
|
|
412
|
+
const ms = Math.floor((t - s) * 1e3);
|
|
413
|
+
const time = BigInt(s) * BigInt(1e9) + BigInt(ms) * BigInt(1e6);
|
|
414
|
+
dv.setBigUint64(time_out, time, true);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
default:
|
|
418
|
+
return ERRNO_INVAL;
|
|
419
|
+
}
|
|
420
|
+
return ERRNO_SUCCESS;
|
|
421
|
+
},
|
|
422
|
+
),
|
|
423
|
+
|
|
424
|
+
/* ---- fd operations ------------------------------------------- */
|
|
425
|
+
|
|
426
|
+
fd_advise: syscall(
|
|
427
|
+
(_fd: number, _offset: bigint, _len: bigint, _advice: number): number => {
|
|
428
|
+
return ERRNO_SUCCESS; // advisory, can be a no-op
|
|
429
|
+
},
|
|
430
|
+
),
|
|
431
|
+
|
|
432
|
+
fd_allocate: syscall(
|
|
433
|
+
(_fd: number, _offset: bigint, _len: bigint): number => {
|
|
434
|
+
return ERRNO_NOSYS;
|
|
435
|
+
},
|
|
436
|
+
),
|
|
437
|
+
|
|
438
|
+
fd_close: syscall((fd: number): number => {
|
|
439
|
+
const entry = fds.get(fd);
|
|
440
|
+
if (!entry) return ERRNO_BADF;
|
|
441
|
+
if (entry.kind === FdKind.File) flushFile(entry);
|
|
442
|
+
fds.delete(fd);
|
|
443
|
+
return ERRNO_SUCCESS;
|
|
444
|
+
}),
|
|
445
|
+
|
|
446
|
+
fd_datasync: syscall((fd: number): number => {
|
|
447
|
+
const entry = fds.get(fd);
|
|
448
|
+
if (!entry) return ERRNO_BADF;
|
|
449
|
+
if (entry.kind === FdKind.File) flushFile(entry);
|
|
450
|
+
return ERRNO_SUCCESS;
|
|
451
|
+
}),
|
|
452
|
+
|
|
453
|
+
fd_fdstat_get: syscall((fd: number, stat_out: number): number => {
|
|
454
|
+
const entry = fds.get(fd);
|
|
455
|
+
if (!entry) return ERRNO_BADF;
|
|
456
|
+
const dv = view();
|
|
457
|
+
|
|
458
|
+
let filetype = FILETYPE_UNKNOWN;
|
|
459
|
+
let fdflags = 0;
|
|
460
|
+
let rightsBase = entry.rights;
|
|
461
|
+
let rightsInheriting = 0n;
|
|
462
|
+
|
|
463
|
+
switch (entry.kind) {
|
|
464
|
+
case FdKind.Stdin:
|
|
465
|
+
case FdKind.Stdout:
|
|
466
|
+
case FdKind.Stderr:
|
|
467
|
+
filetype = FILETYPE_UNKNOWN; // character device, but WASI doesnt have that in snapshot1 for this
|
|
468
|
+
break;
|
|
469
|
+
case FdKind.PreopenDir:
|
|
470
|
+
filetype = FILETYPE_DIRECTORY;
|
|
471
|
+
rightsInheriting = RIGHTS_ALL;
|
|
472
|
+
break;
|
|
473
|
+
case FdKind.File:
|
|
474
|
+
filetype = FILETYPE_REGULAR_FILE;
|
|
475
|
+
if (entry.flags && entry.flags & FDFLAGS_APPEND)
|
|
476
|
+
fdflags |= FDFLAGS_APPEND;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// fdstat layout: u8 filetype, u16 fdflags, u64 rights_base, u64 rights_inheriting
|
|
481
|
+
dv.setUint8(stat_out, filetype);
|
|
482
|
+
dv.setUint16(stat_out + 2, fdflags, true);
|
|
483
|
+
dv.setBigUint64(stat_out + 8, rightsBase, true);
|
|
484
|
+
dv.setBigUint64(stat_out + 16, rightsInheriting, true);
|
|
485
|
+
return ERRNO_SUCCESS;
|
|
486
|
+
}),
|
|
487
|
+
|
|
488
|
+
fd_fdstat_set_flags: syscall((_fd: number, _flags: number): number => {
|
|
489
|
+
return ERRNO_NOSYS;
|
|
490
|
+
}),
|
|
491
|
+
|
|
492
|
+
fd_fdstat_set_rights: syscall(
|
|
493
|
+
(
|
|
494
|
+
_fd: number,
|
|
495
|
+
_rights_base: bigint,
|
|
496
|
+
_rights_inheriting: bigint,
|
|
497
|
+
): number => {
|
|
498
|
+
return ERRNO_NOSYS;
|
|
499
|
+
},
|
|
500
|
+
),
|
|
501
|
+
|
|
502
|
+
fd_filestat_get: syscall((fd: number, buf_out: number): number => {
|
|
503
|
+
const entry = fds.get(fd);
|
|
504
|
+
if (!entry) return ERRNO_BADF;
|
|
505
|
+
|
|
506
|
+
const dv = view();
|
|
507
|
+
let size = 0n;
|
|
508
|
+
let filetype = FILETYPE_UNKNOWN;
|
|
509
|
+
let mtimeNs = 0n;
|
|
510
|
+
let atimeNs = 0n;
|
|
511
|
+
let ctimeNs = 0n;
|
|
512
|
+
let ino = 0n;
|
|
513
|
+
let nlink = 1n;
|
|
514
|
+
|
|
515
|
+
if (entry.kind === FdKind.File) {
|
|
516
|
+
size = BigInt(entry.data ? entry.data.length : 0);
|
|
517
|
+
filetype = FILETYPE_REGULAR_FILE;
|
|
518
|
+
if (fs && entry.path) {
|
|
519
|
+
try {
|
|
520
|
+
const stat = fs.statSync(entry.path);
|
|
521
|
+
mtimeNs = BigInt(Math.floor(stat.mtimeMs)) * BigInt(1e6);
|
|
522
|
+
atimeNs = BigInt(Math.floor(stat.atimeMs)) * BigInt(1e6);
|
|
523
|
+
ctimeNs = BigInt(Math.floor(stat.ctimeMs)) * BigInt(1e6);
|
|
524
|
+
if (stat.ino) ino = BigInt(stat.ino);
|
|
525
|
+
if (stat.nlink) nlink = BigInt(stat.nlink);
|
|
526
|
+
} catch {
|
|
527
|
+
/* ignore */
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
} else if (entry.kind === FdKind.PreopenDir) {
|
|
531
|
+
filetype = FILETYPE_DIRECTORY;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// filestat layout: u64 dev, u64 ino, u8 filetype (at +16), u64 nlink, u64 size,
|
|
535
|
+
// u64 atim, u64 mtim, u64 ctim
|
|
536
|
+
dv.setBigUint64(buf_out, 0n, true); // dev
|
|
537
|
+
dv.setBigUint64(buf_out + 8, ino, true); // ino
|
|
538
|
+
dv.setUint8(buf_out + 16, filetype); // filetype
|
|
539
|
+
dv.setBigUint64(buf_out + 24, nlink, true); // nlink
|
|
540
|
+
dv.setBigUint64(buf_out + 32, size, true); // size
|
|
541
|
+
dv.setBigUint64(buf_out + 40, atimeNs, true);
|
|
542
|
+
dv.setBigUint64(buf_out + 48, mtimeNs, true);
|
|
543
|
+
dv.setBigUint64(buf_out + 56, ctimeNs, true);
|
|
544
|
+
return ERRNO_SUCCESS;
|
|
545
|
+
}),
|
|
546
|
+
|
|
547
|
+
fd_filestat_set_size: syscall((fd: number, size: bigint): number => {
|
|
548
|
+
const entry = fds.get(fd);
|
|
549
|
+
if (!entry || entry.kind !== FdKind.File) return ERRNO_BADF;
|
|
550
|
+
const newSize = Number(size);
|
|
551
|
+
if (!entry.data) {
|
|
552
|
+
entry.data = new Uint8Array(newSize);
|
|
553
|
+
} else if (entry.data.length !== newSize) {
|
|
554
|
+
const newData = new Uint8Array(newSize);
|
|
555
|
+
newData.set(
|
|
556
|
+
entry.data.subarray(0, Math.min(entry.data.length, newSize)),
|
|
557
|
+
);
|
|
558
|
+
entry.data = newData;
|
|
559
|
+
}
|
|
560
|
+
entry.dirty = true;
|
|
561
|
+
return ERRNO_SUCCESS;
|
|
562
|
+
}),
|
|
563
|
+
|
|
564
|
+
fd_filestat_set_times: syscall(
|
|
565
|
+
(
|
|
566
|
+
_fd: number,
|
|
567
|
+
_atim: bigint,
|
|
568
|
+
_mtim: bigint,
|
|
569
|
+
_fst_flags: number,
|
|
570
|
+
): number => {
|
|
571
|
+
return ERRNO_SUCCESS; // no-op, our MemoryVolume auto timestamps
|
|
572
|
+
},
|
|
573
|
+
),
|
|
574
|
+
|
|
575
|
+
fd_pread: syscall(
|
|
576
|
+
(
|
|
577
|
+
fd: number,
|
|
578
|
+
iovs_ptr: number,
|
|
579
|
+
iovs_len: number,
|
|
580
|
+
offset: bigint,
|
|
581
|
+
nread_out: number,
|
|
582
|
+
): number => {
|
|
583
|
+
const entry = fds.get(fd);
|
|
584
|
+
if (!entry || entry.kind !== FdKind.File) return ERRNO_BADF;
|
|
585
|
+
if (!entry.data) {
|
|
586
|
+
view().setUint32(nread_out, 0, true);
|
|
587
|
+
return ERRNO_SUCCESS;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const dv = view();
|
|
591
|
+
let pos = Number(offset);
|
|
592
|
+
let totalRead = 0;
|
|
593
|
+
|
|
594
|
+
for (let i = 0; i < iovs_len; i++) {
|
|
595
|
+
const bufPtr = dv.getUint32(iovs_ptr + i * 8, true);
|
|
596
|
+
const bufLen = dv.getUint32(iovs_ptr + i * 8 + 4, true);
|
|
597
|
+
const toRead = Math.min(bufLen, entry.data.length - pos);
|
|
598
|
+
if (toRead <= 0) break;
|
|
599
|
+
bytes().set(entry.data.subarray(pos, pos + toRead), bufPtr);
|
|
600
|
+
pos += toRead;
|
|
601
|
+
totalRead += toRead;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
dv.setUint32(nread_out, totalRead, true);
|
|
605
|
+
return ERRNO_SUCCESS;
|
|
606
|
+
},
|
|
607
|
+
),
|
|
608
|
+
|
|
609
|
+
fd_prestat_get: syscall((fd: number, buf_out: number): number => {
|
|
610
|
+
const entry = fds.get(fd);
|
|
611
|
+
if (!entry || entry.kind !== FdKind.PreopenDir) return ERRNO_BADF;
|
|
612
|
+
|
|
613
|
+
const preopen = preopenEntries.find((p) => p.fd === fd);
|
|
614
|
+
if (!preopen) return ERRNO_BADF;
|
|
615
|
+
|
|
616
|
+
const dv = view();
|
|
617
|
+
const nameLen = encoder.encode(preopen.virtualPath).length;
|
|
618
|
+
// prestat: u8 type (0 = dir), then u32 name_len at +4
|
|
619
|
+
dv.setUint8(buf_out, PREOPENTYPE_DIR);
|
|
620
|
+
dv.setUint32(buf_out + 4, nameLen, true);
|
|
621
|
+
return ERRNO_SUCCESS;
|
|
622
|
+
}),
|
|
623
|
+
|
|
624
|
+
fd_prestat_dir_name: syscall(
|
|
625
|
+
(fd: number, path_ptr: number, path_len: number): number => {
|
|
626
|
+
const entry = fds.get(fd);
|
|
627
|
+
if (!entry || entry.kind !== FdKind.PreopenDir) return ERRNO_BADF;
|
|
628
|
+
|
|
629
|
+
const preopen = preopenEntries.find((p) => p.fd === fd);
|
|
630
|
+
if (!preopen) return ERRNO_BADF;
|
|
631
|
+
|
|
632
|
+
const encoded = encoder.encode(preopen.virtualPath);
|
|
633
|
+
const toCopy = Math.min(encoded.length, path_len);
|
|
634
|
+
bytes().set(encoded.subarray(0, toCopy), path_ptr);
|
|
635
|
+
return ERRNO_SUCCESS;
|
|
636
|
+
},
|
|
637
|
+
),
|
|
638
|
+
|
|
639
|
+
fd_pwrite: syscall(
|
|
640
|
+
(
|
|
641
|
+
fd: number,
|
|
642
|
+
iovs_ptr: number,
|
|
643
|
+
iovs_len: number,
|
|
644
|
+
offset: bigint,
|
|
645
|
+
nwritten_out: number,
|
|
646
|
+
): number => {
|
|
647
|
+
const entry = fds.get(fd);
|
|
648
|
+
if (!entry || entry.kind !== FdKind.File) return ERRNO_BADF;
|
|
649
|
+
|
|
650
|
+
const dv = view();
|
|
651
|
+
let pos = Number(offset);
|
|
652
|
+
let totalWritten = 0;
|
|
653
|
+
|
|
654
|
+
for (let i = 0; i < iovs_len; i++) {
|
|
655
|
+
const bufPtr = dv.getUint32(iovs_ptr + i * 8, true);
|
|
656
|
+
const bufLen = dv.getUint32(iovs_ptr + i * 8 + 4, true);
|
|
657
|
+
const chunk = new Uint8Array(getMemory().buffer, bufPtr, bufLen);
|
|
658
|
+
|
|
659
|
+
// grow data if needed
|
|
660
|
+
const needed = pos + bufLen;
|
|
661
|
+
if (!entry.data || entry.data.length < needed) {
|
|
662
|
+
const newData = new Uint8Array(needed);
|
|
663
|
+
if (entry.data) newData.set(entry.data);
|
|
664
|
+
entry.data = newData;
|
|
665
|
+
}
|
|
666
|
+
entry.data.set(chunk, pos);
|
|
667
|
+
pos += bufLen;
|
|
668
|
+
totalWritten += bufLen;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
entry.dirty = true;
|
|
672
|
+
dv.setUint32(nwritten_out, totalWritten, true);
|
|
673
|
+
return ERRNO_SUCCESS;
|
|
674
|
+
},
|
|
675
|
+
),
|
|
676
|
+
|
|
677
|
+
fd_read: syscall(
|
|
678
|
+
(
|
|
679
|
+
fd: number,
|
|
680
|
+
iovs_ptr: number,
|
|
681
|
+
iovs_len: number,
|
|
682
|
+
nread_out: number,
|
|
683
|
+
): number => {
|
|
684
|
+
const entry = fds.get(fd);
|
|
685
|
+
if (!entry) return ERRNO_BADF;
|
|
686
|
+
|
|
687
|
+
const dv = view();
|
|
688
|
+
|
|
689
|
+
if (entry.kind === FdKind.Stdin) {
|
|
690
|
+
// stdin - no interactive input in browser, return 0 bytes (EOF)
|
|
691
|
+
dv.setUint32(nread_out, 0, true);
|
|
692
|
+
return ERRNO_SUCCESS;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (entry.kind !== FdKind.File) return ERRNO_BADF;
|
|
696
|
+
if (!entry.data) {
|
|
697
|
+
dv.setUint32(nread_out, 0, true);
|
|
698
|
+
return ERRNO_SUCCESS;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
let totalRead = 0;
|
|
702
|
+
let pos = entry.offset ?? 0;
|
|
703
|
+
|
|
704
|
+
for (let i = 0; i < iovs_len; i++) {
|
|
705
|
+
const bufPtr = dv.getUint32(iovs_ptr + i * 8, true);
|
|
706
|
+
const bufLen = dv.getUint32(iovs_ptr + i * 8 + 4, true);
|
|
707
|
+
const toRead = Math.min(bufLen, entry.data.length - pos);
|
|
708
|
+
if (toRead <= 0) break;
|
|
709
|
+
bytes().set(entry.data.subarray(pos, pos + toRead), bufPtr);
|
|
710
|
+
pos += toRead;
|
|
711
|
+
totalRead += toRead;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
entry.offset = pos;
|
|
715
|
+
dv.setUint32(nread_out, totalRead, true);
|
|
716
|
+
return ERRNO_SUCCESS;
|
|
717
|
+
},
|
|
718
|
+
),
|
|
719
|
+
|
|
720
|
+
fd_readdir: syscall(
|
|
721
|
+
(
|
|
722
|
+
fd: number,
|
|
723
|
+
buf_ptr: number,
|
|
724
|
+
buf_len: number,
|
|
725
|
+
cookie: bigint,
|
|
726
|
+
bufused_out: number,
|
|
727
|
+
): number => {
|
|
728
|
+
const entry = fds.get(fd);
|
|
729
|
+
if (
|
|
730
|
+
!entry ||
|
|
731
|
+
(entry.kind !== FdKind.PreopenDir && entry.kind !== FdKind.File)
|
|
732
|
+
)
|
|
733
|
+
return ERRNO_BADF;
|
|
734
|
+
if (!fs) return ERRNO_NOSYS;
|
|
735
|
+
|
|
736
|
+
let entries: string[];
|
|
737
|
+
try {
|
|
738
|
+
entries = fs.readdirSync(entry.path);
|
|
739
|
+
} catch {
|
|
740
|
+
return ERRNO_IO;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const dv = view();
|
|
744
|
+
const mem = bytes();
|
|
745
|
+
let offset = buf_ptr;
|
|
746
|
+
const end = buf_ptr + buf_len;
|
|
747
|
+
let idx = 0;
|
|
748
|
+
const start = Number(cookie);
|
|
749
|
+
|
|
750
|
+
for (let i = start; i < entries.length; i++) {
|
|
751
|
+
const name = entries[i];
|
|
752
|
+
const nameBytes = encoder.encode(name);
|
|
753
|
+
|
|
754
|
+
// dirent: u64 d_next, u64 d_ino, u32 d_namlen, u8 d_type, then name
|
|
755
|
+
const direntSize = 24 + nameBytes.length;
|
|
756
|
+
if (offset + 24 > end) break; // not enough space for header
|
|
757
|
+
|
|
758
|
+
dv.setBigUint64(offset, BigInt(i + 1), true); // d_next
|
|
759
|
+
dv.setBigUint64(offset + 8, BigInt(i + 1), true); // d_ino (fake)
|
|
760
|
+
|
|
761
|
+
dv.setUint32(offset + 16, nameBytes.length, true); // d_namlen
|
|
762
|
+
|
|
763
|
+
// determine type
|
|
764
|
+
let dtype = FILETYPE_REGULAR_FILE;
|
|
765
|
+
try {
|
|
766
|
+
const st = fs.statSync(joinPath(entry.path, name));
|
|
767
|
+
if (st.isDirectory()) dtype = FILETYPE_DIRECTORY;
|
|
768
|
+
else if (st.isSymbolicLink()) dtype = FILETYPE_SYMBOLIC_LINK;
|
|
769
|
+
} catch {
|
|
770
|
+
/* default to regular */
|
|
771
|
+
}
|
|
772
|
+
dv.setUint8(offset + 20, dtype); // d_type
|
|
773
|
+
|
|
774
|
+
// write name (may be partial if buf too small)
|
|
775
|
+
const nameCopy = Math.min(nameBytes.length, end - (offset + 24));
|
|
776
|
+
if (nameCopy > 0)
|
|
777
|
+
mem.set(nameBytes.subarray(0, nameCopy), offset + 24);
|
|
778
|
+
|
|
779
|
+
offset += 24 + nameCopy;
|
|
780
|
+
idx++;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
dv.setUint32(bufused_out, offset - buf_ptr, true);
|
|
784
|
+
return ERRNO_SUCCESS;
|
|
785
|
+
},
|
|
786
|
+
),
|
|
787
|
+
|
|
788
|
+
fd_renumber: syscall((fd: number, to: number): number => {
|
|
789
|
+
const entry = fds.get(fd);
|
|
790
|
+
if (!entry) return ERRNO_BADF;
|
|
791
|
+
if (fds.has(to)) {
|
|
792
|
+
const toEntry = fds.get(to)!;
|
|
793
|
+
if (toEntry.kind === FdKind.File) flushFile(toEntry);
|
|
794
|
+
}
|
|
795
|
+
fds.set(to, entry);
|
|
796
|
+
fds.delete(fd);
|
|
797
|
+
return ERRNO_SUCCESS;
|
|
798
|
+
}),
|
|
799
|
+
|
|
800
|
+
fd_seek: syscall(
|
|
801
|
+
(
|
|
802
|
+
fd: number,
|
|
803
|
+
offset: bigint,
|
|
804
|
+
whence: number,
|
|
805
|
+
newoffset_out: number,
|
|
806
|
+
): number => {
|
|
807
|
+
const entry = fds.get(fd);
|
|
808
|
+
if (!entry) return ERRNO_BADF;
|
|
809
|
+
|
|
810
|
+
if (
|
|
811
|
+
entry.kind === FdKind.Stdin ||
|
|
812
|
+
entry.kind === FdKind.Stdout ||
|
|
813
|
+
entry.kind === FdKind.Stderr
|
|
814
|
+
) {
|
|
815
|
+
return ERRNO_SPIPE;
|
|
816
|
+
}
|
|
817
|
+
if (entry.kind === FdKind.PreopenDir) return ERRNO_BADF;
|
|
818
|
+
|
|
819
|
+
const dataLen = entry.data ? entry.data.length : 0;
|
|
820
|
+
let pos = entry.offset ?? 0;
|
|
821
|
+
const off = Number(offset);
|
|
822
|
+
|
|
823
|
+
switch (whence) {
|
|
824
|
+
case WHENCE_SET:
|
|
825
|
+
pos = off;
|
|
826
|
+
break;
|
|
827
|
+
case WHENCE_CUR:
|
|
828
|
+
pos += off;
|
|
829
|
+
break;
|
|
830
|
+
case WHENCE_END:
|
|
831
|
+
pos = dataLen + off;
|
|
832
|
+
break;
|
|
833
|
+
default:
|
|
834
|
+
return ERRNO_INVAL;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (pos < 0) pos = 0;
|
|
838
|
+
entry.offset = pos;
|
|
839
|
+
view().setBigUint64(newoffset_out, BigInt(pos), true);
|
|
840
|
+
return ERRNO_SUCCESS;
|
|
841
|
+
},
|
|
842
|
+
),
|
|
843
|
+
|
|
844
|
+
fd_sync: syscall((fd: number): number => {
|
|
845
|
+
const entry = fds.get(fd);
|
|
846
|
+
if (!entry) return ERRNO_BADF;
|
|
847
|
+
if (entry.kind === FdKind.File) flushFile(entry);
|
|
848
|
+
return ERRNO_SUCCESS;
|
|
849
|
+
}),
|
|
850
|
+
|
|
851
|
+
fd_tell: syscall((fd: number, offset_out: number): number => {
|
|
852
|
+
const entry = fds.get(fd);
|
|
853
|
+
if (!entry || entry.kind !== FdKind.File) return ERRNO_BADF;
|
|
854
|
+
view().setBigUint64(offset_out, BigInt(entry.offset ?? 0), true);
|
|
855
|
+
return ERRNO_SUCCESS;
|
|
856
|
+
}),
|
|
857
|
+
|
|
858
|
+
fd_write: syscall(
|
|
859
|
+
(
|
|
860
|
+
fd: number,
|
|
861
|
+
iovs_ptr: number,
|
|
862
|
+
iovs_len: number,
|
|
863
|
+
nwritten_out: number,
|
|
864
|
+
): number => {
|
|
865
|
+
const entry = fds.get(fd);
|
|
866
|
+
if (!entry) return ERRNO_BADF;
|
|
867
|
+
|
|
868
|
+
const dv = view();
|
|
869
|
+
|
|
870
|
+
// stdout / stderr
|
|
871
|
+
if (entry.kind === FdKind.Stdout || entry.kind === FdKind.Stderr) {
|
|
872
|
+
let totalWritten = 0;
|
|
873
|
+
for (let i = 0; i < iovs_len; i++) {
|
|
874
|
+
const bufPtr = dv.getUint32(iovs_ptr + i * 8, true);
|
|
875
|
+
const bufLen = dv.getUint32(iovs_ptr + i * 8 + 4, true);
|
|
876
|
+
const data = new Uint8Array(getMemory().buffer, bufPtr, bufLen);
|
|
877
|
+
const text = decoder.decode(data);
|
|
878
|
+
if (entry.kind === FdKind.Stdout) {
|
|
879
|
+
stdoutBuf += text;
|
|
880
|
+
stdoutBuf = flushLine(1, stdoutBuf);
|
|
881
|
+
} else {
|
|
882
|
+
stderrBuf += text;
|
|
883
|
+
stderrBuf = flushLine(2, stderrBuf);
|
|
884
|
+
}
|
|
885
|
+
totalWritten += bufLen;
|
|
886
|
+
}
|
|
887
|
+
dv.setUint32(nwritten_out, totalWritten, true);
|
|
888
|
+
return ERRNO_SUCCESS;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (entry.kind !== FdKind.File) return ERRNO_BADF;
|
|
892
|
+
|
|
893
|
+
let totalWritten = 0;
|
|
894
|
+
let pos =
|
|
895
|
+
entry.flags && entry.flags & FDFLAGS_APPEND
|
|
896
|
+
? entry.data
|
|
897
|
+
? entry.data.length
|
|
898
|
+
: 0
|
|
899
|
+
: (entry.offset ?? 0);
|
|
900
|
+
|
|
901
|
+
for (let i = 0; i < iovs_len; i++) {
|
|
902
|
+
const bufPtr = dv.getUint32(iovs_ptr + i * 8, true);
|
|
903
|
+
const bufLen = dv.getUint32(iovs_ptr + i * 8 + 4, true);
|
|
904
|
+
const chunk = new Uint8Array(getMemory().buffer, bufPtr, bufLen);
|
|
905
|
+
|
|
906
|
+
const needed = pos + bufLen;
|
|
907
|
+
if (!entry.data || entry.data.length < needed) {
|
|
908
|
+
const newData = new Uint8Array(needed);
|
|
909
|
+
if (entry.data) newData.set(entry.data);
|
|
910
|
+
entry.data = newData;
|
|
911
|
+
}
|
|
912
|
+
entry.data.set(chunk, pos);
|
|
913
|
+
pos += bufLen;
|
|
914
|
+
totalWritten += bufLen;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
entry.offset = pos;
|
|
918
|
+
entry.dirty = true;
|
|
919
|
+
dv.setUint32(nwritten_out, totalWritten, true);
|
|
920
|
+
return ERRNO_SUCCESS;
|
|
921
|
+
},
|
|
922
|
+
),
|
|
923
|
+
|
|
924
|
+
/* path operations */
|
|
925
|
+
|
|
926
|
+
path_create_directory: syscall(
|
|
927
|
+
(fd: number, path_ptr: number, path_len: number): number => {
|
|
928
|
+
if (!fs) return ERRNO_NOSYS;
|
|
929
|
+
const entry = fds.get(fd);
|
|
930
|
+
if (!entry) return ERRNO_BADF;
|
|
931
|
+
const rel = readString(path_ptr, path_len);
|
|
932
|
+
const fullPath = joinPath(entry.path, rel);
|
|
933
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
934
|
+
return ERRNO_SUCCESS;
|
|
935
|
+
},
|
|
936
|
+
),
|
|
937
|
+
|
|
938
|
+
path_filestat_get: syscall(
|
|
939
|
+
(
|
|
940
|
+
fd: number,
|
|
941
|
+
_flags: number,
|
|
942
|
+
path_ptr: number,
|
|
943
|
+
path_len: number,
|
|
944
|
+
buf_out: number,
|
|
945
|
+
): number => {
|
|
946
|
+
if (!fs) return ERRNO_NOSYS;
|
|
947
|
+
const entry = fds.get(fd);
|
|
948
|
+
if (!entry) return ERRNO_BADF;
|
|
949
|
+
const rel = readString(path_ptr, path_len);
|
|
950
|
+
const fullPath = joinPath(entry.path, rel);
|
|
951
|
+
|
|
952
|
+
const stat = fs.statSync(fullPath);
|
|
953
|
+
const dv = view();
|
|
954
|
+
|
|
955
|
+
let filetype = FILETYPE_REGULAR_FILE;
|
|
956
|
+
if (stat.isDirectory()) filetype = FILETYPE_DIRECTORY;
|
|
957
|
+
else if (stat.isSymbolicLink()) filetype = FILETYPE_SYMBOLIC_LINK;
|
|
958
|
+
|
|
959
|
+
const mtimeNs = BigInt(Math.floor(stat.mtimeMs)) * BigInt(1e6);
|
|
960
|
+
const atimeNs = BigInt(Math.floor(stat.atimeMs)) * BigInt(1e6);
|
|
961
|
+
const ctimeNs = BigInt(Math.floor(stat.ctimeMs)) * BigInt(1e6);
|
|
962
|
+
|
|
963
|
+
dv.setBigUint64(buf_out, 0n, true); // dev
|
|
964
|
+
dv.setBigUint64(buf_out + 8, BigInt(stat.ino ?? 0), true); // ino
|
|
965
|
+
dv.setUint8(buf_out + 16, filetype); // filetype
|
|
966
|
+
dv.setBigUint64(buf_out + 24, BigInt(stat.nlink ?? 1), true);
|
|
967
|
+
dv.setBigUint64(buf_out + 32, BigInt(stat.size), true);
|
|
968
|
+
dv.setBigUint64(buf_out + 40, atimeNs, true);
|
|
969
|
+
dv.setBigUint64(buf_out + 48, mtimeNs, true);
|
|
970
|
+
dv.setBigUint64(buf_out + 56, ctimeNs, true);
|
|
971
|
+
return ERRNO_SUCCESS;
|
|
972
|
+
},
|
|
973
|
+
),
|
|
974
|
+
|
|
975
|
+
path_filestat_set_times: syscall(
|
|
976
|
+
(
|
|
977
|
+
_fd: number,
|
|
978
|
+
_flags: number,
|
|
979
|
+
_path_ptr: number,
|
|
980
|
+
_path_len: number,
|
|
981
|
+
_atim: bigint,
|
|
982
|
+
_mtim: bigint,
|
|
983
|
+
_fst_flags: number,
|
|
984
|
+
): number => {
|
|
985
|
+
return ERRNO_SUCCESS; // no-op
|
|
986
|
+
},
|
|
987
|
+
),
|
|
988
|
+
|
|
989
|
+
path_link: syscall(
|
|
990
|
+
(
|
|
991
|
+
_old_fd: number,
|
|
992
|
+
_old_flags: number,
|
|
993
|
+
_old_path_ptr: number,
|
|
994
|
+
_old_path_len: number,
|
|
995
|
+
_new_fd: number,
|
|
996
|
+
_new_path_ptr: number,
|
|
997
|
+
_new_path_len: number,
|
|
998
|
+
): number => {
|
|
999
|
+
return ERRNO_NOSYS;
|
|
1000
|
+
},
|
|
1001
|
+
),
|
|
1002
|
+
|
|
1003
|
+
path_open: syscall(
|
|
1004
|
+
(
|
|
1005
|
+
fd: number,
|
|
1006
|
+
_dirflags: number,
|
|
1007
|
+
path_ptr: number,
|
|
1008
|
+
path_len: number,
|
|
1009
|
+
oflags: number,
|
|
1010
|
+
_fs_rights_base: bigint,
|
|
1011
|
+
_fs_rights_inheriting: bigint,
|
|
1012
|
+
fdflags: number,
|
|
1013
|
+
opened_fd_out: number,
|
|
1014
|
+
): number => {
|
|
1015
|
+
if (!fs) return ERRNO_NOSYS;
|
|
1016
|
+
const dirEntry = fds.get(fd);
|
|
1017
|
+
if (!dirEntry) return ERRNO_BADF;
|
|
1018
|
+
|
|
1019
|
+
const rel = readString(path_ptr, path_len);
|
|
1020
|
+
const fullPath = joinPath(dirEntry.path, rel);
|
|
1021
|
+
|
|
1022
|
+
const wantDir = (oflags & OFLAGS_DIRECTORY) !== 0;
|
|
1023
|
+
const wantCreate = (oflags & OFLAGS_CREAT) !== 0;
|
|
1024
|
+
const wantExcl = (oflags & OFLAGS_EXCL) !== 0;
|
|
1025
|
+
const wantTrunc = (oflags & OFLAGS_TRUNC) !== 0;
|
|
1026
|
+
|
|
1027
|
+
let exists = fs.existsSync(fullPath);
|
|
1028
|
+
|
|
1029
|
+
if (wantExcl && exists) return ERRNO_EXIST;
|
|
1030
|
+
|
|
1031
|
+
if (wantDir) {
|
|
1032
|
+
if (!exists) {
|
|
1033
|
+
if (wantCreate) {
|
|
1034
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
1035
|
+
} else {
|
|
1036
|
+
return ERRNO_NOENT;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
// open directory fd
|
|
1040
|
+
const newFd = nextFd++;
|
|
1041
|
+
fds.set(newFd, {
|
|
1042
|
+
kind: FdKind.PreopenDir,
|
|
1043
|
+
path: fullPath,
|
|
1044
|
+
rights: RIGHTS_DIR_BASE,
|
|
1045
|
+
});
|
|
1046
|
+
view().setUint32(opened_fd_out, newFd, true);
|
|
1047
|
+
return ERRNO_SUCCESS;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// regular file
|
|
1051
|
+
let data: Uint8Array;
|
|
1052
|
+
if (exists && !wantTrunc) {
|
|
1053
|
+
data = fs.readFileSync(fullPath);
|
|
1054
|
+
// make a copy so mutations dont affect the volume until flush
|
|
1055
|
+
const copy = new Uint8Array(data.length);
|
|
1056
|
+
copy.set(data);
|
|
1057
|
+
data = copy;
|
|
1058
|
+
} else if (wantCreate || wantTrunc) {
|
|
1059
|
+
if (!exists) {
|
|
1060
|
+
fs.writeFileSync(fullPath, new Uint8Array(0));
|
|
1061
|
+
}
|
|
1062
|
+
data = new Uint8Array(0);
|
|
1063
|
+
} else {
|
|
1064
|
+
if (!exists) return ERRNO_NOENT;
|
|
1065
|
+
data = fs.readFileSync(fullPath);
|
|
1066
|
+
const copy = new Uint8Array(data.length);
|
|
1067
|
+
copy.set(data);
|
|
1068
|
+
data = copy;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const newFd = nextFd++;
|
|
1072
|
+
fds.set(newFd, {
|
|
1073
|
+
kind: FdKind.File,
|
|
1074
|
+
path: fullPath,
|
|
1075
|
+
rights: RIGHTS_FILE_BASE,
|
|
1076
|
+
data,
|
|
1077
|
+
offset: 0,
|
|
1078
|
+
dirty: false,
|
|
1079
|
+
flags: fdflags,
|
|
1080
|
+
});
|
|
1081
|
+
view().setUint32(opened_fd_out, newFd, true);
|
|
1082
|
+
return ERRNO_SUCCESS;
|
|
1083
|
+
},
|
|
1084
|
+
),
|
|
1085
|
+
|
|
1086
|
+
path_readlink: syscall(
|
|
1087
|
+
(
|
|
1088
|
+
fd: number,
|
|
1089
|
+
path_ptr: number,
|
|
1090
|
+
path_len: number,
|
|
1091
|
+
buf_ptr: number,
|
|
1092
|
+
buf_len: number,
|
|
1093
|
+
bufused_out: number,
|
|
1094
|
+
): number => {
|
|
1095
|
+
if (!fs || !fs.readlinkSync) return ERRNO_NOSYS;
|
|
1096
|
+
const entry = fds.get(fd);
|
|
1097
|
+
if (!entry) return ERRNO_BADF;
|
|
1098
|
+
const rel = readString(path_ptr, path_len);
|
|
1099
|
+
const fullPath = joinPath(entry.path, rel);
|
|
1100
|
+
const target = fs.readlinkSync(fullPath);
|
|
1101
|
+
const encoded = encoder.encode(target);
|
|
1102
|
+
const toCopy = Math.min(encoded.length, buf_len);
|
|
1103
|
+
bytes().set(encoded.subarray(0, toCopy), buf_ptr);
|
|
1104
|
+
view().setUint32(bufused_out, toCopy, true);
|
|
1105
|
+
return ERRNO_SUCCESS;
|
|
1106
|
+
},
|
|
1107
|
+
),
|
|
1108
|
+
|
|
1109
|
+
path_remove_directory: syscall(
|
|
1110
|
+
(fd: number, path_ptr: number, path_len: number): number => {
|
|
1111
|
+
if (!fs) return ERRNO_NOSYS;
|
|
1112
|
+
const entry = fds.get(fd);
|
|
1113
|
+
if (!entry) return ERRNO_BADF;
|
|
1114
|
+
const rel = readString(path_ptr, path_len);
|
|
1115
|
+
const fullPath = joinPath(entry.path, rel);
|
|
1116
|
+
fs.rmdirSync(fullPath);
|
|
1117
|
+
return ERRNO_SUCCESS;
|
|
1118
|
+
},
|
|
1119
|
+
),
|
|
1120
|
+
|
|
1121
|
+
path_rename: syscall(
|
|
1122
|
+
(
|
|
1123
|
+
fd: number,
|
|
1124
|
+
old_path_ptr: number,
|
|
1125
|
+
old_path_len: number,
|
|
1126
|
+
new_fd: number,
|
|
1127
|
+
new_path_ptr: number,
|
|
1128
|
+
new_path_len: number,
|
|
1129
|
+
): number => {
|
|
1130
|
+
if (!fs) return ERRNO_NOSYS;
|
|
1131
|
+
const oldEntry = fds.get(fd);
|
|
1132
|
+
const newEntry = fds.get(new_fd);
|
|
1133
|
+
if (!oldEntry || !newEntry) return ERRNO_BADF;
|
|
1134
|
+
const oldRel = readString(old_path_ptr, old_path_len);
|
|
1135
|
+
const newRel = readString(new_path_ptr, new_path_len);
|
|
1136
|
+
const oldPath = joinPath(oldEntry.path, oldRel);
|
|
1137
|
+
const newPath = joinPath(newEntry.path, newRel);
|
|
1138
|
+
fs.renameSync(oldPath, newPath);
|
|
1139
|
+
return ERRNO_SUCCESS;
|
|
1140
|
+
},
|
|
1141
|
+
),
|
|
1142
|
+
|
|
1143
|
+
path_symlink: syscall(
|
|
1144
|
+
(
|
|
1145
|
+
old_path_ptr: number,
|
|
1146
|
+
old_path_len: number,
|
|
1147
|
+
fd: number,
|
|
1148
|
+
new_path_ptr: number,
|
|
1149
|
+
new_path_len: number,
|
|
1150
|
+
): number => {
|
|
1151
|
+
if (!fs || !fs.symlinkSync) return ERRNO_NOSYS;
|
|
1152
|
+
const entry = fds.get(fd);
|
|
1153
|
+
if (!entry) return ERRNO_BADF;
|
|
1154
|
+
const target = readString(old_path_ptr, old_path_len);
|
|
1155
|
+
const linkRel = readString(new_path_ptr, new_path_len);
|
|
1156
|
+
const linkPath = joinPath(entry.path, linkRel);
|
|
1157
|
+
fs.symlinkSync(target, linkPath);
|
|
1158
|
+
return ERRNO_SUCCESS;
|
|
1159
|
+
},
|
|
1160
|
+
),
|
|
1161
|
+
|
|
1162
|
+
path_unlink_file: syscall(
|
|
1163
|
+
(fd: number, path_ptr: number, path_len: number): number => {
|
|
1164
|
+
if (!fs) return ERRNO_NOSYS;
|
|
1165
|
+
const entry = fds.get(fd);
|
|
1166
|
+
if (!entry) return ERRNO_BADF;
|
|
1167
|
+
const rel = readString(path_ptr, path_len);
|
|
1168
|
+
const fullPath = joinPath(entry.path, rel);
|
|
1169
|
+
fs.unlinkSync(fullPath);
|
|
1170
|
+
return ERRNO_SUCCESS;
|
|
1171
|
+
},
|
|
1172
|
+
),
|
|
1173
|
+
|
|
1174
|
+
/* ---- misc ---------------------------------------------------- */
|
|
1175
|
+
|
|
1176
|
+
poll_oneoff: syscall(
|
|
1177
|
+
(
|
|
1178
|
+
in_ptr: number,
|
|
1179
|
+
out_ptr: number,
|
|
1180
|
+
nsubscriptions: number,
|
|
1181
|
+
nevents_out: number,
|
|
1182
|
+
): number => {
|
|
1183
|
+
// uhm this is a very minimal implementation: handle clock subscriptions as immediate,
|
|
1184
|
+
// report all fd subscriptions as ready
|
|
1185
|
+
const dv = view();
|
|
1186
|
+
let nevents = 0;
|
|
1187
|
+
|
|
1188
|
+
for (let i = 0; i < nsubscriptions; i++) {
|
|
1189
|
+
const subPtr = in_ptr + i * 48;
|
|
1190
|
+
const eventPtr = out_ptr + nevents * 32;
|
|
1191
|
+
|
|
1192
|
+
const userdata = dv.getBigUint64(subPtr, true);
|
|
1193
|
+
const eventType = dv.getUint8(subPtr + 8);
|
|
1194
|
+
|
|
1195
|
+
dv.setBigUint64(eventPtr, userdata, true); // userdata
|
|
1196
|
+
dv.setUint16(eventPtr + 8, ERRNO_SUCCESS, true); // error
|
|
1197
|
+
dv.setUint8(eventPtr + 10, eventType); // type
|
|
1198
|
+
|
|
1199
|
+
if (
|
|
1200
|
+
eventType === EVENTTYPE_FD_READ ||
|
|
1201
|
+
eventType === EVENTTYPE_FD_WRITE
|
|
1202
|
+
) {
|
|
1203
|
+
// report some available bytes
|
|
1204
|
+
dv.setBigUint64(eventPtr + 16, BigInt(1), true); // nbytes
|
|
1205
|
+
dv.setUint16(eventPtr + 24, 0, true); // flags
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
nevents++;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
dv.setUint32(nevents_out, nevents, true);
|
|
1212
|
+
return ERRNO_SUCCESS;
|
|
1213
|
+
},
|
|
1214
|
+
),
|
|
1215
|
+
|
|
1216
|
+
proc_exit: syscall((rval: number): never => {
|
|
1217
|
+
// flush remaining output
|
|
1218
|
+
if (stdoutBuf) {
|
|
1219
|
+
console.log(stdoutBuf);
|
|
1220
|
+
stdoutBuf = "";
|
|
1221
|
+
}
|
|
1222
|
+
if (stderrBuf) {
|
|
1223
|
+
console.error(stderrBuf);
|
|
1224
|
+
stderrBuf = "";
|
|
1225
|
+
}
|
|
1226
|
+
throw new ExitStatus(rval);
|
|
1227
|
+
}),
|
|
1228
|
+
|
|
1229
|
+
proc_raise: syscall((_sig: number): number => {
|
|
1230
|
+
return ERRNO_NOSYS;
|
|
1231
|
+
}),
|
|
1232
|
+
|
|
1233
|
+
sched_yield: syscall((): number => {
|
|
1234
|
+
return ERRNO_SUCCESS;
|
|
1235
|
+
}),
|
|
1236
|
+
|
|
1237
|
+
random_get: syscall((buf_ptr: number, buf_len: number): number => {
|
|
1238
|
+
const slice = new Uint8Array(getMemory().buffer, buf_ptr, buf_len);
|
|
1239
|
+
crypto.getRandomValues(slice);
|
|
1240
|
+
return ERRNO_SUCCESS;
|
|
1241
|
+
}),
|
|
1242
|
+
|
|
1243
|
+
sock_recv: syscall((): number => ERRNO_NOSYS),
|
|
1244
|
+
sock_send: syscall((): number => ERRNO_NOSYS),
|
|
1245
|
+
sock_shutdown: syscall((): number => ERRNO_NOSYS),
|
|
1246
|
+
sock_accept: syscall((): number => ERRNO_NOSYS),
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
/* public api */
|
|
1250
|
+
|
|
1251
|
+
const self: any = this;
|
|
1252
|
+
self.wasiImport = wasiImport;
|
|
1253
|
+
|
|
1254
|
+
self.start = function start(wasmInstance: any): number {
|
|
1255
|
+
instance = wasmInstance;
|
|
1256
|
+
memory = wasmInstance.exports.memory as WebAssembly.Memory;
|
|
1257
|
+
|
|
1258
|
+
const _start = wasmInstance.exports._start;
|
|
1259
|
+
if (typeof _start !== "function") {
|
|
1260
|
+
throw new Error("WASI: instance has no _start export");
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
try {
|
|
1264
|
+
_start();
|
|
1265
|
+
} catch (err) {
|
|
1266
|
+
if (err instanceof ExitStatus) {
|
|
1267
|
+
if (returnOnExit) return err.code;
|
|
1268
|
+
throw err;
|
|
1269
|
+
}
|
|
1270
|
+
throw err;
|
|
1271
|
+
} finally {
|
|
1272
|
+
// flush remaining output
|
|
1273
|
+
if (stdoutBuf) {
|
|
1274
|
+
console.log(stdoutBuf);
|
|
1275
|
+
stdoutBuf = "";
|
|
1276
|
+
}
|
|
1277
|
+
if (stderrBuf) {
|
|
1278
|
+
console.error(stderrBuf);
|
|
1279
|
+
stderrBuf = "";
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return 0;
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
self.initialize = function initialize(wasmInstance: any): void {
|
|
1286
|
+
instance = wasmInstance;
|
|
1287
|
+
memory = wasmInstance.exports.memory as WebAssembly.Memory;
|
|
1288
|
+
|
|
1289
|
+
const _initialize = wasmInstance.exports._initialize;
|
|
1290
|
+
if (typeof _initialize === "function") {
|
|
1291
|
+
_initialize();
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
self.getImportObject = function getImportObject(): Record<
|
|
1296
|
+
string,
|
|
1297
|
+
Record<string, Function>
|
|
1298
|
+
> {
|
|
1299
|
+
return { wasi_snapshot_preview1: wasiImport };
|
|
1300
|
+
};
|
|
1301
|
+
} as unknown as WASIConstructor;
|
|
41
1302
|
|
|
1303
|
+
/* matches Node.js `require('wasi')` */
|
|
42
1304
|
export default {
|
|
43
1305
|
WASI,
|
|
44
1306
|
};
|