@oh-my-pi/pi-natives 9.4.0 → 9.6.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/README.md +10 -13
- package/native/pi_natives.darwin-arm64.node +0 -0
- package/native/pi_natives.darwin-x64.node +0 -0
- package/native/pi_natives.linux-arm64.node +0 -0
- package/native/pi_natives.linux-x64.node +0 -0
- package/native/pi_natives.win32-x64.node +0 -0
- package/package.json +6 -6
- package/src/find/types.ts +31 -0
- package/src/grep/index.ts +37 -295
- package/src/grep/types.ts +59 -19
- package/src/highlight/index.ts +5 -13
- package/src/html/index.ts +5 -35
- package/src/html/types.ts +1 -16
- package/src/image/index.ts +52 -73
- package/src/index.ts +20 -93
- package/src/native.ts +159 -0
- package/src/request-options.ts +94 -0
- package/src/text/index.ts +16 -24
- package/src/grep/file-reader.ts +0 -51
- package/src/grep/filters.ts +0 -77
- package/src/grep/worker.ts +0 -212
- package/src/html/worker.ts +0 -40
- package/src/image/types.ts +0 -52
- package/src/image/worker.ts +0 -152
- package/src/pool.ts +0 -362
- package/src/wasix.ts +0 -1745
- package/src/worker-resolver.ts +0 -9
- package/wasm/pi_natives.d.ts +0 -148
- package/wasm/pi_natives.js +0 -891
- package/wasm/pi_natives_bg.wasm +0 -0
- package/wasm/pi_natives_bg.wasm.d.ts +0 -32
package/src/wasix.ts
DELETED
|
@@ -1,1745 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WASI Preview 1 Implementation
|
|
3
|
-
*
|
|
4
|
-
* A non-sandboxed, spec-accurate WASI implementation for TypeScript/Bun.
|
|
5
|
-
* Preopens "/" (Unix) or all drive letters (Windows) for full filesystem access.
|
|
6
|
-
*
|
|
7
|
-
* Reference: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import * as crypto from "node:crypto";
|
|
11
|
-
import * as fs from "node:fs";
|
|
12
|
-
import * as os from "node:os";
|
|
13
|
-
import * as path from "node:path";
|
|
14
|
-
import * as tty from "node:tty";
|
|
15
|
-
|
|
16
|
-
// =============================================================================
|
|
17
|
-
// WASI Error Codes (errno)
|
|
18
|
-
// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-errno-variant
|
|
19
|
-
// =============================================================================
|
|
20
|
-
|
|
21
|
-
export const enum Errno {
|
|
22
|
-
/** No error occurred. System call completed successfully. */
|
|
23
|
-
SUCCESS = 0,
|
|
24
|
-
/** Argument list too long. */
|
|
25
|
-
E2BIG = 1,
|
|
26
|
-
/** Permission denied. */
|
|
27
|
-
EACCES = 2,
|
|
28
|
-
/** Address in use. */
|
|
29
|
-
EADDRINUSE = 3,
|
|
30
|
-
/** Address not available. */
|
|
31
|
-
EADDRNOTAVAIL = 4,
|
|
32
|
-
/** Address family not supported. */
|
|
33
|
-
EAFNOSUPPORT = 5,
|
|
34
|
-
/** Resource unavailable, or operation would block. */
|
|
35
|
-
EAGAIN = 6,
|
|
36
|
-
/** Connection already in progress. */
|
|
37
|
-
EALREADY = 7,
|
|
38
|
-
/** Bad file descriptor. */
|
|
39
|
-
EBADF = 8,
|
|
40
|
-
/** Bad message. */
|
|
41
|
-
EBADMSG = 9,
|
|
42
|
-
/** Device or resource busy. */
|
|
43
|
-
EBUSY = 10,
|
|
44
|
-
/** Operation canceled. */
|
|
45
|
-
ECANCELED = 11,
|
|
46
|
-
/** No child processes. */
|
|
47
|
-
ECHILD = 12,
|
|
48
|
-
/** Connection aborted. */
|
|
49
|
-
ECONNABORTED = 13,
|
|
50
|
-
/** Connection refused. */
|
|
51
|
-
ECONNREFUSED = 14,
|
|
52
|
-
/** Connection reset. */
|
|
53
|
-
ECONNRESET = 15,
|
|
54
|
-
/** Resource deadlock would occur. */
|
|
55
|
-
EDEADLK = 16,
|
|
56
|
-
/** Destination address required. */
|
|
57
|
-
EDESTADDRREQ = 17,
|
|
58
|
-
/** Mathematics argument out of domain of function. */
|
|
59
|
-
EDOM = 18,
|
|
60
|
-
/** Reserved. */
|
|
61
|
-
EDQUOT = 19,
|
|
62
|
-
/** File exists. */
|
|
63
|
-
EEXIST = 20,
|
|
64
|
-
/** Bad address. */
|
|
65
|
-
EFAULT = 21,
|
|
66
|
-
/** File too large. */
|
|
67
|
-
EFBIG = 22,
|
|
68
|
-
/** Host is unreachable. */
|
|
69
|
-
EHOSTUNREACH = 23,
|
|
70
|
-
/** Identifier removed. */
|
|
71
|
-
EIDRM = 24,
|
|
72
|
-
/** Illegal byte sequence. */
|
|
73
|
-
EILSEQ = 25,
|
|
74
|
-
/** Operation in progress. */
|
|
75
|
-
EINPROGRESS = 26,
|
|
76
|
-
/** Interrupted function. */
|
|
77
|
-
EINTR = 27,
|
|
78
|
-
/** Invalid argument. */
|
|
79
|
-
EINVAL = 28,
|
|
80
|
-
/** I/O error. */
|
|
81
|
-
EIO = 29,
|
|
82
|
-
/** Socket is connected. */
|
|
83
|
-
EISCONN = 30,
|
|
84
|
-
/** Is a directory. */
|
|
85
|
-
EISDIR = 31,
|
|
86
|
-
/** Too many levels of symbolic links. */
|
|
87
|
-
ELOOP = 32,
|
|
88
|
-
/** File descriptor value too large. */
|
|
89
|
-
EMFILE = 33,
|
|
90
|
-
/** Too many links. */
|
|
91
|
-
EMLINK = 34,
|
|
92
|
-
/** Message too large. */
|
|
93
|
-
EMSGSIZE = 35,
|
|
94
|
-
/** Reserved. */
|
|
95
|
-
EMULTIHOP = 36,
|
|
96
|
-
/** Filename too long. */
|
|
97
|
-
ENAMETOOLONG = 37,
|
|
98
|
-
/** Network is down. */
|
|
99
|
-
ENETDOWN = 38,
|
|
100
|
-
/** Connection aborted by network. */
|
|
101
|
-
ENETRESET = 39,
|
|
102
|
-
/** Network unreachable. */
|
|
103
|
-
ENETUNREACH = 40,
|
|
104
|
-
/** Too many files open in system. */
|
|
105
|
-
ENFILE = 41,
|
|
106
|
-
/** No buffer space available. */
|
|
107
|
-
ENOBUFS = 42,
|
|
108
|
-
/** No such device. */
|
|
109
|
-
ENODEV = 43,
|
|
110
|
-
/** No such file or directory. */
|
|
111
|
-
ENOENT = 44,
|
|
112
|
-
/** Executable file format error. */
|
|
113
|
-
ENOEXEC = 45,
|
|
114
|
-
/** No locks available. */
|
|
115
|
-
ENOLCK = 46,
|
|
116
|
-
/** Reserved. */
|
|
117
|
-
ENOLINK = 47,
|
|
118
|
-
/** Not enough space. */
|
|
119
|
-
ENOMEM = 48,
|
|
120
|
-
/** No message of the desired type. */
|
|
121
|
-
ENOMSG = 49,
|
|
122
|
-
/** Protocol not available. */
|
|
123
|
-
ENOPROTOOPT = 50,
|
|
124
|
-
/** No space left on device. */
|
|
125
|
-
ENOSPC = 51,
|
|
126
|
-
/** Function not supported. */
|
|
127
|
-
ENOSYS = 52,
|
|
128
|
-
/** The socket is not connected. */
|
|
129
|
-
ENOTCONN = 53,
|
|
130
|
-
/** Not a directory or a symbolic link to a directory. */
|
|
131
|
-
ENOTDIR = 54,
|
|
132
|
-
/** Directory not empty. */
|
|
133
|
-
ENOTEMPTY = 55,
|
|
134
|
-
/** State not recoverable. */
|
|
135
|
-
ENOTRECOVERABLE = 56,
|
|
136
|
-
/** Not a socket. */
|
|
137
|
-
ENOTSOCK = 57,
|
|
138
|
-
/** Not supported, or operation not supported on socket. */
|
|
139
|
-
ENOTSUP = 58,
|
|
140
|
-
/** Inappropriate I/O control operation. */
|
|
141
|
-
ENOTTY = 59,
|
|
142
|
-
/** No such device or address. */
|
|
143
|
-
ENXIO = 60,
|
|
144
|
-
/** Value too large to be stored in data type. */
|
|
145
|
-
EOVERFLOW = 61,
|
|
146
|
-
/** Previous owner died. */
|
|
147
|
-
EOWNERDEAD = 62,
|
|
148
|
-
/** Operation not permitted. */
|
|
149
|
-
EPERM = 63,
|
|
150
|
-
/** Broken pipe. */
|
|
151
|
-
EPIPE = 64,
|
|
152
|
-
/** Protocol error. */
|
|
153
|
-
EPROTO = 65,
|
|
154
|
-
/** Protocol not supported. */
|
|
155
|
-
EPROTONOSUPPORT = 66,
|
|
156
|
-
/** Protocol wrong type for socket. */
|
|
157
|
-
EPROTOTYPE = 67,
|
|
158
|
-
/** Result too large. */
|
|
159
|
-
ERANGE = 68,
|
|
160
|
-
/** Read-only file system. */
|
|
161
|
-
EROFS = 69,
|
|
162
|
-
/** Invalid seek. */
|
|
163
|
-
ESPIPE = 70,
|
|
164
|
-
/** No such process. */
|
|
165
|
-
ESRCH = 71,
|
|
166
|
-
/** Reserved. */
|
|
167
|
-
ESTALE = 72,
|
|
168
|
-
/** Connection timed out. */
|
|
169
|
-
ETIMEDOUT = 73,
|
|
170
|
-
/** Text file busy. */
|
|
171
|
-
ETXTBSY = 74,
|
|
172
|
-
/** Cross-device link. */
|
|
173
|
-
EXDEV = 75,
|
|
174
|
-
/** Extension: Capabilities insufficient. */
|
|
175
|
-
ENOTCAPABLE = 76,
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/** Map Node.js error codes to WASI errno */
|
|
179
|
-
const NODE_ERROR_MAP: Record<string, Errno> = {
|
|
180
|
-
E2BIG: Errno.E2BIG,
|
|
181
|
-
EACCES: Errno.EACCES,
|
|
182
|
-
EADDRINUSE: Errno.EADDRINUSE,
|
|
183
|
-
EADDRNOTAVAIL: Errno.EADDRNOTAVAIL,
|
|
184
|
-
EAFNOSUPPORT: Errno.EAFNOSUPPORT,
|
|
185
|
-
EAGAIN: Errno.EAGAIN,
|
|
186
|
-
EALREADY: Errno.EALREADY,
|
|
187
|
-
EBADF: Errno.EBADF,
|
|
188
|
-
EBADMSG: Errno.EBADMSG,
|
|
189
|
-
EBUSY: Errno.EBUSY,
|
|
190
|
-
ECANCELED: Errno.ECANCELED,
|
|
191
|
-
ECHILD: Errno.ECHILD,
|
|
192
|
-
ECONNABORTED: Errno.ECONNABORTED,
|
|
193
|
-
ECONNREFUSED: Errno.ECONNREFUSED,
|
|
194
|
-
ECONNRESET: Errno.ECONNRESET,
|
|
195
|
-
EDEADLK: Errno.EDEADLK,
|
|
196
|
-
EDEADLOCK: Errno.EDEADLK,
|
|
197
|
-
EDESTADDRREQ: Errno.EDESTADDRREQ,
|
|
198
|
-
EDOM: Errno.EDOM,
|
|
199
|
-
EDQUOT: Errno.EDQUOT,
|
|
200
|
-
EEXIST: Errno.EEXIST,
|
|
201
|
-
EFAULT: Errno.EFAULT,
|
|
202
|
-
EFBIG: Errno.EFBIG,
|
|
203
|
-
EHOSTDOWN: Errno.EHOSTUNREACH,
|
|
204
|
-
EHOSTUNREACH: Errno.EHOSTUNREACH,
|
|
205
|
-
EIDRM: Errno.EIDRM,
|
|
206
|
-
EILSEQ: Errno.EILSEQ,
|
|
207
|
-
EINPROGRESS: Errno.EINPROGRESS,
|
|
208
|
-
EINTR: Errno.EINTR,
|
|
209
|
-
EINVAL: Errno.EINVAL,
|
|
210
|
-
EIO: Errno.EIO,
|
|
211
|
-
EISCONN: Errno.EISCONN,
|
|
212
|
-
EISDIR: Errno.EISDIR,
|
|
213
|
-
ELOOP: Errno.ELOOP,
|
|
214
|
-
EMFILE: Errno.EMFILE,
|
|
215
|
-
EMLINK: Errno.EMLINK,
|
|
216
|
-
EMSGSIZE: Errno.EMSGSIZE,
|
|
217
|
-
EMULTIHOP: Errno.EMULTIHOP,
|
|
218
|
-
ENAMETOOLONG: Errno.ENAMETOOLONG,
|
|
219
|
-
ENETDOWN: Errno.ENETDOWN,
|
|
220
|
-
ENETRESET: Errno.ENETRESET,
|
|
221
|
-
ENETUNREACH: Errno.ENETUNREACH,
|
|
222
|
-
ENFILE: Errno.ENFILE,
|
|
223
|
-
ENOBUFS: Errno.ENOBUFS,
|
|
224
|
-
ENODEV: Errno.ENODEV,
|
|
225
|
-
ENOENT: Errno.ENOENT,
|
|
226
|
-
ENOEXEC: Errno.ENOEXEC,
|
|
227
|
-
ENOLCK: Errno.ENOLCK,
|
|
228
|
-
ENOLINK: Errno.ENOLINK,
|
|
229
|
-
ENOMEM: Errno.ENOMEM,
|
|
230
|
-
ENOMSG: Errno.ENOMSG,
|
|
231
|
-
ENOPROTOOPT: Errno.ENOPROTOOPT,
|
|
232
|
-
ENOSPC: Errno.ENOSPC,
|
|
233
|
-
ENOSYS: Errno.ENOSYS,
|
|
234
|
-
ENOTCONN: Errno.ENOTCONN,
|
|
235
|
-
ENOTDIR: Errno.ENOTDIR,
|
|
236
|
-
ENOTEMPTY: Errno.ENOTEMPTY,
|
|
237
|
-
ENOTRECOVERABLE: Errno.ENOTRECOVERABLE,
|
|
238
|
-
ENOTSOCK: Errno.ENOTSOCK,
|
|
239
|
-
ENOTSUP: Errno.ENOTSUP,
|
|
240
|
-
ENOTTY: Errno.ENOTTY,
|
|
241
|
-
ENXIO: Errno.ENXIO,
|
|
242
|
-
EOPNOTSUPP: Errno.ENOTSUP,
|
|
243
|
-
EOVERFLOW: Errno.EOVERFLOW,
|
|
244
|
-
EOWNERDEAD: Errno.EOWNERDEAD,
|
|
245
|
-
EPERM: Errno.EPERM,
|
|
246
|
-
EPIPE: Errno.EPIPE,
|
|
247
|
-
EPROTO: Errno.EPROTO,
|
|
248
|
-
EPROTONOSUPPORT: Errno.EPROTONOSUPPORT,
|
|
249
|
-
EPROTOTYPE: Errno.EPROTOTYPE,
|
|
250
|
-
ERANGE: Errno.ERANGE,
|
|
251
|
-
EROFS: Errno.EROFS,
|
|
252
|
-
ESPIPE: Errno.ESPIPE,
|
|
253
|
-
ESRCH: Errno.ESRCH,
|
|
254
|
-
ESTALE: Errno.ESTALE,
|
|
255
|
-
ETIMEDOUT: Errno.ETIMEDOUT,
|
|
256
|
-
ETXTBSY: Errno.ETXTBSY,
|
|
257
|
-
EWOULDBLOCK: Errno.EAGAIN,
|
|
258
|
-
EXDEV: Errno.EXDEV,
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const INVERSE_ERROR_MAP: string[] = [];
|
|
262
|
-
for (const [key, value] of Object.entries(NODE_ERROR_MAP)) {
|
|
263
|
-
INVERSE_ERROR_MAP[value as number] = key;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function getErrnoName(errno: Errno): string {
|
|
267
|
-
if (errno < 0 || errno > INVERSE_ERROR_MAP.length) {
|
|
268
|
-
return "UNKNOWN";
|
|
269
|
-
}
|
|
270
|
-
return INVERSE_ERROR_MAP[errno];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// =============================================================================
|
|
274
|
-
// WASI Clock IDs
|
|
275
|
-
// =============================================================================
|
|
276
|
-
|
|
277
|
-
export const enum ClockId {
|
|
278
|
-
/** Wall clock time. */
|
|
279
|
-
REALTIME = 0,
|
|
280
|
-
/** Monotonic clock for measuring elapsed time. */
|
|
281
|
-
MONOTONIC = 1,
|
|
282
|
-
/** CPU-time clock for the current process. */
|
|
283
|
-
PROCESS_CPUTIME_ID = 2,
|
|
284
|
-
/** CPU-time clock for the current thread. */
|
|
285
|
-
THREAD_CPUTIME_ID = 3,
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// =============================================================================
|
|
289
|
-
// WASI File Types
|
|
290
|
-
// =============================================================================
|
|
291
|
-
|
|
292
|
-
export const enum FileType {
|
|
293
|
-
/** The type of the file descriptor is unknown. */
|
|
294
|
-
UNKNOWN = 0,
|
|
295
|
-
/** The file descriptor refers to a block device. */
|
|
296
|
-
BLOCK_DEVICE = 1,
|
|
297
|
-
/** The file descriptor refers to a character device. */
|
|
298
|
-
CHARACTER_DEVICE = 2,
|
|
299
|
-
/** The file descriptor refers to a directory. */
|
|
300
|
-
DIRECTORY = 3,
|
|
301
|
-
/** The file descriptor refers to a regular file. */
|
|
302
|
-
REGULAR_FILE = 4,
|
|
303
|
-
/** The file descriptor refers to a datagram socket. */
|
|
304
|
-
SOCKET_DGRAM = 5,
|
|
305
|
-
/** The file descriptor refers to a stream socket. */
|
|
306
|
-
SOCKET_STREAM = 6,
|
|
307
|
-
/** The file descriptor refers to a symbolic link. */
|
|
308
|
-
SYMBOLIC_LINK = 7,
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// =============================================================================
|
|
312
|
-
// WASI FD Flags
|
|
313
|
-
// =============================================================================
|
|
314
|
-
|
|
315
|
-
export const enum FdFlags {
|
|
316
|
-
/** Append mode: Data written to the file is always appended. */
|
|
317
|
-
APPEND = 1 << 0,
|
|
318
|
-
/** Write according to synchronized I/O data integrity completion. */
|
|
319
|
-
DSYNC = 1 << 1,
|
|
320
|
-
/** Non-blocking mode. */
|
|
321
|
-
NONBLOCK = 1 << 2,
|
|
322
|
-
/** Synchronized read I/O operations. */
|
|
323
|
-
RSYNC = 1 << 3,
|
|
324
|
-
/** Write according to synchronized I/O file integrity completion. */
|
|
325
|
-
SYNC = 1 << 4,
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// =============================================================================
|
|
329
|
-
// WASI Rights
|
|
330
|
-
// =============================================================================
|
|
331
|
-
|
|
332
|
-
export const Rights = {
|
|
333
|
-
FD_DATASYNC: 1n << 0n,
|
|
334
|
-
FD_READ: 1n << 1n,
|
|
335
|
-
FD_SEEK: 1n << 2n,
|
|
336
|
-
FD_FDSTAT_SET_FLAGS: 1n << 3n,
|
|
337
|
-
FD_SYNC: 1n << 4n,
|
|
338
|
-
FD_TELL: 1n << 5n,
|
|
339
|
-
FD_WRITE: 1n << 6n,
|
|
340
|
-
FD_ADVISE: 1n << 7n,
|
|
341
|
-
FD_ALLOCATE: 1n << 8n,
|
|
342
|
-
PATH_CREATE_DIRECTORY: 1n << 9n,
|
|
343
|
-
PATH_CREATE_FILE: 1n << 10n,
|
|
344
|
-
PATH_LINK_SOURCE: 1n << 11n,
|
|
345
|
-
PATH_LINK_TARGET: 1n << 12n,
|
|
346
|
-
PATH_OPEN: 1n << 13n,
|
|
347
|
-
FD_READDIR: 1n << 14n,
|
|
348
|
-
PATH_READLINK: 1n << 15n,
|
|
349
|
-
PATH_RENAME_SOURCE: 1n << 16n,
|
|
350
|
-
PATH_RENAME_TARGET: 1n << 17n,
|
|
351
|
-
PATH_FILESTAT_GET: 1n << 18n,
|
|
352
|
-
PATH_FILESTAT_SET_SIZE: 1n << 19n,
|
|
353
|
-
PATH_FILESTAT_SET_TIMES: 1n << 20n,
|
|
354
|
-
FD_FILESTAT_GET: 1n << 21n,
|
|
355
|
-
FD_FILESTAT_SET_SIZE: 1n << 22n,
|
|
356
|
-
FD_FILESTAT_SET_TIMES: 1n << 23n,
|
|
357
|
-
PATH_SYMLINK: 1n << 24n,
|
|
358
|
-
PATH_REMOVE_DIRECTORY: 1n << 25n,
|
|
359
|
-
PATH_UNLINK_FILE: 1n << 26n,
|
|
360
|
-
POLL_FD_READWRITE: 1n << 27n,
|
|
361
|
-
SOCK_SHUTDOWN: 1n << 28n,
|
|
362
|
-
SOCK_ACCEPT: 1n << 29n,
|
|
363
|
-
} as const;
|
|
364
|
-
|
|
365
|
-
/** All rights combined */
|
|
366
|
-
const RIGHTS_ALL = Object.values(Rights).reduce((a, b) => a | b, 0n);
|
|
367
|
-
|
|
368
|
-
/** Rights for regular files */
|
|
369
|
-
const RIGHTS_FILE_BASE =
|
|
370
|
-
Rights.FD_DATASYNC |
|
|
371
|
-
Rights.FD_READ |
|
|
372
|
-
Rights.FD_SEEK |
|
|
373
|
-
Rights.FD_FDSTAT_SET_FLAGS |
|
|
374
|
-
Rights.FD_SYNC |
|
|
375
|
-
Rights.FD_TELL |
|
|
376
|
-
Rights.FD_WRITE |
|
|
377
|
-
Rights.FD_ADVISE |
|
|
378
|
-
Rights.FD_ALLOCATE |
|
|
379
|
-
Rights.FD_FILESTAT_GET |
|
|
380
|
-
Rights.FD_FILESTAT_SET_SIZE |
|
|
381
|
-
Rights.FD_FILESTAT_SET_TIMES |
|
|
382
|
-
Rights.POLL_FD_READWRITE;
|
|
383
|
-
|
|
384
|
-
/** Rights for directories */
|
|
385
|
-
const RIGHTS_DIRECTORY_BASE =
|
|
386
|
-
Rights.FD_FDSTAT_SET_FLAGS |
|
|
387
|
-
Rights.FD_SYNC |
|
|
388
|
-
Rights.FD_ADVISE |
|
|
389
|
-
Rights.PATH_CREATE_DIRECTORY |
|
|
390
|
-
Rights.PATH_CREATE_FILE |
|
|
391
|
-
Rights.PATH_LINK_SOURCE |
|
|
392
|
-
Rights.PATH_LINK_TARGET |
|
|
393
|
-
Rights.PATH_OPEN |
|
|
394
|
-
Rights.FD_READDIR |
|
|
395
|
-
Rights.PATH_READLINK |
|
|
396
|
-
Rights.PATH_RENAME_SOURCE |
|
|
397
|
-
Rights.PATH_RENAME_TARGET |
|
|
398
|
-
Rights.PATH_FILESTAT_GET |
|
|
399
|
-
Rights.PATH_FILESTAT_SET_SIZE |
|
|
400
|
-
Rights.PATH_FILESTAT_SET_TIMES |
|
|
401
|
-
Rights.FD_FILESTAT_GET |
|
|
402
|
-
Rights.FD_FILESTAT_SET_TIMES |
|
|
403
|
-
Rights.PATH_SYMLINK |
|
|
404
|
-
Rights.PATH_REMOVE_DIRECTORY |
|
|
405
|
-
Rights.PATH_UNLINK_FILE |
|
|
406
|
-
Rights.POLL_FD_READWRITE;
|
|
407
|
-
|
|
408
|
-
const RIGHTS_DIRECTORY_INHERITING = RIGHTS_DIRECTORY_BASE | RIGHTS_FILE_BASE;
|
|
409
|
-
|
|
410
|
-
/** Rights for TTY/character devices */
|
|
411
|
-
const RIGHTS_TTY =
|
|
412
|
-
Rights.FD_READ | Rights.FD_FDSTAT_SET_FLAGS | Rights.FD_WRITE | Rights.FD_FILESTAT_GET | Rights.POLL_FD_READWRITE;
|
|
413
|
-
|
|
414
|
-
// =============================================================================
|
|
415
|
-
// WASI Open Flags (oflags)
|
|
416
|
-
// =============================================================================
|
|
417
|
-
|
|
418
|
-
export const enum OFlags {
|
|
419
|
-
/** Create file if it does not exist. */
|
|
420
|
-
CREAT = 1 << 0,
|
|
421
|
-
/** Fail if not a directory. */
|
|
422
|
-
DIRECTORY = 1 << 1,
|
|
423
|
-
/** Fail if file already exists. */
|
|
424
|
-
EXCL = 1 << 2,
|
|
425
|
-
/** Truncate file to size 0. */
|
|
426
|
-
TRUNC = 1 << 3,
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// =============================================================================
|
|
430
|
-
// WASI Lookup Flags
|
|
431
|
-
// =============================================================================
|
|
432
|
-
|
|
433
|
-
export const enum LookupFlags {
|
|
434
|
-
/** Follow symlinks. */
|
|
435
|
-
SYMLINK_FOLLOW = 1 << 0,
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// =============================================================================
|
|
439
|
-
// WASI Whence (for seek)
|
|
440
|
-
// =============================================================================
|
|
441
|
-
|
|
442
|
-
export const enum Whence {
|
|
443
|
-
/** Seek relative to start of file. */
|
|
444
|
-
SET = 0,
|
|
445
|
-
/** Seek relative to current position. */
|
|
446
|
-
CUR = 1,
|
|
447
|
-
/** Seek relative to end of file. */
|
|
448
|
-
END = 2,
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// =============================================================================
|
|
452
|
-
// WASI Filestat Set Flags (fstflags)
|
|
453
|
-
// =============================================================================
|
|
454
|
-
|
|
455
|
-
export const enum FstFlags {
|
|
456
|
-
/** Adjust the last data access timestamp to the provided value. */
|
|
457
|
-
ATIM = 1 << 0,
|
|
458
|
-
/** Adjust the last data access timestamp to the current time. */
|
|
459
|
-
ATIM_NOW = 1 << 1,
|
|
460
|
-
/** Adjust the last data modification timestamp to the provided value. */
|
|
461
|
-
MTIM = 1 << 2,
|
|
462
|
-
/** Adjust the last data modification timestamp to the current time. */
|
|
463
|
-
MTIM_NOW = 1 << 3,
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// =============================================================================
|
|
467
|
-
// WASI Event Types (for poll_oneoff)
|
|
468
|
-
// =============================================================================
|
|
469
|
-
|
|
470
|
-
export const enum EventType {
|
|
471
|
-
/** Clock event. */
|
|
472
|
-
CLOCK = 0,
|
|
473
|
-
/** File descriptor read event. */
|
|
474
|
-
FD_READ = 1,
|
|
475
|
-
/** File descriptor write event. */
|
|
476
|
-
FD_WRITE = 2,
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// =============================================================================
|
|
480
|
-
// WASI Subscription Clock Flags
|
|
481
|
-
// =============================================================================
|
|
482
|
-
|
|
483
|
-
export const enum SubClockFlags {
|
|
484
|
-
/** Clock is absolute (vs relative). */
|
|
485
|
-
ABSTIME = 1 << 0,
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// =============================================================================
|
|
489
|
-
// WASI Signals
|
|
490
|
-
// =============================================================================
|
|
491
|
-
|
|
492
|
-
export const enum Signal {
|
|
493
|
-
NONE = 0,
|
|
494
|
-
HUP = 1,
|
|
495
|
-
INT = 2,
|
|
496
|
-
QUIT = 3,
|
|
497
|
-
ILL = 4,
|
|
498
|
-
TRAP = 5,
|
|
499
|
-
ABRT = 6,
|
|
500
|
-
BUS = 7,
|
|
501
|
-
FPE = 8,
|
|
502
|
-
KILL = 9,
|
|
503
|
-
USR1 = 10,
|
|
504
|
-
SEGV = 11,
|
|
505
|
-
USR2 = 12,
|
|
506
|
-
PIPE = 13,
|
|
507
|
-
ALRM = 14,
|
|
508
|
-
TERM = 15,
|
|
509
|
-
CHLD = 16,
|
|
510
|
-
CONT = 17,
|
|
511
|
-
STOP = 18,
|
|
512
|
-
TSTP = 19,
|
|
513
|
-
TTIN = 20,
|
|
514
|
-
TTOU = 21,
|
|
515
|
-
URG = 22,
|
|
516
|
-
XCPU = 23,
|
|
517
|
-
XFSZ = 24,
|
|
518
|
-
VTALRM = 25,
|
|
519
|
-
PROF = 26,
|
|
520
|
-
WINCH = 27,
|
|
521
|
-
POLL = 28,
|
|
522
|
-
PWR = 29,
|
|
523
|
-
SYS = 30,
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
const SIGNAL_MAP: Record<number, NodeJS.Signals> = {
|
|
527
|
-
[Signal.HUP]: "SIGHUP",
|
|
528
|
-
[Signal.INT]: "SIGINT",
|
|
529
|
-
[Signal.QUIT]: "SIGQUIT",
|
|
530
|
-
[Signal.ILL]: "SIGILL",
|
|
531
|
-
[Signal.TRAP]: "SIGTRAP",
|
|
532
|
-
[Signal.ABRT]: "SIGABRT",
|
|
533
|
-
[Signal.BUS]: "SIGBUS",
|
|
534
|
-
[Signal.FPE]: "SIGFPE",
|
|
535
|
-
[Signal.KILL]: "SIGKILL",
|
|
536
|
-
[Signal.USR1]: "SIGUSR1",
|
|
537
|
-
[Signal.SEGV]: "SIGSEGV",
|
|
538
|
-
[Signal.USR2]: "SIGUSR2",
|
|
539
|
-
[Signal.PIPE]: "SIGPIPE",
|
|
540
|
-
[Signal.ALRM]: "SIGALRM",
|
|
541
|
-
[Signal.TERM]: "SIGTERM",
|
|
542
|
-
[Signal.CHLD]: "SIGCHLD",
|
|
543
|
-
[Signal.CONT]: "SIGCONT",
|
|
544
|
-
[Signal.STOP]: "SIGSTOP",
|
|
545
|
-
[Signal.TSTP]: "SIGTSTP",
|
|
546
|
-
[Signal.TTIN]: "SIGTTIN",
|
|
547
|
-
[Signal.TTOU]: "SIGTTOU",
|
|
548
|
-
[Signal.URG]: "SIGURG",
|
|
549
|
-
[Signal.XCPU]: "SIGXCPU",
|
|
550
|
-
[Signal.XFSZ]: "SIGXFSZ",
|
|
551
|
-
[Signal.VTALRM]: "SIGVTALRM",
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
// =============================================================================
|
|
555
|
-
// WASI Preopentype
|
|
556
|
-
// =============================================================================
|
|
557
|
-
|
|
558
|
-
export const enum PreopenType {
|
|
559
|
-
/** A pre-opened directory. */
|
|
560
|
-
DIR = 0,
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// =============================================================================
|
|
564
|
-
// WASI Advice (for fd_advise)
|
|
565
|
-
// =============================================================================
|
|
566
|
-
|
|
567
|
-
export const enum Advice {
|
|
568
|
-
NORMAL = 0,
|
|
569
|
-
SEQUENTIAL = 1,
|
|
570
|
-
RANDOM = 2,
|
|
571
|
-
WILLNEED = 3,
|
|
572
|
-
DONTNEED = 4,
|
|
573
|
-
NOREUSE = 5,
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// =============================================================================
|
|
577
|
-
// Custom Error Classes
|
|
578
|
-
// =============================================================================
|
|
579
|
-
|
|
580
|
-
export class WASIError extends Error {
|
|
581
|
-
constructor(public errno: Errno) {
|
|
582
|
-
super(`WASI error: ${getErrnoName(errno)} (${errno})`);
|
|
583
|
-
this.name = "WASIError";
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
export class WASIExitError extends Error {
|
|
588
|
-
constructor(public code: number) {
|
|
589
|
-
super(`WASI exit: ${code}`);
|
|
590
|
-
this.name = "WASIExitError";
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// =============================================================================
|
|
595
|
-
// File Descriptor Entry
|
|
596
|
-
// =============================================================================
|
|
597
|
-
|
|
598
|
-
interface FdEntry {
|
|
599
|
-
/** The real OS file descriptor or handle */
|
|
600
|
-
fd: number;
|
|
601
|
-
/** WASI file type */
|
|
602
|
-
fileType: FileType;
|
|
603
|
-
/** Base rights */
|
|
604
|
-
rightsBase: bigint;
|
|
605
|
-
/** Inheriting rights */
|
|
606
|
-
rightsInheriting: bigint;
|
|
607
|
-
/** Current file offset (for regular files) */
|
|
608
|
-
offset?: bigint;
|
|
609
|
-
/** Path on the real filesystem (for preopens/opened paths) */
|
|
610
|
-
realPath?: string;
|
|
611
|
-
/** Virtual path (for preopens) */
|
|
612
|
-
preopenPath?: string;
|
|
613
|
-
/** FD flags */
|
|
614
|
-
fdFlags: number;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// =============================================================================
|
|
618
|
-
// WASI Configuration
|
|
619
|
-
// =============================================================================
|
|
620
|
-
|
|
621
|
-
export interface WASIOptions {
|
|
622
|
-
/** Command-line arguments */
|
|
623
|
-
args?: string[];
|
|
624
|
-
/** Environment variables */
|
|
625
|
-
env?: Record<string, string>;
|
|
626
|
-
/** Preopened directories: map of virtual path -> real path */
|
|
627
|
-
preopens?: Record<string, string>;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// =============================================================================
|
|
631
|
-
// Utility Functions
|
|
632
|
-
// =============================================================================
|
|
633
|
-
|
|
634
|
-
/** Convert milliseconds to nanoseconds (as bigint) */
|
|
635
|
-
function msToNs(ms: number): bigint {
|
|
636
|
-
return BigInt(Math.trunc(ms * 1_000_000));
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/** Convert nanoseconds to milliseconds */
|
|
640
|
-
function nsToMs(ns: bigint): number {
|
|
641
|
-
return Number(ns / 1_000_000n);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
/** Get current time in nanoseconds for a given clock */
|
|
645
|
-
function clockTimeNs(clockId: ClockId): bigint | null {
|
|
646
|
-
switch (clockId) {
|
|
647
|
-
case ClockId.REALTIME:
|
|
648
|
-
return msToNs(Date.now());
|
|
649
|
-
case ClockId.MONOTONIC:
|
|
650
|
-
return process.hrtime.bigint();
|
|
651
|
-
case ClockId.PROCESS_CPUTIME_ID:
|
|
652
|
-
case ClockId.THREAD_CPUTIME_ID:
|
|
653
|
-
// Use hrtime as approximation for CPU time
|
|
654
|
-
return process.hrtime.bigint();
|
|
655
|
-
default:
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/** Convert Node.js fs.Stats to WASI FileType */
|
|
661
|
-
function statsToFileType(stats: fs.Stats): FileType {
|
|
662
|
-
if (stats.isFile()) return FileType.REGULAR_FILE;
|
|
663
|
-
if (stats.isDirectory()) return FileType.DIRECTORY;
|
|
664
|
-
if (stats.isSymbolicLink()) return FileType.SYMBOLIC_LINK;
|
|
665
|
-
if (stats.isCharacterDevice()) return FileType.CHARACTER_DEVICE;
|
|
666
|
-
if (stats.isBlockDevice()) return FileType.BLOCK_DEVICE;
|
|
667
|
-
if (stats.isFIFO()) return FileType.SOCKET_STREAM;
|
|
668
|
-
if (stats.isSocket()) return FileType.SOCKET_STREAM;
|
|
669
|
-
return FileType.UNKNOWN;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/** Get rights for a file type */
|
|
673
|
-
function rightsForFileType(fileType: FileType, isTTY: boolean): { base: bigint; inheriting: bigint } {
|
|
674
|
-
switch (fileType) {
|
|
675
|
-
case FileType.REGULAR_FILE:
|
|
676
|
-
return { base: RIGHTS_FILE_BASE, inheriting: 0n };
|
|
677
|
-
case FileType.DIRECTORY:
|
|
678
|
-
return { base: RIGHTS_DIRECTORY_BASE, inheriting: RIGHTS_DIRECTORY_INHERITING };
|
|
679
|
-
case FileType.CHARACTER_DEVICE:
|
|
680
|
-
if (isTTY) {
|
|
681
|
-
return { base: RIGHTS_TTY, inheriting: 0n };
|
|
682
|
-
}
|
|
683
|
-
return { base: RIGHTS_ALL, inheriting: RIGHTS_ALL };
|
|
684
|
-
case FileType.BLOCK_DEVICE:
|
|
685
|
-
return { base: RIGHTS_ALL, inheriting: RIGHTS_ALL };
|
|
686
|
-
case FileType.SOCKET_STREAM:
|
|
687
|
-
case FileType.SOCKET_DGRAM:
|
|
688
|
-
return { base: RIGHTS_ALL, inheriting: RIGHTS_ALL };
|
|
689
|
-
default:
|
|
690
|
-
return { base: RIGHTS_ALL, inheriting: RIGHTS_ALL };
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
/** Wrap a syscall function to catch errors and return errno */
|
|
695
|
-
function wrap<T extends (...args: any[]) => number>(fn: T): T {
|
|
696
|
-
return ((...args: Parameters<T>): number => {
|
|
697
|
-
try {
|
|
698
|
-
return fn(...args);
|
|
699
|
-
} catch (err: any) {
|
|
700
|
-
if (err instanceof WASIError) {
|
|
701
|
-
return err.errno;
|
|
702
|
-
}
|
|
703
|
-
if (err instanceof WASIExitError) {
|
|
704
|
-
throw err;
|
|
705
|
-
}
|
|
706
|
-
// Map Node.js errors
|
|
707
|
-
let e = err;
|
|
708
|
-
while (e?.cause) e = e.cause;
|
|
709
|
-
if (e?.code && typeof e.code === "string" && e.code in NODE_ERROR_MAP) {
|
|
710
|
-
return NODE_ERROR_MAP[e.code];
|
|
711
|
-
}
|
|
712
|
-
console.error("WASI unexpected error:", err);
|
|
713
|
-
return Errno.EIO;
|
|
714
|
-
}
|
|
715
|
-
}) as T;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/** Read a null-terminated string from memory */
|
|
719
|
-
function readString(memory: DataView, ptr: number, len: number): string {
|
|
720
|
-
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
|
721
|
-
return new TextDecoder().decode(bytes);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
/** Write a string to memory */
|
|
725
|
-
function writeString(memory: DataView, ptr: number, str: string): number {
|
|
726
|
-
const bytes = new TextEncoder().encode(str);
|
|
727
|
-
new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
|
|
728
|
-
return bytes.length;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// =============================================================================
|
|
732
|
-
// WASI Class
|
|
733
|
-
// =============================================================================
|
|
734
|
-
|
|
735
|
-
const FD_INIT = [
|
|
736
|
-
{ fd: 0, fileType: FileType.CHARACTER_DEVICE, rightsBase: RIGHTS_TTY, rightsInheriting: 0n, fdFlags: 0 },
|
|
737
|
-
{ fd: 1, fileType: FileType.CHARACTER_DEVICE, rightsBase: RIGHTS_TTY, rightsInheriting: 0n, fdFlags: 0 },
|
|
738
|
-
{ fd: 2, fileType: FileType.CHARACTER_DEVICE, rightsBase: RIGHTS_TTY, rightsInheriting: 0n, fdFlags: 0 },
|
|
739
|
-
];
|
|
740
|
-
|
|
741
|
-
export class WASI1 {
|
|
742
|
-
#memory!: WebAssembly.Memory; /** WebAssembly memory */
|
|
743
|
-
#args: string[]; /** Program arguments */
|
|
744
|
-
#env: Record<string, string>; /** Environment variables */
|
|
745
|
-
#fds = new Map<number, FdEntry>(); /** Map of WASI fd -> FdEntry */
|
|
746
|
-
#nextFd = 3; /** Next free WASI fd */
|
|
747
|
-
#wasiImport: ReturnType<WASI1["buildImports"]>; /** WASI imports object */
|
|
748
|
-
|
|
749
|
-
constructor(options: WASIOptions = {}) {
|
|
750
|
-
this.#args = options.args ?? [];
|
|
751
|
-
this.#env = options.env ?? {};
|
|
752
|
-
|
|
753
|
-
// Initialize stdin/stdout/stderr
|
|
754
|
-
for (let i = 0; i < FD_INIT.length; i++) {
|
|
755
|
-
this.#fds.set(FD_INIT[i].fd, FD_INIT[i]);
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// Set up preopens
|
|
759
|
-
const preopens = options.preopens ?? this.getDefaultPreopens();
|
|
760
|
-
for (const [virtualPath, realPath] of Object.entries(preopens)) {
|
|
761
|
-
this.addPreopen(virtualPath, realPath);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// Build the WASI imports
|
|
765
|
-
this.#wasiImport = this.buildImports();
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
/** Get default preopens based on platform */
|
|
769
|
-
private getDefaultPreopens(): Record<string, string> {
|
|
770
|
-
if (os.platform() === "win32") {
|
|
771
|
-
// On Windows, preopen all available drive letters
|
|
772
|
-
const preopens: Record<string, string> = {};
|
|
773
|
-
for (let i = 65; i <= 90; i++) {
|
|
774
|
-
// A-Z
|
|
775
|
-
const letter = String.fromCharCode(i);
|
|
776
|
-
const drivePath = `${letter}:\\`;
|
|
777
|
-
try {
|
|
778
|
-
fs.accessSync(drivePath);
|
|
779
|
-
preopens[`/${letter.toLowerCase()}`] = drivePath;
|
|
780
|
-
} catch {
|
|
781
|
-
// Drive doesn't exist, skip
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
return preopens;
|
|
785
|
-
} else {
|
|
786
|
-
// On Unix, preopen root
|
|
787
|
-
return { "/": "/" };
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/** Add a preopen directory */
|
|
792
|
-
private addPreopen(virtualPath: string, realPath: string): void {
|
|
793
|
-
try {
|
|
794
|
-
const fd = fs.openSync(realPath, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY);
|
|
795
|
-
const wasiFd = this.#nextFd++;
|
|
796
|
-
this.#fds.set(wasiFd, {
|
|
797
|
-
fd,
|
|
798
|
-
fileType: FileType.DIRECTORY,
|
|
799
|
-
rightsBase: RIGHTS_DIRECTORY_BASE,
|
|
800
|
-
rightsInheriting: RIGHTS_DIRECTORY_INHERITING,
|
|
801
|
-
realPath,
|
|
802
|
-
preopenPath: virtualPath,
|
|
803
|
-
fdFlags: 0,
|
|
804
|
-
});
|
|
805
|
-
} catch (err) {
|
|
806
|
-
console.warn(`Failed to preopen ${virtualPath} -> ${realPath}:`, err);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
#view() {
|
|
811
|
-
return new DataView(this.#memory.buffer);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
#mem(ptr: number, len: number) {
|
|
815
|
-
return new Uint8Array(this.#memory.buffer, ptr, len);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
/** Get an FD entry, throwing if invalid */
|
|
819
|
-
private getFd(fd: number): FdEntry {
|
|
820
|
-
const entry = this.#fds.get(fd);
|
|
821
|
-
if (!entry) {
|
|
822
|
-
throw new WASIError(Errno.EBADF);
|
|
823
|
-
}
|
|
824
|
-
return entry;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
/** Check rights on an FD */
|
|
828
|
-
private checkRights(fd: number, rights: bigint): FdEntry {
|
|
829
|
-
const entry = this.getFd(fd);
|
|
830
|
-
if ((entry.rightsBase & rights) !== rights) {
|
|
831
|
-
throw new WASIError(Errno.ENOTCAPABLE);
|
|
832
|
-
}
|
|
833
|
-
return entry;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
/** Allocate a new WASI fd */
|
|
837
|
-
private allocateFd(): number {
|
|
838
|
-
return this.#nextFd++;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/** Read iovecs from memory */
|
|
842
|
-
private readIovecs(iovsPtr: number, iovsLen: number): Uint8Array[] {
|
|
843
|
-
const v = this.#view();
|
|
844
|
-
const buffers: Uint8Array[] = [];
|
|
845
|
-
for (let i = 0; i < iovsLen; i++) {
|
|
846
|
-
const ptr = v.getUint32(iovsPtr + i * 8, true);
|
|
847
|
-
const len = v.getUint32(iovsPtr + i * 8 + 4, true);
|
|
848
|
-
buffers.push(this.#mem(ptr, len));
|
|
849
|
-
}
|
|
850
|
-
return buffers;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/** Resolve a path relative to a directory fd */
|
|
854
|
-
private resolvePath(dirFd: number, pathPtr: number, pathLen: number): string {
|
|
855
|
-
const entry = this.getFd(dirFd);
|
|
856
|
-
if (entry.fileType !== FileType.DIRECTORY) {
|
|
857
|
-
throw new WASIError(Errno.ENOTDIR);
|
|
858
|
-
}
|
|
859
|
-
const relativePath = readString(this.#view(), pathPtr, pathLen);
|
|
860
|
-
if (!entry.realPath) {
|
|
861
|
-
throw new WASIError(Errno.EINVAL);
|
|
862
|
-
}
|
|
863
|
-
return path.resolve(entry.realPath, relativePath);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
/** Build all WASI imports */
|
|
867
|
-
private buildImports() {
|
|
868
|
-
return {
|
|
869
|
-
// =========================================================================
|
|
870
|
-
// Arguments
|
|
871
|
-
// =========================================================================
|
|
872
|
-
|
|
873
|
-
args_get: wrap((argvPtr: number, argvBufPtr: number): number => {
|
|
874
|
-
const v = this.#view();
|
|
875
|
-
let bufOffset = argvBufPtr;
|
|
876
|
-
for (let i = 0; i < this.#args.length; i++) {
|
|
877
|
-
v.setUint32(argvPtr + i * 4, bufOffset, true);
|
|
878
|
-
bufOffset += writeString(v, bufOffset, `${this.#args[i]}\0`);
|
|
879
|
-
}
|
|
880
|
-
return Errno.SUCCESS;
|
|
881
|
-
}),
|
|
882
|
-
|
|
883
|
-
args_sizes_get: wrap((argcPtr: number, argvBufSizePtr: number): number => {
|
|
884
|
-
const v = this.#view();
|
|
885
|
-
v.setUint32(argcPtr, this.#args.length, true);
|
|
886
|
-
const bufSize = this.#args.reduce((sum, arg) => sum + new TextEncoder().encode(arg).length + 1, 0);
|
|
887
|
-
v.setUint32(argvBufSizePtr, bufSize, true);
|
|
888
|
-
return Errno.SUCCESS;
|
|
889
|
-
}),
|
|
890
|
-
|
|
891
|
-
// =========================================================================
|
|
892
|
-
// Environment
|
|
893
|
-
// =========================================================================
|
|
894
|
-
|
|
895
|
-
environ_get: wrap((environPtr: number, environBufPtr: number): number => {
|
|
896
|
-
const v = this.#view();
|
|
897
|
-
const entries = Object.entries(this.#env);
|
|
898
|
-
let bufOffset = environBufPtr;
|
|
899
|
-
for (let i = 0; i < entries.length; i++) {
|
|
900
|
-
const [key, value] = entries[i];
|
|
901
|
-
v.setUint32(environPtr + i * 4, bufOffset, true);
|
|
902
|
-
bufOffset += writeString(v, bufOffset, `${key}=${value}\0`);
|
|
903
|
-
}
|
|
904
|
-
return Errno.SUCCESS;
|
|
905
|
-
}),
|
|
906
|
-
|
|
907
|
-
environ_sizes_get: wrap((environCountPtr: number, environBufSizePtr: number): number => {
|
|
908
|
-
const v = this.#view();
|
|
909
|
-
const entries = Object.entries(this.#env);
|
|
910
|
-
v.setUint32(environCountPtr, entries.length, true);
|
|
911
|
-
const bufSize = entries.reduce((sum, [k, v]) => sum + new TextEncoder().encode(`${k}=${v}`).length + 1, 0);
|
|
912
|
-
v.setUint32(environBufSizePtr, bufSize, true);
|
|
913
|
-
return Errno.SUCCESS;
|
|
914
|
-
}),
|
|
915
|
-
|
|
916
|
-
// =========================================================================
|
|
917
|
-
// Clock
|
|
918
|
-
// =========================================================================
|
|
919
|
-
|
|
920
|
-
clock_res_get: wrap((clockId: number, resPtr: number): number => {
|
|
921
|
-
const v = this.#view();
|
|
922
|
-
let res: bigint;
|
|
923
|
-
switch (clockId) {
|
|
924
|
-
case ClockId.REALTIME:
|
|
925
|
-
res = 1_000_000n; // 1ms
|
|
926
|
-
break;
|
|
927
|
-
case ClockId.MONOTONIC:
|
|
928
|
-
case ClockId.PROCESS_CPUTIME_ID:
|
|
929
|
-
case ClockId.THREAD_CPUTIME_ID:
|
|
930
|
-
res = 1n; // 1ns
|
|
931
|
-
break;
|
|
932
|
-
default:
|
|
933
|
-
return Errno.EINVAL;
|
|
934
|
-
}
|
|
935
|
-
v.setBigUint64(resPtr, res, true);
|
|
936
|
-
return Errno.SUCCESS;
|
|
937
|
-
}),
|
|
938
|
-
|
|
939
|
-
clock_time_get: wrap((clockId: number, _precision: bigint, timePtr: number): number => {
|
|
940
|
-
const v = this.#view();
|
|
941
|
-
const time = clockTimeNs(clockId);
|
|
942
|
-
if (time === null) {
|
|
943
|
-
return Errno.EINVAL;
|
|
944
|
-
}
|
|
945
|
-
v.setBigUint64(timePtr, time, true);
|
|
946
|
-
return Errno.SUCCESS;
|
|
947
|
-
}),
|
|
948
|
-
|
|
949
|
-
// =========================================================================
|
|
950
|
-
// File Descriptor Operations
|
|
951
|
-
// =========================================================================
|
|
952
|
-
|
|
953
|
-
fd_advise: wrap((fd: number, _offset: bigint, _len: bigint, _advice: number): number => {
|
|
954
|
-
this.checkRights(fd, Rights.FD_ADVISE);
|
|
955
|
-
// Advisory only, no-op is valid
|
|
956
|
-
return Errno.SUCCESS;
|
|
957
|
-
}),
|
|
958
|
-
|
|
959
|
-
fd_allocate: wrap((fd: number, offset: bigint, len: bigint): number => {
|
|
960
|
-
const entry = this.checkRights(fd, Rights.FD_ALLOCATE);
|
|
961
|
-
// Extend file if needed
|
|
962
|
-
const stats = fs.fstatSync(entry.fd);
|
|
963
|
-
const newSize = Number(offset + len);
|
|
964
|
-
if (newSize > stats.size) {
|
|
965
|
-
fs.ftruncateSync(entry.fd, newSize);
|
|
966
|
-
}
|
|
967
|
-
return Errno.SUCCESS;
|
|
968
|
-
}),
|
|
969
|
-
|
|
970
|
-
fd_close: wrap((fd: number): number => {
|
|
971
|
-
const entry = this.getFd(fd);
|
|
972
|
-
// Don't close stdin/stdout/stderr
|
|
973
|
-
if (fd > 2) {
|
|
974
|
-
fs.closeSync(entry.fd);
|
|
975
|
-
}
|
|
976
|
-
this.#fds.delete(fd);
|
|
977
|
-
return Errno.SUCCESS;
|
|
978
|
-
}),
|
|
979
|
-
|
|
980
|
-
fd_datasync: wrap((fd: number): number => {
|
|
981
|
-
const entry = this.checkRights(fd, Rights.FD_DATASYNC);
|
|
982
|
-
fs.fdatasyncSync(entry.fd);
|
|
983
|
-
return Errno.SUCCESS;
|
|
984
|
-
}),
|
|
985
|
-
|
|
986
|
-
fd_fdstat_get: wrap((fd: number, statPtr: number): number => {
|
|
987
|
-
const v = this.#view();
|
|
988
|
-
const entry = this.getFd(fd);
|
|
989
|
-
|
|
990
|
-
// fdstat structure:
|
|
991
|
-
// u8 fs_filetype
|
|
992
|
-
// u16 fs_flags
|
|
993
|
-
// u64 fs_rights_base
|
|
994
|
-
// u64 fs_rights_inheriting
|
|
995
|
-
v.setUint8(statPtr, entry.fileType);
|
|
996
|
-
v.setUint16(statPtr + 2, entry.fdFlags, true);
|
|
997
|
-
v.setBigUint64(statPtr + 8, entry.rightsBase, true);
|
|
998
|
-
v.setBigUint64(statPtr + 16, entry.rightsInheriting, true);
|
|
999
|
-
return Errno.SUCCESS;
|
|
1000
|
-
}),
|
|
1001
|
-
|
|
1002
|
-
fd_fdstat_set_flags: wrap((fd: number, flags: number): number => {
|
|
1003
|
-
const entry = this.checkRights(fd, Rights.FD_FDSTAT_SET_FLAGS);
|
|
1004
|
-
entry.fdFlags = flags;
|
|
1005
|
-
// Note: Most flags don't have direct OS equivalents in sync I/O
|
|
1006
|
-
return Errno.SUCCESS;
|
|
1007
|
-
}),
|
|
1008
|
-
|
|
1009
|
-
fd_fdstat_set_rights: wrap((fd: number, rightsBase: bigint, rightsInheriting: bigint): number => {
|
|
1010
|
-
const entry = this.getFd(fd);
|
|
1011
|
-
// Can only remove rights, not add
|
|
1012
|
-
if ((rightsBase & ~entry.rightsBase) !== 0n) {
|
|
1013
|
-
return Errno.ENOTCAPABLE;
|
|
1014
|
-
}
|
|
1015
|
-
if ((rightsInheriting & ~entry.rightsInheriting) !== 0n) {
|
|
1016
|
-
return Errno.ENOTCAPABLE;
|
|
1017
|
-
}
|
|
1018
|
-
entry.rightsBase = rightsBase;
|
|
1019
|
-
entry.rightsInheriting = rightsInheriting;
|
|
1020
|
-
return Errno.SUCCESS;
|
|
1021
|
-
}),
|
|
1022
|
-
|
|
1023
|
-
fd_filestat_get: wrap((fd: number, statPtr: number): number => {
|
|
1024
|
-
const v = this.#view();
|
|
1025
|
-
const entry = this.checkRights(fd, Rights.FD_FILESTAT_GET);
|
|
1026
|
-
const stats = fs.fstatSync(entry.fd);
|
|
1027
|
-
|
|
1028
|
-
// filestat structure (64 bytes):
|
|
1029
|
-
// u64 dev, u64 ino, u8 filetype, u64 nlink, u64 size,
|
|
1030
|
-
// u64 atim, u64 mtim, u64 ctim
|
|
1031
|
-
v.setBigUint64(statPtr, BigInt(stats.dev), true);
|
|
1032
|
-
v.setBigUint64(statPtr + 8, BigInt(stats.ino), true);
|
|
1033
|
-
v.setUint8(statPtr + 16, statsToFileType(stats));
|
|
1034
|
-
v.setBigUint64(statPtr + 24, BigInt(stats.nlink), true);
|
|
1035
|
-
v.setBigUint64(statPtr + 32, BigInt(stats.size), true);
|
|
1036
|
-
v.setBigUint64(statPtr + 40, msToNs(stats.atimeMs), true);
|
|
1037
|
-
v.setBigUint64(statPtr + 48, msToNs(stats.mtimeMs), true);
|
|
1038
|
-
v.setBigUint64(statPtr + 56, msToNs(stats.ctimeMs), true);
|
|
1039
|
-
return Errno.SUCCESS;
|
|
1040
|
-
}),
|
|
1041
|
-
|
|
1042
|
-
fd_filestat_set_size: wrap((fd: number, size: bigint): number => {
|
|
1043
|
-
const entry = this.checkRights(fd, Rights.FD_FILESTAT_SET_SIZE);
|
|
1044
|
-
fs.ftruncateSync(entry.fd, Number(size));
|
|
1045
|
-
return Errno.SUCCESS;
|
|
1046
|
-
}),
|
|
1047
|
-
|
|
1048
|
-
fd_filestat_set_times: wrap((fd: number, atim: bigint, mtim: bigint, fstFlags: number): number => {
|
|
1049
|
-
const entry = this.checkRights(fd, Rights.FD_FILESTAT_SET_TIMES);
|
|
1050
|
-
const stats = fs.fstatSync(entry.fd);
|
|
1051
|
-
|
|
1052
|
-
let atime: Date;
|
|
1053
|
-
let mtime: Date;
|
|
1054
|
-
const now = new Date();
|
|
1055
|
-
|
|
1056
|
-
if (fstFlags & FstFlags.ATIM_NOW) {
|
|
1057
|
-
atime = now;
|
|
1058
|
-
} else if (fstFlags & FstFlags.ATIM) {
|
|
1059
|
-
atime = new Date(nsToMs(atim));
|
|
1060
|
-
} else {
|
|
1061
|
-
atime = stats.atime;
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
if (fstFlags & FstFlags.MTIM_NOW) {
|
|
1065
|
-
mtime = now;
|
|
1066
|
-
} else if (fstFlags & FstFlags.MTIM) {
|
|
1067
|
-
mtime = new Date(nsToMs(mtim));
|
|
1068
|
-
} else {
|
|
1069
|
-
mtime = stats.mtime;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
fs.futimesSync(entry.fd, atime, mtime);
|
|
1073
|
-
return Errno.SUCCESS;
|
|
1074
|
-
}),
|
|
1075
|
-
|
|
1076
|
-
fd_pread: wrap((fd: number, iovsPtr: number, iovsLen: number, offset: bigint, nreadPtr: number): number => {
|
|
1077
|
-
const entry = this.checkRights(fd, Rights.FD_READ | Rights.FD_SEEK);
|
|
1078
|
-
const buffers = this.readIovecs(iovsPtr, iovsLen);
|
|
1079
|
-
|
|
1080
|
-
let totalRead = 0;
|
|
1081
|
-
let currentOffset = Number(offset);
|
|
1082
|
-
for (const buf of buffers) {
|
|
1083
|
-
const bytesRead = fs.readSync(entry.fd, buf, 0, buf.length, currentOffset);
|
|
1084
|
-
totalRead += bytesRead;
|
|
1085
|
-
currentOffset += bytesRead;
|
|
1086
|
-
if (bytesRead < buf.length) break;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
const v = this.#view();
|
|
1090
|
-
v.setUint32(nreadPtr, totalRead, true);
|
|
1091
|
-
return Errno.SUCCESS;
|
|
1092
|
-
}),
|
|
1093
|
-
|
|
1094
|
-
fd_prestat_get: wrap((fd: number, prestatPtr: number): number => {
|
|
1095
|
-
const v = this.#view();
|
|
1096
|
-
const entry = this.getFd(fd);
|
|
1097
|
-
if (!entry.preopenPath) {
|
|
1098
|
-
return Errno.EBADF;
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
// prestat structure:
|
|
1102
|
-
// u8 tag (0 = dir)
|
|
1103
|
-
// u32 name_len
|
|
1104
|
-
v.setUint8(prestatPtr, PreopenType.DIR);
|
|
1105
|
-
const pathBytes = new TextEncoder().encode(entry.preopenPath);
|
|
1106
|
-
v.setUint32(prestatPtr + 4, pathBytes.length, true);
|
|
1107
|
-
return Errno.SUCCESS;
|
|
1108
|
-
}),
|
|
1109
|
-
|
|
1110
|
-
fd_prestat_dir_name: wrap((fd: number, pathPtr: number, pathLen: number): number => {
|
|
1111
|
-
const entry = this.getFd(fd);
|
|
1112
|
-
if (!entry.preopenPath) {
|
|
1113
|
-
return Errno.EBADF;
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
const pathBytes = new TextEncoder().encode(entry.preopenPath);
|
|
1117
|
-
if (pathLen < pathBytes.length) {
|
|
1118
|
-
return Errno.ENOBUFS;
|
|
1119
|
-
}
|
|
1120
|
-
this.#mem(pathPtr, pathBytes.length).set(pathBytes);
|
|
1121
|
-
return Errno.SUCCESS;
|
|
1122
|
-
}),
|
|
1123
|
-
|
|
1124
|
-
fd_pwrite: wrap(
|
|
1125
|
-
(fd: number, iovsPtr: number, iovsLen: number, offset: bigint, nwrittenPtr: number): number => {
|
|
1126
|
-
const entry = this.checkRights(fd, Rights.FD_WRITE | Rights.FD_SEEK);
|
|
1127
|
-
const buffers = this.readIovecs(iovsPtr, iovsLen);
|
|
1128
|
-
|
|
1129
|
-
let totalWritten = 0;
|
|
1130
|
-
let currentOffset = Number(offset);
|
|
1131
|
-
for (const buf of buffers) {
|
|
1132
|
-
const bytesWritten = fs.writeSync(entry.fd, buf, 0, buf.length, currentOffset);
|
|
1133
|
-
totalWritten += bytesWritten;
|
|
1134
|
-
currentOffset += bytesWritten;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
this.#view().setUint32(nwrittenPtr, totalWritten, true);
|
|
1138
|
-
return Errno.SUCCESS;
|
|
1139
|
-
},
|
|
1140
|
-
),
|
|
1141
|
-
|
|
1142
|
-
fd_read: wrap((fd: number, iovsPtr: number, iovsLen: number, nreadPtr: number): number => {
|
|
1143
|
-
const entry = this.checkRights(fd, Rights.FD_READ);
|
|
1144
|
-
const buffers = this.readIovecs(iovsPtr, iovsLen);
|
|
1145
|
-
|
|
1146
|
-
let totalRead = 0;
|
|
1147
|
-
for (const buf of buffers) {
|
|
1148
|
-
const position = entry.offset !== undefined ? Number(entry.offset) : null;
|
|
1149
|
-
const bytesRead = fs.readSync(entry.fd, buf, 0, buf.length, position);
|
|
1150
|
-
totalRead += bytesRead;
|
|
1151
|
-
if (entry.offset !== undefined) {
|
|
1152
|
-
entry.offset += BigInt(bytesRead);
|
|
1153
|
-
}
|
|
1154
|
-
if (bytesRead < buf.length) break;
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
const v = this.#view();
|
|
1158
|
-
v.setUint32(nreadPtr, totalRead, true);
|
|
1159
|
-
return Errno.SUCCESS;
|
|
1160
|
-
}),
|
|
1161
|
-
|
|
1162
|
-
fd_readdir: wrap((fd: number, bufPtr: number, bufLen: number, cookie: bigint, bufUsedPtr: number): number => {
|
|
1163
|
-
const v = this.#view();
|
|
1164
|
-
const entry = this.checkRights(fd, Rights.FD_READDIR);
|
|
1165
|
-
if (!entry.realPath) {
|
|
1166
|
-
return Errno.EINVAL;
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
const dirents = fs.readdirSync(entry.realPath, { withFileTypes: true });
|
|
1170
|
-
let offset = bufPtr;
|
|
1171
|
-
const startPtr = bufPtr;
|
|
1172
|
-
const cookieNum = Number(cookie);
|
|
1173
|
-
|
|
1174
|
-
for (let i = cookieNum; i < dirents.length; i++) {
|
|
1175
|
-
const dirent = dirents[i];
|
|
1176
|
-
const nameBytes = new TextEncoder().encode(dirent.name);
|
|
1177
|
-
|
|
1178
|
-
// dirent structure:
|
|
1179
|
-
// u64 d_next (cookie of next entry)
|
|
1180
|
-
// u64 d_ino
|
|
1181
|
-
// u32 d_namlen
|
|
1182
|
-
// u8 d_type
|
|
1183
|
-
// char d_name[]
|
|
1184
|
-
const direntSize = 24 + nameBytes.length;
|
|
1185
|
-
|
|
1186
|
-
if (offset - startPtr + direntSize > bufLen) {
|
|
1187
|
-
break;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// Get inode
|
|
1191
|
-
let ino = 0n;
|
|
1192
|
-
try {
|
|
1193
|
-
const stats = fs.statSync(path.join(entry.realPath!, dirent.name));
|
|
1194
|
-
ino = BigInt(stats.ino);
|
|
1195
|
-
} catch {}
|
|
1196
|
-
|
|
1197
|
-
v.setBigUint64(offset, BigInt(i + 1), true);
|
|
1198
|
-
v.setBigUint64(offset + 8, ino, true);
|
|
1199
|
-
v.setUint32(offset + 16, nameBytes.length, true);
|
|
1200
|
-
|
|
1201
|
-
let fileType = FileType.UNKNOWN;
|
|
1202
|
-
if (dirent.isFile()) fileType = FileType.REGULAR_FILE;
|
|
1203
|
-
else if (dirent.isDirectory()) fileType = FileType.DIRECTORY;
|
|
1204
|
-
else if (dirent.isSymbolicLink()) fileType = FileType.SYMBOLIC_LINK;
|
|
1205
|
-
else if (dirent.isCharacterDevice()) fileType = FileType.CHARACTER_DEVICE;
|
|
1206
|
-
else if (dirent.isBlockDevice()) fileType = FileType.BLOCK_DEVICE;
|
|
1207
|
-
else if (dirent.isFIFO()) fileType = FileType.SOCKET_STREAM;
|
|
1208
|
-
else if (dirent.isSocket()) fileType = FileType.SOCKET_STREAM;
|
|
1209
|
-
|
|
1210
|
-
v.setUint8(offset + 20, fileType);
|
|
1211
|
-
this.#mem(offset + 24, nameBytes.length).set(nameBytes);
|
|
1212
|
-
|
|
1213
|
-
offset += direntSize;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
v.setUint32(bufUsedPtr, offset - startPtr, true);
|
|
1217
|
-
return Errno.SUCCESS;
|
|
1218
|
-
}),
|
|
1219
|
-
|
|
1220
|
-
fd_renumber: wrap((from: number, to: number): number => {
|
|
1221
|
-
const fromEntry = this.getFd(from);
|
|
1222
|
-
const toEntry = this.#fds.get(to);
|
|
1223
|
-
|
|
1224
|
-
// Close the target if it exists
|
|
1225
|
-
if (toEntry && to > 2) {
|
|
1226
|
-
fs.closeSync(toEntry.fd);
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
this.#fds.set(to, fromEntry);
|
|
1230
|
-
this.#fds.delete(from);
|
|
1231
|
-
return Errno.SUCCESS;
|
|
1232
|
-
}),
|
|
1233
|
-
|
|
1234
|
-
fd_seek: wrap((fd: number, offset: bigint, whence: number, newOffsetPtr: number): number => {
|
|
1235
|
-
const v = this.#view();
|
|
1236
|
-
const entry = this.checkRights(fd, Rights.FD_SEEK);
|
|
1237
|
-
|
|
1238
|
-
if (entry.offset === undefined) {
|
|
1239
|
-
entry.offset = 0n;
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
let newOffset: bigint;
|
|
1243
|
-
switch (whence) {
|
|
1244
|
-
case Whence.SET:
|
|
1245
|
-
newOffset = offset;
|
|
1246
|
-
break;
|
|
1247
|
-
case Whence.CUR:
|
|
1248
|
-
newOffset = entry.offset + offset;
|
|
1249
|
-
break;
|
|
1250
|
-
case Whence.END: {
|
|
1251
|
-
const stats = fs.fstatSync(entry.fd);
|
|
1252
|
-
newOffset = BigInt(stats.size) + offset;
|
|
1253
|
-
break;
|
|
1254
|
-
}
|
|
1255
|
-
default:
|
|
1256
|
-
return Errno.EINVAL;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
if (newOffset < 0n) {
|
|
1260
|
-
return Errno.EINVAL;
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
entry.offset = newOffset;
|
|
1264
|
-
v.setBigUint64(newOffsetPtr, newOffset, true);
|
|
1265
|
-
return Errno.SUCCESS;
|
|
1266
|
-
}),
|
|
1267
|
-
|
|
1268
|
-
fd_sync: wrap((fd: number): number => {
|
|
1269
|
-
const entry = this.checkRights(fd, Rights.FD_SYNC);
|
|
1270
|
-
fs.fsyncSync(entry.fd);
|
|
1271
|
-
return Errno.SUCCESS;
|
|
1272
|
-
}),
|
|
1273
|
-
|
|
1274
|
-
fd_tell: wrap((fd: number, offsetPtr: number): number => {
|
|
1275
|
-
const v = this.#view();
|
|
1276
|
-
const entry = this.checkRights(fd, Rights.FD_TELL);
|
|
1277
|
-
const offset = entry.offset ?? 0n;
|
|
1278
|
-
v.setBigUint64(offsetPtr, offset, true);
|
|
1279
|
-
return Errno.SUCCESS;
|
|
1280
|
-
}),
|
|
1281
|
-
|
|
1282
|
-
fd_write: wrap((fd: number, iovsPtr: number, iovsLen: number, nwrittenPtr: number): number => {
|
|
1283
|
-
const entry = this.checkRights(fd, Rights.FD_WRITE);
|
|
1284
|
-
const buffers = this.readIovecs(iovsPtr, iovsLen);
|
|
1285
|
-
|
|
1286
|
-
let totalWritten = 0;
|
|
1287
|
-
for (const buf of buffers) {
|
|
1288
|
-
if (buf.length === 0) continue;
|
|
1289
|
-
const position = entry.offset !== undefined ? Number(entry.offset) : null;
|
|
1290
|
-
const bytesWritten = fs.writeSync(entry.fd, buf, 0, buf.length, position);
|
|
1291
|
-
totalWritten += bytesWritten;
|
|
1292
|
-
if (entry.offset !== undefined) {
|
|
1293
|
-
entry.offset += BigInt(bytesWritten);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
const v = this.#view();
|
|
1298
|
-
v.setUint32(nwrittenPtr, totalWritten, true);
|
|
1299
|
-
return Errno.SUCCESS;
|
|
1300
|
-
}),
|
|
1301
|
-
|
|
1302
|
-
// =========================================================================
|
|
1303
|
-
// Path Operations
|
|
1304
|
-
// =========================================================================
|
|
1305
|
-
|
|
1306
|
-
path_create_directory: wrap((fd: number, pathPtr: number, pathLen: number): number => {
|
|
1307
|
-
this.checkRights(fd, Rights.PATH_CREATE_DIRECTORY);
|
|
1308
|
-
const fullPath = this.resolvePath(fd, pathPtr, pathLen);
|
|
1309
|
-
fs.mkdirSync(fullPath);
|
|
1310
|
-
return Errno.SUCCESS;
|
|
1311
|
-
}),
|
|
1312
|
-
|
|
1313
|
-
path_filestat_get: wrap(
|
|
1314
|
-
(fd: number, flags: number, pathPtr: number, pathLen: number, statPtr: number): number => {
|
|
1315
|
-
const v = this.#view();
|
|
1316
|
-
this.checkRights(fd, Rights.PATH_FILESTAT_GET);
|
|
1317
|
-
const fullPath = this.resolvePath(fd, pathPtr, pathLen);
|
|
1318
|
-
|
|
1319
|
-
const stats = flags & LookupFlags.SYMLINK_FOLLOW ? fs.statSync(fullPath) : fs.lstatSync(fullPath);
|
|
1320
|
-
|
|
1321
|
-
v.setBigUint64(statPtr, BigInt(stats.dev), true);
|
|
1322
|
-
v.setBigUint64(statPtr + 8, BigInt(stats.ino), true);
|
|
1323
|
-
v.setUint8(statPtr + 16, statsToFileType(stats));
|
|
1324
|
-
v.setBigUint64(statPtr + 24, BigInt(stats.nlink), true);
|
|
1325
|
-
v.setBigUint64(statPtr + 32, BigInt(stats.size), true);
|
|
1326
|
-
v.setBigUint64(statPtr + 40, msToNs(stats.atimeMs), true);
|
|
1327
|
-
v.setBigUint64(statPtr + 48, msToNs(stats.mtimeMs), true);
|
|
1328
|
-
v.setBigUint64(statPtr + 56, msToNs(stats.ctimeMs), true);
|
|
1329
|
-
return Errno.SUCCESS;
|
|
1330
|
-
},
|
|
1331
|
-
),
|
|
1332
|
-
|
|
1333
|
-
path_filestat_set_times: wrap(
|
|
1334
|
-
(
|
|
1335
|
-
fd: number,
|
|
1336
|
-
flags: number,
|
|
1337
|
-
pathPtr: number,
|
|
1338
|
-
pathLen: number,
|
|
1339
|
-
atim: bigint,
|
|
1340
|
-
mtim: bigint,
|
|
1341
|
-
fstFlags: number,
|
|
1342
|
-
): number => {
|
|
1343
|
-
this.checkRights(fd, Rights.PATH_FILESTAT_SET_TIMES);
|
|
1344
|
-
const fullPath = this.resolvePath(fd, pathPtr, pathLen);
|
|
1345
|
-
|
|
1346
|
-
const stats = flags & LookupFlags.SYMLINK_FOLLOW ? fs.statSync(fullPath) : fs.lstatSync(fullPath);
|
|
1347
|
-
|
|
1348
|
-
let atime: Date;
|
|
1349
|
-
let mtime: Date;
|
|
1350
|
-
const now = new Date();
|
|
1351
|
-
|
|
1352
|
-
if (fstFlags & FstFlags.ATIM_NOW) {
|
|
1353
|
-
atime = now;
|
|
1354
|
-
} else if (fstFlags & FstFlags.ATIM) {
|
|
1355
|
-
atime = new Date(nsToMs(atim));
|
|
1356
|
-
} else {
|
|
1357
|
-
atime = stats.atime;
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
if (fstFlags & FstFlags.MTIM_NOW) {
|
|
1361
|
-
mtime = now;
|
|
1362
|
-
} else if (fstFlags & FstFlags.MTIM) {
|
|
1363
|
-
mtime = new Date(nsToMs(mtim));
|
|
1364
|
-
} else {
|
|
1365
|
-
mtime = stats.mtime;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
fs.utimesSync(fullPath, atime, mtime);
|
|
1369
|
-
return Errno.SUCCESS;
|
|
1370
|
-
},
|
|
1371
|
-
),
|
|
1372
|
-
|
|
1373
|
-
path_link: wrap(
|
|
1374
|
-
(
|
|
1375
|
-
oldFd: number,
|
|
1376
|
-
_oldFlags: number,
|
|
1377
|
-
oldPathPtr: number,
|
|
1378
|
-
oldPathLen: number,
|
|
1379
|
-
newFd: number,
|
|
1380
|
-
newPathPtr: number,
|
|
1381
|
-
newPathLen: number,
|
|
1382
|
-
): number => {
|
|
1383
|
-
this.checkRights(oldFd, Rights.PATH_LINK_SOURCE);
|
|
1384
|
-
this.checkRights(newFd, Rights.PATH_LINK_TARGET);
|
|
1385
|
-
const oldPath = this.resolvePath(oldFd, oldPathPtr, oldPathLen);
|
|
1386
|
-
const newPath = this.resolvePath(newFd, newPathPtr, newPathLen);
|
|
1387
|
-
fs.linkSync(oldPath, newPath);
|
|
1388
|
-
return Errno.SUCCESS;
|
|
1389
|
-
},
|
|
1390
|
-
),
|
|
1391
|
-
|
|
1392
|
-
path_open: wrap(
|
|
1393
|
-
(
|
|
1394
|
-
dirFd: number,
|
|
1395
|
-
_dirFlags: number,
|
|
1396
|
-
pathPtr: number,
|
|
1397
|
-
pathLen: number,
|
|
1398
|
-
oflags: number,
|
|
1399
|
-
fsRightsBase: bigint,
|
|
1400
|
-
fsRightsInheriting: bigint,
|
|
1401
|
-
fdFlags: number,
|
|
1402
|
-
fdPtr: number,
|
|
1403
|
-
): number => {
|
|
1404
|
-
const v = this.#view();
|
|
1405
|
-
const fullPath = this.resolvePath(dirFd, pathPtr, pathLen);
|
|
1406
|
-
|
|
1407
|
-
// Build Node.js open flags
|
|
1408
|
-
let nodeFlags = 0;
|
|
1409
|
-
|
|
1410
|
-
// Determine read/write mode from requested rights
|
|
1411
|
-
const wantsRead = (fsRightsBase & Rights.FD_READ) !== 0n;
|
|
1412
|
-
const wantsWrite =
|
|
1413
|
-
(fsRightsBase & (Rights.FD_WRITE | Rights.FD_ALLOCATE | Rights.FD_FILESTAT_SET_SIZE)) !== 0n;
|
|
1414
|
-
|
|
1415
|
-
if (wantsRead && wantsWrite) {
|
|
1416
|
-
nodeFlags |= fs.constants.O_RDWR;
|
|
1417
|
-
} else if (wantsWrite) {
|
|
1418
|
-
nodeFlags |= fs.constants.O_WRONLY;
|
|
1419
|
-
} else {
|
|
1420
|
-
nodeFlags |= fs.constants.O_RDONLY;
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
// Handle oflags
|
|
1424
|
-
if (oflags & OFlags.CREAT) {
|
|
1425
|
-
nodeFlags |= fs.constants.O_CREAT;
|
|
1426
|
-
}
|
|
1427
|
-
if (oflags & OFlags.DIRECTORY) {
|
|
1428
|
-
nodeFlags |= fs.constants.O_DIRECTORY;
|
|
1429
|
-
}
|
|
1430
|
-
if (oflags & OFlags.EXCL) {
|
|
1431
|
-
nodeFlags |= fs.constants.O_EXCL;
|
|
1432
|
-
}
|
|
1433
|
-
if (oflags & OFlags.TRUNC) {
|
|
1434
|
-
nodeFlags |= fs.constants.O_TRUNC;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
// Handle fdFlags
|
|
1438
|
-
if (fdFlags & FdFlags.APPEND) {
|
|
1439
|
-
nodeFlags |= fs.constants.O_APPEND;
|
|
1440
|
-
}
|
|
1441
|
-
if (fdFlags & FdFlags.DSYNC) {
|
|
1442
|
-
nodeFlags |= fs.constants.O_DSYNC || fs.constants.O_SYNC;
|
|
1443
|
-
}
|
|
1444
|
-
if (fdFlags & FdFlags.NONBLOCK) {
|
|
1445
|
-
nodeFlags |= fs.constants.O_NONBLOCK;
|
|
1446
|
-
}
|
|
1447
|
-
if (fdFlags & FdFlags.SYNC) {
|
|
1448
|
-
nodeFlags |= fs.constants.O_SYNC;
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
// Check if it's a directory that we need to open read-only
|
|
1452
|
-
let isDir = false;
|
|
1453
|
-
try {
|
|
1454
|
-
isDir = fs.statSync(fullPath).isDirectory();
|
|
1455
|
-
} catch {}
|
|
1456
|
-
|
|
1457
|
-
if (isDir && !wantsWrite) {
|
|
1458
|
-
nodeFlags = fs.constants.O_RDONLY | fs.constants.O_DIRECTORY;
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
|
-
const realFd = fs.openSync(fullPath, nodeFlags);
|
|
1462
|
-
const stats = fs.fstatSync(realFd);
|
|
1463
|
-
const fileType = statsToFileType(stats);
|
|
1464
|
-
const { base, inheriting } = rightsForFileType(fileType, tty.isatty(realFd));
|
|
1465
|
-
|
|
1466
|
-
const wasiFd = this.allocateFd();
|
|
1467
|
-
this.#fds.set(wasiFd, {
|
|
1468
|
-
fd: realFd,
|
|
1469
|
-
fileType,
|
|
1470
|
-
rightsBase: fsRightsBase & base,
|
|
1471
|
-
rightsInheriting: fsRightsInheriting & inheriting,
|
|
1472
|
-
offset: fileType === FileType.REGULAR_FILE ? 0n : undefined,
|
|
1473
|
-
realPath: fullPath,
|
|
1474
|
-
fdFlags,
|
|
1475
|
-
});
|
|
1476
|
-
|
|
1477
|
-
v.setUint32(fdPtr, wasiFd, true);
|
|
1478
|
-
return Errno.SUCCESS;
|
|
1479
|
-
},
|
|
1480
|
-
),
|
|
1481
|
-
|
|
1482
|
-
path_readlink: wrap(
|
|
1483
|
-
(
|
|
1484
|
-
fd: number,
|
|
1485
|
-
pathPtr: number,
|
|
1486
|
-
pathLen: number,
|
|
1487
|
-
bufPtr: number,
|
|
1488
|
-
bufLen: number,
|
|
1489
|
-
bufUsedPtr: number,
|
|
1490
|
-
): number => {
|
|
1491
|
-
const v = this.#view();
|
|
1492
|
-
this.checkRights(fd, Rights.PATH_READLINK);
|
|
1493
|
-
const fullPath = this.resolvePath(fd, pathPtr, pathLen);
|
|
1494
|
-
const target = fs.readlinkSync(fullPath);
|
|
1495
|
-
const targetBytes = new TextEncoder().encode(target);
|
|
1496
|
-
const len = Math.min(targetBytes.length, bufLen);
|
|
1497
|
-
this.#mem(bufPtr, len).set(targetBytes.subarray(0, len));
|
|
1498
|
-
v.setUint32(bufUsedPtr, len, true);
|
|
1499
|
-
return Errno.SUCCESS;
|
|
1500
|
-
},
|
|
1501
|
-
),
|
|
1502
|
-
|
|
1503
|
-
path_remove_directory: wrap((fd: number, pathPtr: number, pathLen: number): number => {
|
|
1504
|
-
this.checkRights(fd, Rights.PATH_REMOVE_DIRECTORY);
|
|
1505
|
-
const fullPath = this.resolvePath(fd, pathPtr, pathLen);
|
|
1506
|
-
fs.rmdirSync(fullPath);
|
|
1507
|
-
return Errno.SUCCESS;
|
|
1508
|
-
}),
|
|
1509
|
-
|
|
1510
|
-
path_rename: wrap(
|
|
1511
|
-
(
|
|
1512
|
-
oldFd: number,
|
|
1513
|
-
oldPathPtr: number,
|
|
1514
|
-
oldPathLen: number,
|
|
1515
|
-
newFd: number,
|
|
1516
|
-
newPathPtr: number,
|
|
1517
|
-
newPathLen: number,
|
|
1518
|
-
): number => {
|
|
1519
|
-
this.checkRights(oldFd, Rights.PATH_RENAME_SOURCE);
|
|
1520
|
-
this.checkRights(newFd, Rights.PATH_RENAME_TARGET);
|
|
1521
|
-
const oldPath = this.resolvePath(oldFd, oldPathPtr, oldPathLen);
|
|
1522
|
-
const newPath = this.resolvePath(newFd, newPathPtr, newPathLen);
|
|
1523
|
-
fs.renameSync(oldPath, newPath);
|
|
1524
|
-
return Errno.SUCCESS;
|
|
1525
|
-
},
|
|
1526
|
-
),
|
|
1527
|
-
|
|
1528
|
-
path_symlink: wrap(
|
|
1529
|
-
(oldPathPtr: number, oldPathLen: number, fd: number, newPathPtr: number, newPathLen: number): number => {
|
|
1530
|
-
this.checkRights(fd, Rights.PATH_SYMLINK);
|
|
1531
|
-
const oldPath = readString(this.#view(), oldPathPtr, oldPathLen);
|
|
1532
|
-
const newPath = this.resolvePath(fd, newPathPtr, newPathLen);
|
|
1533
|
-
fs.symlinkSync(oldPath, newPath);
|
|
1534
|
-
return Errno.SUCCESS;
|
|
1535
|
-
},
|
|
1536
|
-
),
|
|
1537
|
-
|
|
1538
|
-
path_unlink_file: wrap((fd: number, pathPtr: number, pathLen: number): number => {
|
|
1539
|
-
this.checkRights(fd, Rights.PATH_UNLINK_FILE);
|
|
1540
|
-
const fullPath = this.resolvePath(fd, pathPtr, pathLen);
|
|
1541
|
-
fs.unlinkSync(fullPath);
|
|
1542
|
-
return Errno.SUCCESS;
|
|
1543
|
-
}),
|
|
1544
|
-
|
|
1545
|
-
// =========================================================================
|
|
1546
|
-
// Polling
|
|
1547
|
-
// =========================================================================
|
|
1548
|
-
|
|
1549
|
-
poll_oneoff: wrap((inPtr: number, outPtr: number, nsubscriptions: number, neventsPtr: number): number => {
|
|
1550
|
-
const v = this.#view();
|
|
1551
|
-
|
|
1552
|
-
let nevents = 0;
|
|
1553
|
-
let maxWaitNs = 0n;
|
|
1554
|
-
|
|
1555
|
-
for (let i = 0; i < nsubscriptions; i++) {
|
|
1556
|
-
const subPtr = inPtr + i * 48;
|
|
1557
|
-
const userdata = v.getBigUint64(subPtr, true);
|
|
1558
|
-
const tag = v.getUint8(subPtr + 8);
|
|
1559
|
-
|
|
1560
|
-
const eventPtr = outPtr + nevents * 32;
|
|
1561
|
-
|
|
1562
|
-
switch (tag) {
|
|
1563
|
-
case EventType.CLOCK: {
|
|
1564
|
-
const clockId = v.getUint32(subPtr + 16, true);
|
|
1565
|
-
const timeout = v.getBigUint64(subPtr + 24, true);
|
|
1566
|
-
const _precision = v.getBigUint64(subPtr + 32, true);
|
|
1567
|
-
const flags = v.getUint16(subPtr + 40, true);
|
|
1568
|
-
|
|
1569
|
-
const isAbsolute = (flags & SubClockFlags.ABSTIME) !== 0;
|
|
1570
|
-
const now = clockTimeNs(clockId);
|
|
1571
|
-
|
|
1572
|
-
if (now === null) {
|
|
1573
|
-
v.setBigUint64(eventPtr, userdata, true);
|
|
1574
|
-
v.setUint16(eventPtr + 8, Errno.EINVAL, true);
|
|
1575
|
-
v.setUint8(eventPtr + 10, EventType.CLOCK);
|
|
1576
|
-
} else {
|
|
1577
|
-
let waitNs = isAbsolute ? timeout - now : timeout;
|
|
1578
|
-
if (waitNs < 0n) waitNs = 0n;
|
|
1579
|
-
if (waitNs > maxWaitNs) maxWaitNs = waitNs;
|
|
1580
|
-
|
|
1581
|
-
v.setBigUint64(eventPtr, userdata, true);
|
|
1582
|
-
v.setUint16(eventPtr + 8, Errno.SUCCESS, true);
|
|
1583
|
-
v.setUint8(eventPtr + 10, EventType.CLOCK);
|
|
1584
|
-
}
|
|
1585
|
-
nevents++;
|
|
1586
|
-
break;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
case EventType.FD_READ:
|
|
1590
|
-
case EventType.FD_WRITE: {
|
|
1591
|
-
const _fd = v.getUint32(subPtr + 16, true);
|
|
1592
|
-
|
|
1593
|
-
// For simplicity, we just return immediately with success
|
|
1594
|
-
// A full implementation would use select/poll/epoll
|
|
1595
|
-
v.setBigUint64(eventPtr, userdata, true);
|
|
1596
|
-
v.setUint16(eventPtr + 8, Errno.SUCCESS, true);
|
|
1597
|
-
v.setUint8(eventPtr + 10, tag);
|
|
1598
|
-
// nbytes and flags at eventPtr + 16 and eventPtr + 24
|
|
1599
|
-
v.setBigUint64(eventPtr + 16, 0n, true);
|
|
1600
|
-
v.setUint16(eventPtr + 24, 0, true);
|
|
1601
|
-
nevents++;
|
|
1602
|
-
break;
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
default:
|
|
1606
|
-
return Errno.EINVAL;
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
// Sleep if there's a timeout
|
|
1611
|
-
if (maxWaitNs > 0n && nevents > 0) {
|
|
1612
|
-
const sleepMs = Number(maxWaitNs / 1_000_000n);
|
|
1613
|
-
if (sleepMs > 0) {
|
|
1614
|
-
// Use Bun.sleepSync if available, otherwise busy-wait (not ideal)
|
|
1615
|
-
if (typeof Bun !== "undefined" && Bun.sleepSync) {
|
|
1616
|
-
Bun.sleepSync(sleepMs);
|
|
1617
|
-
} else {
|
|
1618
|
-
const end = Date.now() + sleepMs;
|
|
1619
|
-
while (Date.now() < end) {
|
|
1620
|
-
// Busy wait - not ideal but works
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
v.setUint32(neventsPtr, nevents, true);
|
|
1627
|
-
return Errno.SUCCESS;
|
|
1628
|
-
}),
|
|
1629
|
-
|
|
1630
|
-
// =========================================================================
|
|
1631
|
-
// Process
|
|
1632
|
-
// =========================================================================
|
|
1633
|
-
|
|
1634
|
-
proc_exit: (code: number): never => {
|
|
1635
|
-
throw new WASIExitError(code);
|
|
1636
|
-
},
|
|
1637
|
-
|
|
1638
|
-
proc_raise: wrap((sig: number): number => {
|
|
1639
|
-
const signal = SIGNAL_MAP[sig];
|
|
1640
|
-
if (!signal) {
|
|
1641
|
-
return Errno.EINVAL;
|
|
1642
|
-
}
|
|
1643
|
-
process.kill(process.pid, signal);
|
|
1644
|
-
return Errno.SUCCESS;
|
|
1645
|
-
}),
|
|
1646
|
-
|
|
1647
|
-
// =========================================================================
|
|
1648
|
-
// Random
|
|
1649
|
-
// =========================================================================
|
|
1650
|
-
|
|
1651
|
-
random_get: wrap((bufPtr: number, bufLen: number): number => {
|
|
1652
|
-
const buf = this.#mem(bufPtr, bufLen);
|
|
1653
|
-
crypto.getRandomValues(buf);
|
|
1654
|
-
return Errno.SUCCESS;
|
|
1655
|
-
}),
|
|
1656
|
-
|
|
1657
|
-
// =========================================================================
|
|
1658
|
-
// Scheduler
|
|
1659
|
-
// =========================================================================
|
|
1660
|
-
|
|
1661
|
-
sched_yield: wrap((): number => {
|
|
1662
|
-
// No-op in single-threaded JS
|
|
1663
|
-
return Errno.SUCCESS;
|
|
1664
|
-
}),
|
|
1665
|
-
|
|
1666
|
-
// =========================================================================
|
|
1667
|
-
// Sockets (stubs - return ENOSYS)
|
|
1668
|
-
// =========================================================================
|
|
1669
|
-
|
|
1670
|
-
sock_accept: wrap((_fd: number, _flags: number, _fdPtr: number): number => {
|
|
1671
|
-
return Errno.ENOSYS;
|
|
1672
|
-
}),
|
|
1673
|
-
|
|
1674
|
-
sock_recv: wrap(
|
|
1675
|
-
(
|
|
1676
|
-
_fd: number,
|
|
1677
|
-
_iovs: number,
|
|
1678
|
-
_iovsLen: number,
|
|
1679
|
-
_flags: number,
|
|
1680
|
-
_nreadPtr: number,
|
|
1681
|
-
_flagsPtr: number,
|
|
1682
|
-
): number => {
|
|
1683
|
-
return Errno.ENOSYS;
|
|
1684
|
-
},
|
|
1685
|
-
),
|
|
1686
|
-
|
|
1687
|
-
sock_send: wrap(
|
|
1688
|
-
(_fd: number, _iovs: number, _iovsLen: number, _flags: number, _nwrittenPtr: number): number => {
|
|
1689
|
-
return Errno.ENOSYS;
|
|
1690
|
-
},
|
|
1691
|
-
),
|
|
1692
|
-
|
|
1693
|
-
sock_shutdown: wrap((_fd: number, _how: number): number => {
|
|
1694
|
-
return Errno.ENOSYS;
|
|
1695
|
-
}),
|
|
1696
|
-
};
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
/** Get imports for a WebAssembly module */
|
|
1700
|
-
getImportObject() {
|
|
1701
|
-
return {
|
|
1702
|
-
wasi_snapshot_preview1: this.#wasiImport,
|
|
1703
|
-
wasi_unstable: this.#wasiImport,
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
/** Initialize WASI with a WebAssembly instance */
|
|
1708
|
-
initialize(instance: WebAssembly.Instance): void {
|
|
1709
|
-
const exports = instance.exports;
|
|
1710
|
-
if (exports.memory instanceof WebAssembly.Memory) {
|
|
1711
|
-
this.#memory = exports.memory;
|
|
1712
|
-
} else {
|
|
1713
|
-
throw new Error("WebAssembly instance must export memory");
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
/** Start the WASI program */
|
|
1718
|
-
start(instance: WebAssembly.Instance): number {
|
|
1719
|
-
this.initialize(instance);
|
|
1720
|
-
const exports = instance.exports;
|
|
1721
|
-
|
|
1722
|
-
if (typeof exports._start === "function") {
|
|
1723
|
-
try {
|
|
1724
|
-
(exports._start as () => void)();
|
|
1725
|
-
return 0;
|
|
1726
|
-
} catch (err) {
|
|
1727
|
-
if (err instanceof WASIExitError) {
|
|
1728
|
-
return err.code;
|
|
1729
|
-
}
|
|
1730
|
-
throw err;
|
|
1731
|
-
}
|
|
1732
|
-
} else if (typeof exports._initialize === "function") {
|
|
1733
|
-
(exports._initialize as () => void)();
|
|
1734
|
-
return 0;
|
|
1735
|
-
} else {
|
|
1736
|
-
throw new Error("WebAssembly instance must export _start or _initialize");
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
// =============================================================================
|
|
1742
|
-
// Default Export
|
|
1743
|
-
// =============================================================================
|
|
1744
|
-
|
|
1745
|
-
export default WASI1;
|