@tybys/wasm-util 0.4.0 → 0.5.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.
@@ -1,11 +1,12 @@
1
1
  import { _WebAssembly } from "../webassembly.mjs";
2
2
  import { resolve } from "./path.mjs";
3
3
  import { WasiErrno, WasiRights, FileControlFlag, WasiFileControlFlag, WasiFdFlag, WasiFileType, WasiClockid, WasiFstFlag } from "./types.mjs";
4
- import { FileDescriptorTable, concatBuffer, toFileStat } from "./fd.mjs";
4
+ import { concatBuffer, toFileStat, AsyncTable, SyncTable } from "./fd.mjs";
5
5
  import { WasiError } from "./error.mjs";
6
6
  import { isPromiseLike } from "./util.mjs";
7
7
  import { getRights } from "./rights.mjs";
8
8
  import { extendMemory } from "../memory.mjs";
9
+ import { wrapAsyncImport } from "../jspi.mjs";
9
10
  function copyMemory(targets, src) {
10
11
  if (targets.length === 0 || src.length === 0)
11
12
  return 0;
@@ -64,7 +65,7 @@ function defineName(name, f) {
64
65
  Object.defineProperty(f, 'name', { value: name });
65
66
  return f;
66
67
  }
67
- function syscallWrap(name, f) {
68
+ function syscallWrap(self, name, f) {
68
69
  return defineName(name, function () {
69
70
  if (process.env.NODE_DEBUG_NATIVE === 'wasi') {
70
71
  const args = Array.prototype.slice.call(arguments);
@@ -74,7 +75,7 @@ function syscallWrap(name, f) {
74
75
  }
75
76
  let r;
76
77
  try {
77
- r = f.apply(this, arguments);
78
+ r = f.apply(self, arguments);
78
79
  }
79
80
  catch (err) {
80
81
  return handleError(err);
@@ -85,7 +86,7 @@ function syscallWrap(name, f) {
85
86
  return r;
86
87
  });
87
88
  }
88
- function resolvePath(fs, fileDescriptor, path, flags) {
89
+ function resolvePathSync(fs, fileDescriptor, path, flags) {
89
90
  let resolvedPath = resolve(fileDescriptor.realPath, path);
90
91
  if ((flags & 1) === 1) {
91
92
  try {
@@ -99,6 +100,20 @@ function resolvePath(fs, fileDescriptor, path, flags) {
99
100
  }
100
101
  return resolvedPath;
101
102
  }
103
+ async function resolvePathAsync(fs, fileDescriptor, path, flags) {
104
+ let resolvedPath = resolve(fileDescriptor.realPath, path);
105
+ if ((flags & 1) === 1) {
106
+ try {
107
+ resolvedPath = await fs.promises.readlink(resolvedPath);
108
+ }
109
+ catch (err) {
110
+ if (err.code !== 'EINVAL' && err.code !== 'ENOENT') {
111
+ throw err;
112
+ }
113
+ }
114
+ }
115
+ return resolvedPath;
116
+ }
102
117
  const encoder = new TextEncoder();
103
118
  const decoder = new TextDecoder();
104
119
  function readStdin() {
@@ -109,14 +124,8 @@ function readStdin() {
109
124
  return buffer;
110
125
  }
111
126
  export class WASI {
112
- constructor(args, env, preopens, stdio, filesystem, print, printErr) {
113
- this._setMemory = function _setMemory(m) {
114
- if (!(m instanceof _WebAssembly.Memory)) {
115
- throw new TypeError('"instance.exports.memory" property must be a WebAssembly.Memory');
116
- }
117
- _memory.set(this, extendMemory(m));
118
- };
119
- this.args_get = syscallWrap('args_get', function (argv, argv_buf) {
127
+ constructor(args, env, fds, asyncFs, fs, asyncify) {
128
+ this.args_get = syscallWrap(this, 'args_get', function (argv, argv_buf) {
120
129
  argv = Number(argv);
121
130
  argv_buf = Number(argv_buf);
122
131
  if (argv === 0 || argv_buf === 0) {
@@ -135,7 +144,7 @@ export class WASI {
135
144
  }
136
145
  return WasiErrno.ESUCCESS;
137
146
  });
138
- this.args_sizes_get = syscallWrap('args_sizes_get', function (argc, argv_buf_size) {
147
+ this.args_sizes_get = syscallWrap(this, 'args_sizes_get', function (argc, argv_buf_size) {
139
148
  argc = Number(argc);
140
149
  argv_buf_size = Number(argv_buf_size);
141
150
  if (argc === 0 || argv_buf_size === 0) {
@@ -148,7 +157,7 @@ export class WASI {
148
157
  view.setUint32(argv_buf_size, encoder.encode(args.join('\0') + '\0').length, true);
149
158
  return WasiErrno.ESUCCESS;
150
159
  });
151
- this.environ_get = syscallWrap('environ_get', function (environ, environ_buf) {
160
+ this.environ_get = syscallWrap(this, 'environ_get', function (environ, environ_buf) {
152
161
  environ = Number(environ);
153
162
  environ_buf = Number(environ_buf);
154
163
  if (environ === 0 || environ_buf === 0) {
@@ -167,7 +176,7 @@ export class WASI {
167
176
  }
168
177
  return WasiErrno.ESUCCESS;
169
178
  });
170
- this.environ_sizes_get = syscallWrap('environ_sizes_get', function (len, buflen) {
179
+ this.environ_sizes_get = syscallWrap(this, 'environ_sizes_get', function (len, buflen) {
171
180
  len = Number(len);
172
181
  buflen = Number(buflen);
173
182
  if (len === 0 || buflen === 0) {
@@ -179,7 +188,7 @@ export class WASI {
179
188
  view.setUint32(buflen, encoder.encode(wasi.env.join('\0') + '\0').length, true);
180
189
  return WasiErrno.ESUCCESS;
181
190
  });
182
- this.clock_res_get = syscallWrap('clock_res_get', function (id, resolution) {
191
+ this.clock_res_get = syscallWrap(this, 'clock_res_get', function (id, resolution) {
183
192
  resolution = Number(resolution);
184
193
  if (resolution === 0) {
185
194
  return WasiErrno.EINVAL;
@@ -197,7 +206,7 @@ export class WASI {
197
206
  default: return WasiErrno.EINVAL;
198
207
  }
199
208
  });
200
- this.clock_time_get = syscallWrap('clock_time_get', function (id, _percision, time) {
209
+ this.clock_time_get = syscallWrap(this, 'clock_time_get', function (id, _percision, time) {
201
210
  time = Number(time);
202
211
  if (time === 0) {
203
212
  return WasiErrno.EINVAL;
@@ -220,35 +229,10 @@ export class WASI {
220
229
  default: return WasiErrno.EINVAL;
221
230
  }
222
231
  });
223
- this.fd_advise = syscallWrap('fd_advise', function (_fd, _offset, _len, _advice) {
232
+ this.fd_advise = syscallWrap(this, 'fd_advise', function (_fd, _offset, _len, _advice) {
224
233
  return WasiErrno.ENOSYS;
225
234
  });
226
- this.fd_allocate = syscallWrap('fd_allocate', function (fd, offset, len) {
227
- const wasi = _wasi.get(this);
228
- const fs = getFs(this);
229
- const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_ALLOCATE, BigInt(0));
230
- const stat = fs.fstatSync(fileDescriptor.fd, { bigint: true });
231
- if (stat.size < offset + len) {
232
- fs.ftruncateSync(fileDescriptor.fd, Number(offset + len));
233
- }
234
- return WasiErrno.ESUCCESS;
235
- });
236
- this.fd_close = syscallWrap('fd_close', function (fd) {
237
- const wasi = _wasi.get(this);
238
- const fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
239
- const fs = getFs(this);
240
- fs.closeSync(fileDescriptor.fd);
241
- wasi.fds.remove(fd);
242
- return WasiErrno.ESUCCESS;
243
- });
244
- this.fd_datasync = syscallWrap('fd_datasync', function (fd) {
245
- const wasi = _wasi.get(this);
246
- const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_DATASYNC, BigInt(0));
247
- const fs = getFs(this);
248
- fs.fdatasyncSync(fileDescriptor.fd);
249
- return WasiErrno.ESUCCESS;
250
- });
251
- this.fd_fdstat_get = syscallWrap('fd_fdstat_get', function (fd, fdstat) {
235
+ this.fd_fdstat_get = syscallWrap(this, 'fd_fdstat_get', function (fd, fdstat) {
252
236
  fdstat = Number(fdstat);
253
237
  if (fdstat === 0) {
254
238
  return WasiErrno.EINVAL;
@@ -262,10 +246,10 @@ export class WASI {
262
246
  view.setBigUint64(fdstat + 16, fileDescriptor.rightsInheriting, true);
263
247
  return WasiErrno.ESUCCESS;
264
248
  });
265
- this.fd_fdstat_set_flags = syscallWrap('fd_fdstat_set_flags', function (_fd, _flags) {
249
+ this.fd_fdstat_set_flags = syscallWrap(this, 'fd_fdstat_set_flags', function (_fd, _flags) {
266
250
  return WasiErrno.ENOSYS;
267
251
  });
268
- this.fd_fdstat_set_rights = syscallWrap('fd_fdstat_set_rights', function (fd, rightsBase, rightsInheriting) {
252
+ this.fd_fdstat_set_rights = syscallWrap(this, 'fd_fdstat_set_rights', function (fd, rightsBase, rightsInheriting) {
269
253
  const wasi = _wasi.get(this);
270
254
  const fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
271
255
  if ((rightsBase | fileDescriptor.rightsBase) > fileDescriptor.rightsBase) {
@@ -279,7 +263,186 @@ export class WASI {
279
263
  fileDescriptor.rightsInheriting = rightsInheriting;
280
264
  return WasiErrno.ESUCCESS;
281
265
  });
282
- this.fd_filestat_get = syscallWrap('fd_filestat_get', function (fd, buf) {
266
+ this.fd_prestat_get = syscallWrap(this, 'fd_prestat_get', function (fd, prestat) {
267
+ prestat = Number(prestat);
268
+ if (prestat === 0) {
269
+ return WasiErrno.EINVAL;
270
+ }
271
+ const wasi = _wasi.get(this);
272
+ let fileDescriptor;
273
+ try {
274
+ fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
275
+ }
276
+ catch (err) {
277
+ if (err instanceof WasiError)
278
+ return err.errno;
279
+ throw err;
280
+ }
281
+ if (fileDescriptor.preopen !== 1)
282
+ return WasiErrno.EINVAL;
283
+ const { view } = getMemory(this);
284
+ // preopen type is dir(0)
285
+ view.setUint32(prestat, 0, true);
286
+ view.setUint32(prestat + 4, encoder.encode(fileDescriptor.path).length + 1, true);
287
+ return WasiErrno.ESUCCESS;
288
+ });
289
+ this.fd_prestat_dir_name = syscallWrap(this, 'fd_prestat_dir_name', function (fd, path, path_len) {
290
+ path = Number(path);
291
+ path_len = Number(path_len);
292
+ if (path === 0) {
293
+ return WasiErrno.EINVAL;
294
+ }
295
+ const wasi = _wasi.get(this);
296
+ const fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
297
+ if (fileDescriptor.preopen !== 1)
298
+ return WasiErrno.EBADF;
299
+ const buffer = encoder.encode(fileDescriptor.path + '\0');
300
+ const size = buffer.length;
301
+ if (size > path_len)
302
+ return WasiErrno.ENOBUFS;
303
+ const { HEAPU8 } = getMemory(this);
304
+ HEAPU8.set(buffer, path);
305
+ return WasiErrno.ESUCCESS;
306
+ });
307
+ this.fd_seek = syscallWrap(this, 'fd_seek', function (fd, offset, whence, newOffset) {
308
+ newOffset = Number(newOffset);
309
+ if (newOffset === 0) {
310
+ return WasiErrno.EINVAL;
311
+ }
312
+ if (fd === 0 || fd === 1 || fd === 2)
313
+ return WasiErrno.ESUCCESS;
314
+ const wasi = _wasi.get(this);
315
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_SEEK, BigInt(0));
316
+ const r = fileDescriptor.seek(offset, whence);
317
+ const { view } = getMemory(this);
318
+ view.setBigUint64(newOffset, r, true);
319
+ return WasiErrno.ESUCCESS;
320
+ });
321
+ this.fd_tell = syscallWrap(this, 'fd_tell', function (fd, offset) {
322
+ const wasi = _wasi.get(this);
323
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_TELL, BigInt(0));
324
+ const pos = BigInt(fileDescriptor.pos);
325
+ const { view } = getMemory(this);
326
+ view.setBigUint64(Number(offset), pos, true);
327
+ return WasiErrno.ESUCCESS;
328
+ });
329
+ this.poll_oneoff = syscallWrap(this, 'poll_oneoff', function (_in, _out, _sub, _nevents) {
330
+ return WasiErrno.ENOSYS;
331
+ });
332
+ this.proc_exit = syscallWrap(this, 'proc_exit', function (_rval) {
333
+ return WasiErrno.ESUCCESS;
334
+ });
335
+ this.proc_raise = syscallWrap(this, 'proc_raise', function (_sig) {
336
+ return WasiErrno.ENOSYS;
337
+ });
338
+ this.sched_yield = syscallWrap(this, 'sched_yield', function () {
339
+ return WasiErrno.ESUCCESS;
340
+ });
341
+ this.random_get = typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function'
342
+ ? syscallWrap(this, 'random_get', function (buf, buf_len) {
343
+ buf = Number(buf);
344
+ if (buf === 0) {
345
+ return WasiErrno.EINVAL;
346
+ }
347
+ buf_len = Number(buf_len);
348
+ const { HEAPU8 } = getMemory(this);
349
+ let pos;
350
+ const stride = 65536;
351
+ for (pos = 0; pos + stride < buf_len; pos += stride) {
352
+ crypto.getRandomValues(HEAPU8.subarray(buf + pos, buf + pos + stride));
353
+ }
354
+ crypto.getRandomValues(HEAPU8.subarray(buf + pos, buf + buf_len));
355
+ return WasiErrno.ESUCCESS;
356
+ })
357
+ : syscallWrap(this, 'random_get', function (buf, buf_len) {
358
+ buf = Number(buf);
359
+ if (buf === 0) {
360
+ return WasiErrno.EINVAL;
361
+ }
362
+ buf_len = Number(buf_len);
363
+ const { view } = getMemory(this);
364
+ for (let i = buf; i < buf + buf_len; ++i) {
365
+ view.setUint8(i, Math.floor(Math.random() * 256));
366
+ }
367
+ return WasiErrno.ESUCCESS;
368
+ });
369
+ this.sock_recv = syscallWrap(this, 'sock_recv', function () {
370
+ return WasiErrno.ENOTSUP;
371
+ });
372
+ this.sock_send = syscallWrap(this, 'sock_send', function () {
373
+ return WasiErrno.ENOTSUP;
374
+ });
375
+ this.sock_shutdown = syscallWrap(this, 'sock_shutdown', function () {
376
+ return WasiErrno.ENOTSUP;
377
+ });
378
+ _wasi.set(this, {
379
+ fds,
380
+ args,
381
+ env
382
+ });
383
+ if (fs)
384
+ _fs.set(this, fs);
385
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
386
+ const _this = this;
387
+ function defineImport(name, syncVersion, asyncVersion, parameterType, returnType) {
388
+ if (asyncFs) {
389
+ if (asyncify) {
390
+ _this[name] = asyncify.wrapImportFunction(syscallWrap(_this, name, asyncVersion));
391
+ }
392
+ else {
393
+ _this[name] = wrapAsyncImport(syscallWrap(_this, name, asyncVersion), parameterType, returnType);
394
+ }
395
+ }
396
+ else {
397
+ _this[name] = syscallWrap(_this, name, syncVersion);
398
+ }
399
+ }
400
+ defineImport('fd_allocate', function fd_allocate(fd, offset, len) {
401
+ const wasi = _wasi.get(this);
402
+ const fs = getFs(this);
403
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_ALLOCATE, BigInt(0));
404
+ const stat = fs.fstatSync(fileDescriptor.fd, { bigint: true });
405
+ if (stat.size < offset + len) {
406
+ fs.ftruncateSync(fileDescriptor.fd, Number(offset + len));
407
+ }
408
+ return WasiErrno.ESUCCESS;
409
+ }, async function fd_allocate(fd, offset, len) {
410
+ const wasi = _wasi.get(this);
411
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_ALLOCATE, BigInt(0));
412
+ const h = fileDescriptor.fd;
413
+ const stat = await h.stat({ bigint: true });
414
+ if (stat.size < offset + len) {
415
+ await h.truncate(Number(offset + len));
416
+ }
417
+ return WasiErrno.ESUCCESS;
418
+ }, ['i32', 'i64', 'f64'], ['i32']);
419
+ defineImport('fd_close', function fd_close(fd) {
420
+ const wasi = _wasi.get(this);
421
+ const fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
422
+ const fs = getFs(this);
423
+ fs.closeSync(fileDescriptor.fd);
424
+ wasi.fds.remove(fd);
425
+ return WasiErrno.ESUCCESS;
426
+ }, async function fd_close(fd) {
427
+ const wasi = _wasi.get(this);
428
+ const fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
429
+ await fileDescriptor.fd.close();
430
+ wasi.fds.remove(fd);
431
+ return WasiErrno.ESUCCESS;
432
+ }, ['i32'], ['i32']);
433
+ defineImport('fd_datasync', function fd_datasync(fd) {
434
+ const wasi = _wasi.get(this);
435
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_DATASYNC, BigInt(0));
436
+ const fs = getFs(this);
437
+ fs.fdatasyncSync(fileDescriptor.fd);
438
+ return WasiErrno.ESUCCESS;
439
+ }, async function fd_datasync(fd) {
440
+ const wasi = _wasi.get(this);
441
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_DATASYNC, BigInt(0));
442
+ await fileDescriptor.fd.datasync();
443
+ return WasiErrno.ESUCCESS;
444
+ }, ['i32'], ['i32']);
445
+ defineImport('fd_filestat_get', function fd_filestat_get(fd, buf) {
283
446
  buf = Number(buf);
284
447
  if (buf === 0)
285
448
  return WasiErrno.EINVAL;
@@ -290,15 +453,32 @@ export class WASI {
290
453
  const { view } = getMemory(this);
291
454
  toFileStat(view, buf, stat);
292
455
  return WasiErrno.ESUCCESS;
293
- });
294
- this.fd_filestat_set_size = syscallWrap('fd_filestat_set_size', function (fd, size) {
456
+ }, async function fd_filestat_get(fd, buf) {
457
+ buf = Number(buf);
458
+ if (buf === 0)
459
+ return WasiErrno.EINVAL;
460
+ const wasi = _wasi.get(this);
461
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_FILESTAT_GET, BigInt(0));
462
+ const h = fileDescriptor.fd;
463
+ const stat = await h.stat({ bigint: true });
464
+ const { view } = getMemory(this);
465
+ toFileStat(view, buf, stat);
466
+ return WasiErrno.ESUCCESS;
467
+ }, ['i32', 'i32'], ['i32']);
468
+ defineImport('fd_filestat_set_size', function fd_filestat_set_size(fd, size) {
295
469
  const wasi = _wasi.get(this);
296
470
  const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_FILESTAT_SET_SIZE, BigInt(0));
297
471
  const fs = getFs(this);
298
472
  fs.ftruncateSync(fileDescriptor.fd, Number(size));
299
473
  return WasiErrno.ESUCCESS;
300
- });
301
- this.fd_filestat_set_times = syscallWrap('fd_filestat_set_times', function (fd, atim, mtim, flags) {
474
+ }, async function fd_filestat_set_size(fd, size) {
475
+ const wasi = _wasi.get(this);
476
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_FILESTAT_SET_SIZE, BigInt(0));
477
+ const h = fileDescriptor.fd;
478
+ await h.truncate(Number(size));
479
+ return WasiErrno.ESUCCESS;
480
+ }, ['i32', 'i64'], ['i32']);
481
+ function fdFilestatGetTimes(fd, atim, mtim, flags) {
302
482
  const wasi = _wasi.get(this);
303
483
  const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_FILESTAT_SET_TIMES, BigInt(0));
304
484
  if ((flags & WasiFstFlag.SET_ATIM_NOW) === WasiFstFlag.SET_ATIM_NOW) {
@@ -307,11 +487,20 @@ export class WASI {
307
487
  if ((flags & WasiFstFlag.SET_MTIM_NOW) === WasiFstFlag.SET_MTIM_NOW) {
308
488
  mtim = BigInt(Date.now() * 1000000);
309
489
  }
490
+ return { fileDescriptor, atim, mtim };
491
+ }
492
+ defineImport('fd_filestat_set_times', function fd_filestat_set_times(fd, atim, mtim, flags) {
493
+ const { fileDescriptor, atim: atimRes, mtim: mtimRes } = fdFilestatGetTimes.call(this, fd, atim, mtim, flags);
310
494
  const fs = getFs(this);
311
- fs.futimesSync(fileDescriptor.fd, Number(atim), Number(mtim));
495
+ fs.futimesSync(fileDescriptor.fd, Number(atimRes), Number(mtimRes));
312
496
  return WasiErrno.ESUCCESS;
313
- });
314
- this.fd_pread = syscallWrap('fd_pread', function (fd, iovs, iovslen, offset, size) {
497
+ }, async function fd_filestat_set_times(fd, atim, mtim, flags) {
498
+ const { fileDescriptor, atim: atimRes, mtim: mtimRes } = fdFilestatGetTimes.call(this, fd, atim, mtim, flags);
499
+ const h = fileDescriptor.fd;
500
+ await h.utimes(Number(atimRes), Number(mtimRes));
501
+ return WasiErrno.ESUCCESS;
502
+ }, ['i32', 'i64', 'i64', 'i32'], ['i32']);
503
+ defineImport('fd_pread', function fd_pread(fd, iovs, iovslen, offset, size) {
315
504
  iovs = Number(iovs);
316
505
  size = Number(size);
317
506
  if (iovs === 0 || size === 0) {
@@ -336,49 +525,51 @@ export class WASI {
336
525
  nread = buffer ? copyMemory(ioVecs, buffer.subarray(0, bytesRead)) : 0;
337
526
  view.setUint32(size, nread, true);
338
527
  return WasiErrno.ESUCCESS;
339
- });
340
- this.fd_prestat_get = syscallWrap('fd_prestat_get', function (fd, prestat) {
341
- prestat = Number(prestat);
342
- if (prestat === 0) {
528
+ }, async function (fd, iovs, iovslen, offset, size) {
529
+ iovs = Number(iovs);
530
+ size = Number(size);
531
+ if (iovs === 0 || size === 0) {
343
532
  return WasiErrno.EINVAL;
344
533
  }
534
+ const { HEAPU8, view } = getMemory(this);
345
535
  const wasi = _wasi.get(this);
346
- let fileDescriptor;
347
- try {
348
- fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
349
- }
350
- catch (err) {
351
- if (err instanceof WasiError)
352
- return err.errno;
353
- throw err;
354
- }
355
- if (fileDescriptor.preopen !== 1)
356
- return WasiErrno.EINVAL;
357
- const { view } = getMemory(this);
358
- // preopen type is dir(0)
359
- view.setUint32(prestat, 0, true);
360
- view.setUint32(prestat + 4, encoder.encode(fileDescriptor.path).length + 1, true);
536
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_READ | WasiRights.FD_SEEK, BigInt(0));
537
+ let totalSize = 0;
538
+ const ioVecs = Array.from({ length: Number(iovslen) }, (_, i) => {
539
+ const offset = iovs + (i * 8);
540
+ const buf = view.getInt32(offset, true);
541
+ const bufLen = view.getUint32(offset + 4, true);
542
+ totalSize += bufLen;
543
+ return HEAPU8.subarray(buf, buf + bufLen);
544
+ });
545
+ let nread = 0;
546
+ const buffer = new Uint8Array(totalSize);
547
+ buffer._isBuffer = true;
548
+ const { bytesRead } = await fileDescriptor.fd.read(buffer, 0, buffer.length, Number(offset));
549
+ nread = buffer ? copyMemory(ioVecs, buffer.subarray(0, bytesRead)) : 0;
550
+ view.setUint32(size, nread, true);
361
551
  return WasiErrno.ESUCCESS;
362
- });
363
- this.fd_prestat_dir_name = syscallWrap('fd_prestat_dir_name', function (fd, path, path_len) {
364
- path = Number(path);
365
- path_len = Number(path_len);
366
- if (path === 0) {
552
+ }, ['i32', 'i32', 'i32', 'i64', 'i32'], ['i32']);
553
+ defineImport('fd_pwrite', function fd_pwrite(fd, iovs, iovslen, offset, size) {
554
+ iovs = Number(iovs);
555
+ size = Number(size);
556
+ if (iovs === 0 || size === 0) {
367
557
  return WasiErrno.EINVAL;
368
558
  }
559
+ const { HEAPU8, view } = getMemory(this);
369
560
  const wasi = _wasi.get(this);
370
- const fileDescriptor = wasi.fds.get(fd, BigInt(0), BigInt(0));
371
- if (fileDescriptor.preopen !== 1)
372
- return WasiErrno.EBADF;
373
- const buffer = encoder.encode(fileDescriptor.path + '\0');
374
- const size = buffer.length;
375
- if (size > path_len)
376
- return WasiErrno.ENOBUFS;
377
- const { HEAPU8 } = getMemory(this);
378
- HEAPU8.set(buffer, path);
561
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_WRITE | WasiRights.FD_SEEK, BigInt(0));
562
+ const buffer = concatBuffer(Array.from({ length: Number(iovslen) }, (_, i) => {
563
+ const offset = iovs + (i * 8);
564
+ const buf = view.getInt32(offset, true);
565
+ const bufLen = view.getUint32(offset + 4, true);
566
+ return HEAPU8.subarray(buf, buf + bufLen);
567
+ }));
568
+ const fs = getFs(this);
569
+ const nwritten = fs.writeSync(fileDescriptor.fd, buffer, 0, buffer.length, Number(offset));
570
+ view.setUint32(size, nwritten, true);
379
571
  return WasiErrno.ESUCCESS;
380
- });
381
- this.fd_pwrite = syscallWrap('fd_pwrite', function (fd, iovs, iovslen, offset, size) {
572
+ }, async function fd_pwrite(fd, iovs, iovslen, offset, size) {
382
573
  iovs = Number(iovs);
383
574
  size = Number(size);
384
575
  if (iovs === 0 || size === 0) {
@@ -393,12 +584,11 @@ export class WASI {
393
584
  const bufLen = view.getUint32(offset + 4, true);
394
585
  return HEAPU8.subarray(buf, buf + bufLen);
395
586
  }));
396
- const fs = getFs(this);
397
- const nwritten = fs.writeSync(fileDescriptor.fd, buffer, 0, buffer.length, Number(offset));
398
- view.setUint32(size, nwritten, true);
587
+ const { bytesWritten } = await fileDescriptor.fd.write(buffer, 0, buffer.length, Number(offset));
588
+ view.setUint32(size, bytesWritten, true);
399
589
  return WasiErrno.ESUCCESS;
400
- });
401
- this.fd_read = syscallWrap('fd_read', function (fd, iovs, iovslen, size) {
590
+ }, ['i32', 'i32', 'i32', 'i64', 'i32'], ['i32']);
591
+ defineImport('fd_read', function fd_read(fd, iovs, iovslen, size) {
402
592
  iovs = Number(iovs);
403
593
  size = Number(size);
404
594
  if (iovs === 0 || size === 0) {
@@ -434,22 +624,43 @@ export class WASI {
434
624
  }
435
625
  view.setUint32(size, nread, true);
436
626
  return WasiErrno.ESUCCESS;
437
- });
438
- this.fd_seek = syscallWrap('fd_seek', function (fd, offset, whence, newOffset) {
439
- newOffset = Number(newOffset);
440
- if (newOffset === 0) {
627
+ }, async function fd_read(fd, iovs, iovslen, size) {
628
+ iovs = Number(iovs);
629
+ size = Number(size);
630
+ if (iovs === 0 || size === 0) {
441
631
  return WasiErrno.EINVAL;
442
632
  }
443
- if (fd === 0 || fd === 1 || fd === 2)
444
- return WasiErrno.ESUCCESS;
633
+ const { HEAPU8, view } = getMemory(this);
445
634
  const wasi = _wasi.get(this);
446
- const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_SEEK, BigInt(0));
447
- const r = fileDescriptor.seek(offset, whence);
448
- const { view } = getMemory(this);
449
- view.setBigUint64(newOffset, r, true);
635
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_READ, BigInt(0));
636
+ let totalSize = 0;
637
+ const ioVecs = Array.from({ length: Number(iovslen) }, (_, i) => {
638
+ const offset = iovs + (i * 8);
639
+ const buf = view.getInt32(offset, true);
640
+ const bufLen = view.getUint32(offset + 4, true);
641
+ totalSize += bufLen;
642
+ return HEAPU8.subarray(buf, buf + bufLen);
643
+ });
644
+ let buffer;
645
+ let nread = 0;
646
+ if (fd === 0) {
647
+ if (typeof window === 'undefined' || typeof window.prompt !== 'function') {
648
+ return WasiErrno.ENOTSUP;
649
+ }
650
+ buffer = readStdin();
651
+ nread = buffer ? copyMemory(ioVecs, buffer) : 0;
652
+ }
653
+ else {
654
+ buffer = new Uint8Array(totalSize);
655
+ buffer._isBuffer = true;
656
+ const { bytesRead } = await fileDescriptor.fd.read(buffer, 0, buffer.length, Number(fileDescriptor.pos));
657
+ nread = buffer ? copyMemory(ioVecs, buffer.subarray(0, bytesRead)) : 0;
658
+ fileDescriptor.pos += BigInt(nread);
659
+ }
660
+ view.setUint32(size, nread, true);
450
661
  return WasiErrno.ESUCCESS;
451
- });
452
- this.fd_readdir = syscallWrap('fd_readdir', function (fd, buf, buf_len, cookie, bufused) {
662
+ }, ['i32', 'i32', 'i32', 'i32'], ['i32']);
663
+ defineImport('fd_readdir', function fd_readdir(fd, buf, buf_len, cookie, bufused) {
453
664
  buf = Number(buf);
454
665
  buf_len = Number(buf_len);
455
666
  bufused = Number(bufused);
@@ -499,28 +710,79 @@ export class WASI {
499
710
  }
500
711
  view.setUint32(bufused, bufferUsed, true);
501
712
  return WasiErrno.ESUCCESS;
502
- });
503
- this.fd_renumber = syscallWrap('fd_renumber', function (from, to) {
713
+ }, async function fd_readdir(fd, buf, buf_len, cookie, bufused) {
714
+ buf = Number(buf);
715
+ buf_len = Number(buf_len);
716
+ bufused = Number(bufused);
717
+ if (buf === 0 || bufused === 0)
718
+ return WasiErrno.ESUCCESS;
719
+ const wasi = _wasi.get(this);
720
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_READDIR, BigInt(0));
721
+ const fs = getFs(this);
722
+ const entries = await fs.promises.readdir(fileDescriptor.realPath, { withFileTypes: true });
723
+ const { HEAPU8, view } = getMemory(this);
724
+ let bufferUsed = 0;
725
+ for (let i = Number(cookie); i < entries.length; i++) {
726
+ const nameData = encoder.encode(entries[i].name);
727
+ const entryInfo = await fs.promises.stat(resolve(fileDescriptor.realPath, entries[i].name), { bigint: true });
728
+ const entryData = new Uint8Array(24 + nameData.byteLength);
729
+ const entryView = new DataView(entryData.buffer);
730
+ entryView.setBigUint64(0, BigInt(i + 1), true);
731
+ entryView.setBigUint64(8, BigInt(entryInfo.ino ? entryInfo.ino : 0), true);
732
+ entryView.setUint32(16, nameData.byteLength, true);
733
+ let type;
734
+ if (entries[i].isFile()) {
735
+ type = WasiFileType.REGULAR_FILE;
736
+ }
737
+ else if (entries[i].isDirectory()) {
738
+ type = WasiFileType.DIRECTORY;
739
+ }
740
+ else if (entries[i].isSymbolicLink()) {
741
+ type = WasiFileType.SYMBOLIC_LINK;
742
+ }
743
+ else if (entries[i].isCharacterDevice()) {
744
+ type = WasiFileType.CHARACTER_DEVICE;
745
+ }
746
+ else if (entries[i].isBlockDevice()) {
747
+ type = WasiFileType.BLOCK_DEVICE;
748
+ }
749
+ else if (entries[i].isSocket()) {
750
+ type = WasiFileType.SOCKET_STREAM;
751
+ }
752
+ else {
753
+ type = WasiFileType.UNKNOWN;
754
+ }
755
+ entryView.setUint8(20, type);
756
+ entryData.set(nameData, 24);
757
+ const data = entryData.slice(0, Math.min(entryData.length, buf_len - bufferUsed));
758
+ HEAPU8.set(data, buf + bufferUsed);
759
+ bufferUsed += data.byteLength;
760
+ }
761
+ view.setUint32(bufused, bufferUsed, true);
762
+ return WasiErrno.ESUCCESS;
763
+ }, ['i32', 'i32', 'i32', 'i64', 'i32'], ['i32']);
764
+ defineImport('fd_renumber', function fd_renumber(from, to) {
504
765
  const wasi = _wasi.get(this);
505
766
  wasi.fds.renumber(to, from);
506
767
  return WasiErrno.ESUCCESS;
507
- });
508
- this.fd_sync = syscallWrap('fd_sync', function (fd) {
768
+ }, async function fd_renumber(from, to) {
769
+ const wasi = _wasi.get(this);
770
+ await wasi.fds.renumber(to, from);
771
+ return WasiErrno.ESUCCESS;
772
+ }, ['i32', 'i32'], ['i32']);
773
+ defineImport('fd_sync', function fd_sync(fd) {
509
774
  const wasi = _wasi.get(this);
510
775
  const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_SYNC, BigInt(0));
511
776
  const fs = getFs(this);
512
777
  fs.fsyncSync(fileDescriptor.fd);
513
778
  return WasiErrno.ESUCCESS;
514
- });
515
- this.fd_tell = syscallWrap('fd_tell', function (fd, offset) {
779
+ }, async function fd_sync(fd) {
516
780
  const wasi = _wasi.get(this);
517
- const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_TELL, BigInt(0));
518
- const pos = BigInt(fileDescriptor.pos);
519
- const { view } = getMemory(this);
520
- view.setBigUint64(Number(offset), pos, true);
781
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_SYNC, BigInt(0));
782
+ await fileDescriptor.fd.sync();
521
783
  return WasiErrno.ESUCCESS;
522
- });
523
- this.fd_write = syscallWrap('fd_write', function (fd, iovs, iovslen, size) {
784
+ }, ['i32'], ['i32']);
785
+ defineImport('fd_write', function fd_write(fd, iovs, iovslen, size) {
524
786
  iovs = Number(iovs);
525
787
  size = Number(size);
526
788
  if (iovs === 0 || size === 0) {
@@ -546,8 +808,47 @@ export class WASI {
546
808
  }
547
809
  view.setUint32(size, nwritten, true);
548
810
  return WasiErrno.ESUCCESS;
549
- });
550
- this.path_create_directory = syscallWrap('path_create_directory', function (fd, path, path_len) {
811
+ }, async function fd_write(fd, iovs, iovslen, size) {
812
+ iovs = Number(iovs);
813
+ size = Number(size);
814
+ if (iovs === 0 || size === 0) {
815
+ return WasiErrno.EINVAL;
816
+ }
817
+ const { HEAPU8, view } = getMemory(this);
818
+ const wasi = _wasi.get(this);
819
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.FD_WRITE, BigInt(0));
820
+ const buffer = concatBuffer(Array.from({ length: Number(iovslen) }, (_, i) => {
821
+ const offset = iovs + (i * 8);
822
+ const buf = view.getInt32(offset, true);
823
+ const bufLen = view.getUint32(offset + 4, true);
824
+ return HEAPU8.subarray(buf, buf + bufLen);
825
+ }));
826
+ let nwritten;
827
+ if (fd === 1 || fd === 2) {
828
+ nwritten = fileDescriptor.write(buffer);
829
+ }
830
+ else {
831
+ nwritten = await (await (fileDescriptor.fd.write(buffer, 0, buffer.length, Number(fileDescriptor.pos)))).bytesWritten;
832
+ fileDescriptor.pos += BigInt(nwritten);
833
+ }
834
+ view.setUint32(size, nwritten, true);
835
+ return WasiErrno.ESUCCESS;
836
+ }, ['i32', 'i32', 'i32', 'i32'], ['i32']);
837
+ defineImport('path_create_directory', function path_create_directory(fd, path, path_len) {
838
+ path = Number(path);
839
+ path_len = Number(path_len);
840
+ if (path === 0) {
841
+ return WasiErrno.EINVAL;
842
+ }
843
+ const { HEAPU8 } = getMemory(this);
844
+ const wasi = _wasi.get(this);
845
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_CREATE_DIRECTORY, BigInt(0));
846
+ let pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
847
+ pathString = resolve(fileDescriptor.realPath, pathString);
848
+ const fs = getFs(this);
849
+ fs.mkdirSync(pathString);
850
+ return WasiErrno.ESUCCESS;
851
+ }, async function path_create_directory(fd, path, path_len) {
551
852
  path = Number(path);
552
853
  path_len = Number(path_len);
553
854
  if (path === 0) {
@@ -559,10 +860,10 @@ export class WASI {
559
860
  let pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
560
861
  pathString = resolve(fileDescriptor.realPath, pathString);
561
862
  const fs = getFs(this);
562
- fs.mkdirSync(pathString);
863
+ await fs.promises.mkdir(pathString);
563
864
  return WasiErrno.ESUCCESS;
564
- });
565
- this.path_filestat_get = syscallWrap('path_filestat_get', function (fd, flags, path, path_len, filestat) {
865
+ }, ['i32', 'i32', 'i32'], ['i32']);
866
+ defineImport('path_filestat_get', function path_filestat_get(fd, flags, path, path_len, filestat) {
566
867
  path = Number(path);
567
868
  path_len = Number(path_len);
568
869
  filestat = Number(filestat);
@@ -584,8 +885,30 @@ export class WASI {
584
885
  }
585
886
  toFileStat(view, filestat, stat);
586
887
  return WasiErrno.ESUCCESS;
587
- });
588
- this.path_filestat_set_times = syscallWrap('path_filestat_set_times', function (fd, flags, path, path_len, atim, mtim, fst_flags) {
888
+ }, async function path_filestat_get(fd, flags, path, path_len, filestat) {
889
+ path = Number(path);
890
+ path_len = Number(path_len);
891
+ filestat = Number(filestat);
892
+ if (path === 0 || filestat === 0) {
893
+ return WasiErrno.EINVAL;
894
+ }
895
+ const { HEAPU8, view } = getMemory(this);
896
+ const wasi = _wasi.get(this);
897
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_FILESTAT_GET, BigInt(0));
898
+ let pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
899
+ const fs = getFs(this);
900
+ pathString = resolve(fileDescriptor.realPath, pathString);
901
+ let stat;
902
+ if ((flags & 1) === 1) {
903
+ stat = await fs.promises.stat(pathString, { bigint: true });
904
+ }
905
+ else {
906
+ stat = await fs.promises.lstat(pathString, { bigint: true });
907
+ }
908
+ toFileStat(view, filestat, stat);
909
+ return WasiErrno.ESUCCESS;
910
+ }, ['i32', 'i32', 'i32', 'i32', 'i32'], ['i32']);
911
+ defineImport('path_filestat_set_times', function path_filestat_set_times(fd, flags, path, path_len, atim, mtim, fst_flags) {
589
912
  path = Number(path);
590
913
  path_len = Number(path_len);
591
914
  if (path === 0)
@@ -600,7 +923,7 @@ export class WASI {
600
923
  const wasi = _wasi.get(this);
601
924
  const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_FILESTAT_SET_TIMES, BigInt(0));
602
925
  const fs = getFs(this);
603
- const resolvedPath = resolvePath(fs, fileDescriptor, decoder.decode(HEAPU8.subarray(path, path + path_len)), flags);
926
+ const resolvedPath = resolvePathSync(fs, fileDescriptor, decoder.decode(HEAPU8.subarray(path, path + path_len)), flags);
604
927
  if ((fst_flags & WasiFstFlag.SET_ATIM_NOW) === WasiFstFlag.SET_ATIM_NOW) {
605
928
  atim = BigInt(Date.now() * 1000000);
606
929
  }
@@ -609,8 +932,32 @@ export class WASI {
609
932
  }
610
933
  fs.utimesSync(resolvedPath, Number(atim), Number(mtim));
611
934
  return WasiErrno.ESUCCESS;
612
- });
613
- this.path_link = syscallWrap('path_link', function (old_fd, old_flags, old_path, old_path_len, new_fd, new_path, new_path_len) {
935
+ }, async function path_filestat_set_times(fd, flags, path, path_len, atim, mtim, fst_flags) {
936
+ path = Number(path);
937
+ path_len = Number(path_len);
938
+ if (path === 0)
939
+ return WasiErrno.EINVAL;
940
+ if ((fst_flags) & ~(WasiFstFlag.SET_ATIM |
941
+ WasiFstFlag.SET_ATIM_NOW |
942
+ WasiFstFlag.SET_MTIM |
943
+ WasiFstFlag.SET_MTIM_NOW)) {
944
+ return WasiErrno.EINVAL;
945
+ }
946
+ const { HEAPU8 } = getMemory(this);
947
+ const wasi = _wasi.get(this);
948
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_FILESTAT_SET_TIMES, BigInt(0));
949
+ const fs = getFs(this);
950
+ const resolvedPath = await resolvePathAsync(fs, fileDescriptor, decoder.decode(HEAPU8.subarray(path, path + path_len)), flags);
951
+ if ((fst_flags & WasiFstFlag.SET_ATIM_NOW) === WasiFstFlag.SET_ATIM_NOW) {
952
+ atim = BigInt(Date.now() * 1000000);
953
+ }
954
+ if ((fst_flags & WasiFstFlag.SET_MTIM_NOW) === WasiFstFlag.SET_MTIM_NOW) {
955
+ mtim = BigInt(Date.now() * 1000000);
956
+ }
957
+ await fs.promises.utimes(resolvedPath, Number(atim), Number(mtim));
958
+ return WasiErrno.ESUCCESS;
959
+ }, ['i32', 'i32', 'i32', 'i32', 'i64', 'i64', 'i32'], ['i32']);
960
+ defineImport('path_link', function path_link(old_fd, old_flags, old_path, old_path_len, new_fd, new_path, new_path_len) {
614
961
  old_path = Number(old_path);
615
962
  old_path_len = Number(old_path_len);
616
963
  new_path = Number(new_path);
@@ -630,20 +977,36 @@ export class WASI {
630
977
  }
631
978
  const { HEAPU8 } = getMemory(this);
632
979
  const fs = getFs(this);
633
- const resolvedOldPath = resolvePath(fs, oldWrap, decoder.decode(HEAPU8.subarray(old_path, old_path + old_path_len)), old_flags);
980
+ const resolvedOldPath = resolvePathSync(fs, oldWrap, decoder.decode(HEAPU8.subarray(old_path, old_path + old_path_len)), old_flags);
634
981
  const resolvedNewPath = resolve(newWrap.realPath, decoder.decode(HEAPU8.subarray(new_path, new_path + new_path_len)));
635
982
  fs.linkSync(resolvedOldPath, resolvedNewPath);
636
983
  return WasiErrno.ESUCCESS;
637
- });
638
- this.path_open = syscallWrap('path_open', function (dirfd, dirflags, path, path_len, o_flags, fs_rights_base, fs_rights_inheriting, fs_flags, fd) {
639
- path = Number(path);
640
- fd = Number(fd);
641
- if (path === 0 || fd === 0) {
984
+ }, async function path_link(old_fd, old_flags, old_path, old_path_len, new_fd, new_path, new_path_len) {
985
+ old_path = Number(old_path);
986
+ old_path_len = Number(old_path_len);
987
+ new_path = Number(new_path);
988
+ new_path_len = Number(new_path_len);
989
+ if (old_path === 0 || new_path === 0) {
642
990
  return WasiErrno.EINVAL;
643
991
  }
644
- path_len = Number(path_len);
645
- fs_rights_base = BigInt(fs_rights_base);
646
- fs_rights_base = BigInt(fs_rights_base);
992
+ const wasi = _wasi.get(this);
993
+ let oldWrap;
994
+ let newWrap;
995
+ if (old_fd === new_fd) {
996
+ oldWrap = newWrap = wasi.fds.get(old_fd, WasiRights.PATH_LINK_SOURCE | WasiRights.PATH_LINK_TARGET, BigInt(0));
997
+ }
998
+ else {
999
+ oldWrap = wasi.fds.get(old_fd, WasiRights.PATH_LINK_SOURCE, BigInt(0));
1000
+ newWrap = wasi.fds.get(new_fd, WasiRights.PATH_LINK_TARGET, BigInt(0));
1001
+ }
1002
+ const { HEAPU8 } = getMemory(this);
1003
+ const fs = getFs(this);
1004
+ const resolvedOldPath = await resolvePathAsync(fs, oldWrap, decoder.decode(HEAPU8.subarray(old_path, old_path + old_path_len)), old_flags);
1005
+ const resolvedNewPath = resolve(newWrap.realPath, decoder.decode(HEAPU8.subarray(new_path, new_path + new_path_len)));
1006
+ await fs.promises.link(resolvedOldPath, resolvedNewPath);
1007
+ return WasiErrno.ESUCCESS;
1008
+ }, ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'], ['i32']);
1009
+ function pathOpen(o_flags, fs_rights_base, fs_rights_inheriting, fs_flags) {
647
1010
  const read = (fs_rights_base & (WasiRights.FD_READ |
648
1011
  WasiRights.FD_READDIR)) !== BigInt(0);
649
1012
  const write = (fs_rights_base & (WasiRights.FD_DATASYNC |
@@ -688,32 +1051,78 @@ export class WASI {
688
1051
  if (write && (flags & (FileControlFlag.O_APPEND | FileControlFlag.O_TRUNC)) === 0) {
689
1052
  needed_inheriting |= WasiRights.FD_SEEK;
690
1053
  }
1054
+ return { flags, needed_base, needed_inheriting };
1055
+ }
1056
+ defineImport('path_open', function path_open(dirfd, dirflags, path, path_len, o_flags, fs_rights_base, fs_rights_inheriting, fs_flags, fd) {
1057
+ path = Number(path);
1058
+ fd = Number(fd);
1059
+ if (path === 0 || fd === 0) {
1060
+ return WasiErrno.EINVAL;
1061
+ }
1062
+ path_len = Number(path_len);
1063
+ fs_rights_base = BigInt(fs_rights_base);
1064
+ fs_rights_inheriting = BigInt(fs_rights_inheriting);
1065
+ const { flags: flagsRes, needed_base: neededBase, needed_inheriting: neededInheriting } = pathOpen(o_flags, fs_rights_base, fs_rights_inheriting, fs_flags);
691
1066
  const wasi = _wasi.get(this);
692
- const fileDescriptor = wasi.fds.get(dirfd, needed_base, needed_inheriting);
1067
+ const fileDescriptor = wasi.fds.get(dirfd, neededBase, neededInheriting);
693
1068
  const memory = getMemory(this);
694
1069
  const HEAPU8 = memory.HEAPU8;
695
1070
  const pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
696
1071
  const fs = getFs(this);
697
- const resolved_path = resolvePath(fs, fileDescriptor, pathString, dirflags);
698
- const r = fs.openSync(resolved_path, flags, 0o666);
1072
+ const resolved_path = resolvePathSync(fs, fileDescriptor, pathString, dirflags);
1073
+ const r = fs.openSync(resolved_path, flagsRes, 0o666);
699
1074
  const filetype = wasi.fds.getFileTypeByFd(r);
700
1075
  if ((o_flags & WasiFileControlFlag.O_DIRECTORY) !== 0 && filetype !== WasiFileType.DIRECTORY) {
701
1076
  return WasiErrno.ENOTDIR;
702
1077
  }
703
- const { base: max_base, inheriting: max_inheriting } = getRights(wasi.fds.stdio, r, flags, filetype);
1078
+ const { base: max_base, inheriting: max_inheriting } = getRights(wasi.fds.stdio, r, flagsRes, filetype);
704
1079
  const wrap = wasi.fds.insert(r, resolved_path, resolved_path, filetype, fs_rights_base & max_base, fs_rights_inheriting & max_inheriting, 0);
705
1080
  const stat = fs.fstatSync(r, { bigint: true });
706
1081
  if (stat.isFile()) {
707
1082
  wrap.size = stat.size;
708
- if ((flags & FileControlFlag.O_APPEND) !== 0) {
1083
+ if ((flagsRes & FileControlFlag.O_APPEND) !== 0) {
709
1084
  wrap.pos = stat.size;
710
1085
  }
711
1086
  }
712
1087
  const view = memory.view;
713
1088
  view.setInt32(fd, wrap.id, true);
714
1089
  return WasiErrno.ESUCCESS;
715
- });
716
- this.path_readlink = syscallWrap('path_readlink', function (fd, path, path_len, buf, buf_len, bufused) {
1090
+ }, async function path_open(dirfd, dirflags, path, path_len, o_flags, fs_rights_base, fs_rights_inheriting, fs_flags, fd) {
1091
+ path = Number(path);
1092
+ fd = Number(fd);
1093
+ if (path === 0 || fd === 0) {
1094
+ return WasiErrno.EINVAL;
1095
+ }
1096
+ path_len = Number(path_len);
1097
+ fs_rights_base = BigInt(fs_rights_base);
1098
+ fs_rights_inheriting = BigInt(fs_rights_inheriting);
1099
+ const { flags: flagsRes, needed_base: neededBase, needed_inheriting: neededInheriting } = pathOpen(o_flags, fs_rights_base, fs_rights_inheriting, fs_flags);
1100
+ const wasi = _wasi.get(this);
1101
+ const fileDescriptor = wasi.fds.get(dirfd, neededBase, neededInheriting);
1102
+ const memory = getMemory(this);
1103
+ const HEAPU8 = memory.HEAPU8;
1104
+ const pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
1105
+ const fs = getFs(this);
1106
+ const resolved_path = await resolvePathAsync(fs, fileDescriptor, pathString, dirflags);
1107
+ const r = await fs.promises.open(resolved_path, flagsRes, 0o666);
1108
+ const filetype = await wasi.fds.getFileTypeByFd(r);
1109
+ if ((o_flags & WasiFileControlFlag.O_DIRECTORY) !== 0 && filetype !== WasiFileType.DIRECTORY) {
1110
+ return WasiErrno.ENOTDIR;
1111
+ }
1112
+ const { base: max_base, inheriting: max_inheriting } = getRights(wasi.fds.stdio, r.fd, flagsRes, filetype);
1113
+ const wrap = wasi.fds.insert(r, resolved_path, resolved_path, filetype, fs_rights_base & max_base, fs_rights_inheriting & max_inheriting, 0);
1114
+ const stat = await r.stat({ bigint: true });
1115
+ if (stat.isFile()) {
1116
+ wrap.size = stat.size;
1117
+ if ((flagsRes & FileControlFlag.O_APPEND) !== 0) {
1118
+ wrap.pos = stat.size;
1119
+ }
1120
+ }
1121
+ const view = memory.view;
1122
+ view.setInt32(fd, wrap.id, true);
1123
+ return WasiErrno.ESUCCESS;
1124
+ }, ['i32', 'i32', 'i32', 'i32', 'i32', 'i64', 'i64', 'i32', 'i32'], ['i32']);
1125
+ defineImport('path_readlink', function path_readlink(fd, path, path_len, buf, buf_len, bufused) {
717
1126
  path = Number(path);
718
1127
  path_len = Number(path_len);
719
1128
  buf = Number(buf);
@@ -737,8 +1146,32 @@ export class WASI {
737
1146
  HEAPU8[buf + len] = 0;
738
1147
  view.setUint32(bufused, len + 1, true);
739
1148
  return WasiErrno.ESUCCESS;
740
- });
741
- this.path_remove_directory = syscallWrap('path_remove_directory', function (fd, path, path_len) {
1149
+ }, async function path_readlink(fd, path, path_len, buf, buf_len, bufused) {
1150
+ path = Number(path);
1151
+ path_len = Number(path_len);
1152
+ buf = Number(buf);
1153
+ buf_len = Number(buf_len);
1154
+ bufused = Number(bufused);
1155
+ if (path === 0 || buf === 0 || bufused === 0) {
1156
+ return WasiErrno.EINVAL;
1157
+ }
1158
+ const { HEAPU8, view } = getMemory(this);
1159
+ const wasi = _wasi.get(this);
1160
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_READLINK, BigInt(0));
1161
+ let pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
1162
+ pathString = resolve(fileDescriptor.realPath, pathString);
1163
+ const fs = getFs(this);
1164
+ const link = await fs.promises.readlink(pathString);
1165
+ const linkData = encoder.encode(link);
1166
+ const len = Math.min(linkData.length, buf_len);
1167
+ if (len >= buf_len)
1168
+ return WasiErrno.ENOBUFS;
1169
+ HEAPU8.set(linkData.subarray(0, len), buf);
1170
+ HEAPU8[buf + len] = 0;
1171
+ view.setUint32(bufused, len + 1, true);
1172
+ return WasiErrno.ESUCCESS;
1173
+ }, ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'], ['i32']);
1174
+ defineImport('path_remove_directory', function path_remove_directory(fd, path, path_len) {
742
1175
  path = Number(path);
743
1176
  path_len = Number(path_len);
744
1177
  if (path === 0) {
@@ -752,8 +1185,22 @@ export class WASI {
752
1185
  const fs = getFs(this);
753
1186
  fs.rmdirSync(pathString);
754
1187
  return WasiErrno.ESUCCESS;
755
- });
756
- this.path_rename = syscallWrap('path_rename', function (old_fd, old_path, old_path_len, new_fd, new_path, new_path_len) {
1188
+ }, async function path_remove_directory(fd, path, path_len) {
1189
+ path = Number(path);
1190
+ path_len = Number(path_len);
1191
+ if (path === 0) {
1192
+ return WasiErrno.EINVAL;
1193
+ }
1194
+ const { HEAPU8 } = getMemory(this);
1195
+ const wasi = _wasi.get(this);
1196
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_REMOVE_DIRECTORY, BigInt(0));
1197
+ let pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
1198
+ pathString = resolve(fileDescriptor.realPath, pathString);
1199
+ const fs = getFs(this);
1200
+ await fs.promises.rmdir(pathString);
1201
+ return WasiErrno.ESUCCESS;
1202
+ }, ['i32', 'i32', 'i32'], ['i32']);
1203
+ defineImport('path_rename', function path_rename(old_fd, old_path, old_path_len, new_fd, new_path, new_path_len) {
757
1204
  old_path = Number(old_path);
758
1205
  old_path_len = Number(old_path_len);
759
1206
  new_path = Number(new_path);
@@ -777,8 +1224,32 @@ export class WASI {
777
1224
  const fs = getFs(this);
778
1225
  fs.renameSync(resolvedOldPath, resolvedNewPath);
779
1226
  return WasiErrno.ESUCCESS;
780
- });
781
- this.path_symlink = syscallWrap('path_symlink', function (old_path, old_path_len, fd, new_path, new_path_len) {
1227
+ }, async function path_rename(old_fd, old_path, old_path_len, new_fd, new_path, new_path_len) {
1228
+ old_path = Number(old_path);
1229
+ old_path_len = Number(old_path_len);
1230
+ new_path = Number(new_path);
1231
+ new_path_len = Number(new_path_len);
1232
+ if (old_path === 0 || new_path === 0) {
1233
+ return WasiErrno.EINVAL;
1234
+ }
1235
+ const wasi = _wasi.get(this);
1236
+ let oldWrap;
1237
+ let newWrap;
1238
+ if (old_fd === new_fd) {
1239
+ oldWrap = newWrap = wasi.fds.get(old_fd, WasiRights.PATH_RENAME_SOURCE | WasiRights.PATH_RENAME_TARGET, BigInt(0));
1240
+ }
1241
+ else {
1242
+ oldWrap = wasi.fds.get(old_fd, WasiRights.PATH_RENAME_SOURCE, BigInt(0));
1243
+ newWrap = wasi.fds.get(new_fd, WasiRights.PATH_RENAME_TARGET, BigInt(0));
1244
+ }
1245
+ const { HEAPU8 } = getMemory(this);
1246
+ const resolvedOldPath = resolve(oldWrap.realPath, decoder.decode(HEAPU8.subarray(old_path, old_path + old_path_len)));
1247
+ const resolvedNewPath = resolve(newWrap.realPath, decoder.decode(HEAPU8.subarray(new_path, new_path + new_path_len)));
1248
+ const fs = getFs(this);
1249
+ await fs.promises.rename(resolvedOldPath, resolvedNewPath);
1250
+ return WasiErrno.ESUCCESS;
1251
+ }, ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'], ['i32']);
1252
+ defineImport('path_symlink', function path_symlink(old_path, old_path_len, fd, new_path, new_path_len) {
782
1253
  old_path = Number(old_path);
783
1254
  old_path_len = Number(old_path_len);
784
1255
  new_path = Number(new_path);
@@ -795,8 +1266,25 @@ export class WASI {
795
1266
  const fs = getFs(this);
796
1267
  fs.symlinkSync(oldPath, newPath);
797
1268
  return WasiErrno.ESUCCESS;
798
- });
799
- this.path_unlink_file = syscallWrap('path_unlink_file', function (fd, path, path_len) {
1269
+ }, async function path_symlink(old_path, old_path_len, fd, new_path, new_path_len) {
1270
+ old_path = Number(old_path);
1271
+ old_path_len = Number(old_path_len);
1272
+ new_path = Number(new_path);
1273
+ new_path_len = Number(new_path_len);
1274
+ if (old_path === 0 || new_path === 0) {
1275
+ return WasiErrno.EINVAL;
1276
+ }
1277
+ const { HEAPU8 } = getMemory(this);
1278
+ const wasi = _wasi.get(this);
1279
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_SYMLINK, BigInt(0));
1280
+ const oldPath = decoder.decode(HEAPU8.subarray(old_path, old_path + old_path_len));
1281
+ let newPath = decoder.decode(HEAPU8.subarray(new_path, new_path + new_path_len));
1282
+ newPath = resolve(fileDescriptor.realPath, newPath);
1283
+ const fs = getFs(this);
1284
+ await fs.promises.symlink(oldPath, newPath);
1285
+ return WasiErrno.ESUCCESS;
1286
+ }, ['i32', 'i32', 'i32', 'i32', 'i32'], ['i32']);
1287
+ defineImport('path_unlink_file', function path_unlink_file(fd, path, path_len) {
800
1288
  path = Number(path);
801
1289
  path_len = Number(path_len);
802
1290
  if (path === 0) {
@@ -810,58 +1298,30 @@ export class WASI {
810
1298
  const fs = getFs(this);
811
1299
  fs.unlinkSync(pathString);
812
1300
  return WasiErrno.ESUCCESS;
813
- });
814
- this.poll_oneoff = syscallWrap('poll_oneoff', function (_in, _out, _sub, _nevents) {
815
- return WasiErrno.ENOSYS;
816
- });
817
- this.proc_exit = syscallWrap('proc_exit', function (_rval) {
818
- return WasiErrno.ESUCCESS;
819
- });
820
- this.proc_raise = syscallWrap('proc_raise', function (_sig) {
821
- return WasiErrno.ENOSYS;
822
- });
823
- this.sched_yield = syscallWrap('sched_yield', function () {
1301
+ }, async function path_unlink_file(fd, path, path_len) {
1302
+ path = Number(path);
1303
+ path_len = Number(path_len);
1304
+ if (path === 0) {
1305
+ return WasiErrno.EINVAL;
1306
+ }
1307
+ const { HEAPU8 } = getMemory(this);
1308
+ const wasi = _wasi.get(this);
1309
+ const fileDescriptor = wasi.fds.get(fd, WasiRights.PATH_UNLINK_FILE, BigInt(0));
1310
+ let pathString = decoder.decode(HEAPU8.subarray(path, path + path_len));
1311
+ pathString = resolve(fileDescriptor.realPath, pathString);
1312
+ const fs = getFs(this);
1313
+ await fs.promises.unlink(pathString);
824
1314
  return WasiErrno.ESUCCESS;
825
- });
826
- this.random_get = typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function'
827
- ? syscallWrap('random_get', function (buf, buf_len) {
828
- buf = Number(buf);
829
- if (buf === 0) {
830
- return WasiErrno.EINVAL;
831
- }
832
- buf_len = Number(buf_len);
833
- const { HEAPU8 } = getMemory(this);
834
- let pos;
835
- const stride = 65536;
836
- for (pos = 0; pos + stride < buf_len; pos += stride) {
837
- crypto.getRandomValues(HEAPU8.subarray(buf + pos, buf + pos + stride));
838
- }
839
- crypto.getRandomValues(HEAPU8.subarray(buf + pos, buf + buf_len));
840
- return WasiErrno.ESUCCESS;
841
- })
842
- : syscallWrap('random_get', function (buf, buf_len) {
843
- buf = Number(buf);
844
- if (buf === 0) {
845
- return WasiErrno.EINVAL;
846
- }
847
- buf_len = Number(buf_len);
848
- const { view } = getMemory(this);
849
- for (let i = buf; i < buf + buf_len; ++i) {
850
- view.setUint8(i, Math.floor(Math.random() * 256));
851
- }
852
- return WasiErrno.ESUCCESS;
853
- });
854
- this.sock_recv = syscallWrap('sock_recv', function () {
855
- return WasiErrno.ENOTSUP;
856
- });
857
- this.sock_send = syscallWrap('sock_send', function () {
858
- return WasiErrno.ENOTSUP;
859
- });
860
- this.sock_shutdown = syscallWrap('sock_shutdown', function () {
861
- return WasiErrno.ENOTSUP;
862
- });
863
- const fs = filesystem ? filesystem.fs : undefined;
864
- const fds = new FileDescriptorTable({
1315
+ }, ['i32', 'i32', 'i32'], ['i32']);
1316
+ this._setMemory = function setMemory(m) {
1317
+ if (!(m instanceof _WebAssembly.Memory)) {
1318
+ throw new TypeError('"instance.exports.memory" property must be a WebAssembly.Memory');
1319
+ }
1320
+ _memory.set(_this, extendMemory(m));
1321
+ };
1322
+ }
1323
+ static createSync(args, env, preopens, stdio, fs, print, printErr) {
1324
+ const fds = new SyncTable({
865
1325
  size: 3,
866
1326
  in: stdio[0],
867
1327
  out: stdio[1],
@@ -870,13 +1330,7 @@ export class WASI {
870
1330
  print,
871
1331
  printErr
872
1332
  });
873
- _wasi.set(this, {
874
- fds,
875
- args,
876
- env
877
- });
878
- if (fs)
879
- _fs.set(this, fs);
1333
+ const _this = new WASI(args, env, fds, false, fs);
880
1334
  if (preopens.length > 0) {
881
1335
  for (let i = 0; i < preopens.length; ++i) {
882
1336
  const realPath = fs.realpathSync(preopens[i].realPath, 'utf8');
@@ -884,5 +1338,26 @@ export class WASI {
884
1338
  fds.insertPreopen(fd, preopens[i].mappedPath, realPath);
885
1339
  }
886
1340
  }
1341
+ return _this;
1342
+ }
1343
+ static async createAsync(args, env, preopens, stdio, fs, print, printErr, asyncify) {
1344
+ const fds = new AsyncTable({
1345
+ size: 3,
1346
+ in: stdio[0],
1347
+ out: stdio[1],
1348
+ err: stdio[2],
1349
+ print,
1350
+ printErr
1351
+ });
1352
+ const _this = new WASI(args, env, fds, true, fs, asyncify);
1353
+ if (preopens.length > 0) {
1354
+ for (let i = 0; i < preopens.length; ++i) {
1355
+ const entry = preopens[i];
1356
+ const realPath = await fs.promises.realpath(entry.realPath);
1357
+ const fd = await fs.promises.open(realPath, 'r', 0o666);
1358
+ await fds.insertPreopen(fd, entry.mappedPath, realPath);
1359
+ }
1360
+ }
1361
+ return _this;
887
1362
  }
888
1363
  }