diff options
author | 2023-01-28 23:23:26 -0800 | |
---|---|---|
committer | 2023-01-28 23:23:26 -0800 | |
commit | f087388ebc6314c2852d553f4f4ea3074369dfbe (patch) | |
tree | 935a0c205b3eccca29f3bcc5e3db18f7fdc4469d /src | |
parent | 48eb0c12ab2c568d7ff706c2b0a6616d428032c8 (diff) | |
download | bun-f087388ebc6314c2852d553f4f4ea3074369dfbe.tar.gz bun-f087388ebc6314c2852d553f4f4ea3074369dfbe.tar.zst bun-f087388ebc6314c2852d553f4f4ea3074369dfbe.zip |
Support running WASI (WebAssembly) files using `bun run` (#1929)
* another micro bench
* Support running WASI
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/api/.gitignore | 1 | ||||
-rw-r--r-- | src/bun.js/module_loader.zig | 87 | ||||
-rw-r--r-- | src/bun.js/wasi-runner.js | 49 | ||||
-rw-r--r-- | src/bun.js/wasi.exports.js | 2177 | ||||
-rw-r--r-- | src/bundler.zig | 13 | ||||
-rw-r--r-- | src/cli.zig | 2 | ||||
-rw-r--r-- | src/cli/run_command.zig | 4 | ||||
-rw-r--r-- | src/deps/.gitignore | 1 | ||||
-rw-r--r-- | src/logger.zig | 7 | ||||
-rw-r--r-- | src/string_immutable.zig | 11 |
10 files changed, 2348 insertions, 4 deletions
diff --git a/src/api/.gitignore b/src/api/.gitignore new file mode 100644 index 000000000..19e1bced9 --- /dev/null +++ b/src/api/.gitignore @@ -0,0 +1 @@ +*.wasm diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 485a5c59a..0038526a1 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -982,6 +982,25 @@ pub const ModuleLoader = struct { return error.ParseError; }; + if (parse_result.loader == .wasm) { + const wasm_result = transpileSourceCode( + jsc_vm, + specifier, + display_specifier, + referrer, + path, + .wasm, + log, + &parse_result.source, + ret, + promise_ptr, + source_code_printer, + globalObject, + flags, + ); + return wasm_result; + } + if (jsc_vm.bundler.log.errors > 0) { return error.ParseError; } @@ -1180,6 +1199,55 @@ pub const ModuleLoader = struct { // .tag = ResolvedSource.Tag.wasm, // }; // }, + .wasm => { + if (strings.eqlComptime(referrer, "undefined") and strings.eqlLong(jsc_vm.main, path.text, true)) { + if (virtual_source) |source| { + if (globalObject) |globalThis| { + // attempt to avoid reading the WASM file twice. + var encoded = JSC.EncodedJSValue{ + .asPtr = globalThis, + }; + const globalValue = @intToEnum(JSC.JSValue, encoded.asInt64); + globalValue.put( + globalThis, + JSC.ZigString.static("wasmSourceBytes"), + JSC.ArrayBuffer.create(globalThis, source.contents, .Uint8Array), + ); + } + } + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + strings.append3( + bun.default_allocator, + JSC.Node.fs.constants_string, + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./wasi.exports.js")), + jsModuleFromFile(jsc_vm.load_builtins_from_path, "wasi-runner.js"), + ) catch unreachable, + ), + .specifier = ZigString.init(display_specifier), + .source_url = ZigString.init(path.text), + .hash = 0, + }; + } + + return transpileSourceCode( + jsc_vm, + specifier, + display_specifier, + referrer, + path, + .file, + log, + virtual_source, + ret, + promise_ptr, + source_code_printer, + globalObject, + flags, + ); + }, + else => { var stack_buf = std.heap.stackFallback(4096, jsc_vm.allocator); var allocator = stack_buf.get(); @@ -1910,6 +1978,21 @@ pub const ModuleLoader = struct { .hash = 0, }; }, + .@"node:wasi" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + strings.append( + bun.default_allocator, + JSC.Node.fs.constants_string, + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./wasi.exports.js")), + ) catch unreachable, + ), + .specifier = ZigString.init("node:wasi"), + .source_url = ZigString.init("node:wasi"), + .hash = 0, + }; + }, .@"node:http" => { return ResolvedSource{ .allocator = null, @@ -2084,6 +2167,7 @@ pub const HardcodedModule = enum { depd, undici, ws, + @"node:wasi", /// Already resolved modules go in here. /// This does not remap the module name, it is just a hash table. /// Do not put modules that have aliases in here @@ -2130,6 +2214,7 @@ pub const HardcodedModule = enum { .{ "node:url", HardcodedModule.@"node:url" }, .{ "node:util", HardcodedModule.@"node:util" }, .{ "node:util/types", HardcodedModule.@"node:util/types" }, + .{ "node:wasi", HardcodedModule.@"node:wasi" }, .{ "node:zlib", HardcodedModule.@"node:zlib" }, .{ "undici", HardcodedModule.undici }, .{ "ws", HardcodedModule.ws }, @@ -2192,6 +2277,7 @@ pub const HardcodedModule = enum { .{ "node:url", "node:url" }, .{ "node:util", "node:util" }, .{ "node:util/types", "node:util/types" }, + .{ "node:wasi", "node:wasi" }, .{ "node:zlib", "node:zlib" }, .{ "os", "node:os" }, .{ "path", "node:path" }, @@ -2216,6 +2302,7 @@ pub const HardcodedModule = enum { .{ "url", "node:url" }, .{ "util", "node:util" }, .{ "util/types", "node:util/types" }, + .{ "wasi", "node:wasi" }, .{ "ws", "ws" }, .{ "ws/lib/websocket", "ws" }, .{ "zlib", "node:zlib" }, diff --git a/src/bun.js/wasi-runner.js b/src/bun.js/wasi-runner.js new file mode 100644 index 000000000..24ff0a678 --- /dev/null +++ b/src/bun.js/wasi-runner.js @@ -0,0 +1,49 @@ +/** --- WASI */ +// wasi is imported into the top of this file + +const filePath = process.argv.at(1); +if (!filePath) { + var err = new Error( + "To run a wasm file with Bun, the first argument must be a path to a .wasm file", + ); + err.name = "WasmFileNotFound"; + throw err; +} + +// The module specifier is the resolved path to the wasm file + +var { + WASM_CWD = process.cwd(), + WASM_ROOT_DIR = "/", + WASM_ENV_STR = undefined, + WASM_USE_ASYNC_INIT = "", +} = process.env; + +var env = process.env; +if (WASM_ENV_STR?.length) { + env = JSON.parse(WASM_ENV_STR); +} + +const wasi = new WASI({ + args: process.argv.slice(1), + env, + preopens: { + ".": WASM_CWD || process.cwd(), + "/": WASM_ROOT_DIR || "/", + }, +}); + +let source = globalThis.wasmSourceBytes; +if (!source) { + const fs = Bun.fs(); + const file = import.meta.path; + source = fs.readFileSync(file); +} + +const wasm = new WebAssembly.Module(source); +const instance = !WASM_USE_ASYNC_INIT + ? new WebAssembly.Instance(wasm, wasi.getImports(wasm)) + : await WebAssembly.instantiate(wasm, wasi.getImports(wasm)); +wasi.start(instance); + +process.exit(0); diff --git a/src/bun.js/wasi.exports.js b/src/bun.js/wasi.exports.js new file mode 100644 index 000000000..060310a9c --- /dev/null +++ b/src/bun.js/wasi.exports.js @@ -0,0 +1,2177 @@ +// HUGE thanks to @williamstein and https://github.com/sagemathinc/cowasm/tree/main/packages/wasi-js +// This code is mostly a copy of wasi-js +// Eventually we will implement this in native code, but this is just a quick hack to get WASI working. + +/** **/ +// constants is injected into the top of this file +const nodeFsConstants = constants; + +var __getOwnPropNames = Object.getOwnPropertyNames; + +var __commonJS = (cb, mod) => + function __require2() { + return ( + mod || + (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), + mod.exports + ); + }; + +// node_modules/wasi-js/dist/types.js +var require_types = __commonJS({ + "node_modules/wasi-js/dist/types.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.WASIKillError = exports.WASIExitError = exports.WASIError = void 0; + var WASIError = class extends Error { + constructor(errno) { + super(); + this.errno = errno; + Object.setPrototypeOf(this, WASIError.prototype); + } + }; + exports.WASIError = WASIError; + var WASIExitError = class extends Error { + constructor(code) { + super(`WASI Exit error: ${code}`); + this.code = code; + Object.setPrototypeOf(this, WASIExitError.prototype); + } + }; + exports.WASIExitError = WASIExitError; + var WASIKillError = class extends Error { + constructor(signal) { + super(`WASI Kill signal: ${signal}`); + this.signal = signal; + Object.setPrototypeOf(this, WASIKillError.prototype); + } + }; + exports.WASIKillError = WASIKillError; + }, +}); + +// node_modules/wasi-js/dist/constants.js +var require_constants = __commonJS({ + "node_modules/wasi-js/dist/constants.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.WASI_ENOMSG = + exports.WASI_ENOMEM = + exports.WASI_ENOLINK = + exports.WASI_ENOLCK = + exports.WASI_ENOEXEC = + exports.WASI_ENOENT = + exports.WASI_ENODEV = + exports.WASI_ENOBUFS = + exports.WASI_ENFILE = + exports.WASI_ENETUNREACH = + exports.WASI_ENETRESET = + exports.WASI_ENETDOWN = + exports.WASI_ENAMETOOLONG = + exports.WASI_EMULTIHOP = + exports.WASI_EMSGSIZE = + exports.WASI_EMLINK = + exports.WASI_EMFILE = + exports.WASI_ELOOP = + exports.WASI_EISDIR = + exports.WASI_EISCONN = + exports.WASI_EIO = + exports.WASI_EINVAL = + exports.WASI_EINTR = + exports.WASI_EINPROGRESS = + exports.WASI_EILSEQ = + exports.WASI_EIDRM = + exports.WASI_EHOSTUNREACH = + exports.WASI_EFBIG = + exports.WASI_EFAULT = + exports.WASI_EEXIST = + exports.WASI_EDQUOT = + exports.WASI_EDOM = + exports.WASI_EDESTADDRREQ = + exports.WASI_EDEADLK = + exports.WASI_ECONNRESET = + exports.WASI_ECONNREFUSED = + exports.WASI_ECONNABORTED = + exports.WASI_ECHILD = + exports.WASI_ECANCELED = + exports.WASI_EBUSY = + exports.WASI_EBADMSG = + exports.WASI_EBADF = + exports.WASI_EALREADY = + exports.WASI_EAGAIN = + exports.WASI_EAFNOSUPPORT = + exports.WASI_EADDRNOTAVAIL = + exports.WASI_EADDRINUSE = + exports.WASI_EACCES = + exports.WASI_E2BIG = + exports.WASI_ESUCCESS = + void 0; + exports.WASI_SIGVTALRM = + exports.WASI_SIGUSR2 = + exports.WASI_SIGUSR1 = + exports.WASI_SIGURG = + exports.WASI_SIGTTOU = + exports.WASI_SIGTTIN = + exports.WASI_SIGTSTP = + exports.WASI_SIGTRAP = + exports.WASI_SIGTERM = + exports.WASI_SIGSTOP = + exports.WASI_SIGSEGV = + exports.WASI_SIGQUIT = + exports.WASI_SIGPIPE = + exports.WASI_SIGKILL = + exports.WASI_SIGINT = + exports.WASI_SIGILL = + exports.WASI_SIGHUP = + exports.WASI_SIGFPE = + exports.WASI_SIGCONT = + exports.WASI_SIGCHLD = + exports.WASI_SIGBUS = + exports.WASI_SIGALRM = + exports.WASI_SIGABRT = + exports.WASI_ENOTCAPABLE = + exports.WASI_EXDEV = + exports.WASI_ETXTBSY = + exports.WASI_ETIMEDOUT = + exports.WASI_ESTALE = + exports.WASI_ESRCH = + exports.WASI_ESPIPE = + exports.WASI_EROFS = + exports.WASI_ERANGE = + exports.WASI_EPROTOTYPE = + exports.WASI_EPROTONOSUPPORT = + exports.WASI_EPROTO = + exports.WASI_EPIPE = + exports.WASI_EPERM = + exports.WASI_EOWNERDEAD = + exports.WASI_EOVERFLOW = + exports.WASI_ENXIO = + exports.WASI_ENOTTY = + exports.WASI_ENOTSUP = + exports.WASI_ENOTSOCK = + exports.WASI_ENOTRECOVERABLE = + exports.WASI_ENOTEMPTY = + exports.WASI_ENOTDIR = + exports.WASI_ENOTCONN = + exports.WASI_ENOSYS = + exports.WASI_ENOSPC = + exports.WASI_ENOPROTOOPT = + void 0; + exports.RIGHTS_REGULAR_FILE_BASE = + exports.RIGHTS_CHARACTER_DEVICE_INHERITING = + exports.RIGHTS_CHARACTER_DEVICE_BASE = + exports.RIGHTS_BLOCK_DEVICE_INHERITING = + exports.RIGHTS_BLOCK_DEVICE_BASE = + exports.RIGHTS_ALL = + exports.WASI_RIGHT_SOCK_SHUTDOWN = + exports.WASI_RIGHT_POLL_FD_READWRITE = + exports.WASI_RIGHT_PATH_UNLINK_FILE = + exports.WASI_RIGHT_PATH_REMOVE_DIRECTORY = + exports.WASI_RIGHT_PATH_SYMLINK = + exports.WASI_RIGHT_FD_FILESTAT_SET_TIMES = + exports.WASI_RIGHT_FD_FILESTAT_SET_SIZE = + exports.WASI_RIGHT_FD_FILESTAT_GET = + exports.WASI_RIGHT_PATH_FILESTAT_SET_TIMES = + exports.WASI_RIGHT_PATH_FILESTAT_SET_SIZE = + exports.WASI_RIGHT_PATH_FILESTAT_GET = + exports.WASI_RIGHT_PATH_RENAME_TARGET = + exports.WASI_RIGHT_PATH_RENAME_SOURCE = + exports.WASI_RIGHT_PATH_READLINK = + exports.WASI_RIGHT_FD_READDIR = + exports.WASI_RIGHT_PATH_OPEN = + exports.WASI_RIGHT_PATH_LINK_TARGET = + exports.WASI_RIGHT_PATH_LINK_SOURCE = + exports.WASI_RIGHT_PATH_CREATE_FILE = + exports.WASI_RIGHT_PATH_CREATE_DIRECTORY = + exports.WASI_RIGHT_FD_ALLOCATE = + exports.WASI_RIGHT_FD_ADVISE = + exports.WASI_RIGHT_FD_WRITE = + exports.WASI_RIGHT_FD_TELL = + exports.WASI_RIGHT_FD_SYNC = + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS = + exports.WASI_RIGHT_FD_SEEK = + exports.WASI_RIGHT_FD_READ = + exports.WASI_RIGHT_FD_DATASYNC = + exports.WASI_FDFLAG_SYNC = + exports.WASI_FDFLAG_RSYNC = + exports.WASI_FDFLAG_NONBLOCK = + exports.WASI_FDFLAG_DSYNC = + exports.WASI_FDFLAG_APPEND = + exports.WASI_FILETYPE_SYMBOLIC_LINK = + exports.WASI_FILETYPE_SOCKET_STREAM = + exports.WASI_FILETYPE_SOCKET_DGRAM = + exports.WASI_FILETYPE_REGULAR_FILE = + exports.WASI_FILETYPE_DIRECTORY = + exports.WASI_FILETYPE_CHARACTER_DEVICE = + exports.WASI_FILETYPE_BLOCK_DEVICE = + exports.WASI_FILETYPE_UNKNOWN = + exports.WASI_SIGXFSZ = + exports.WASI_SIGXCPU = + void 0; + exports.SIGNAL_MAP = + exports.ERROR_MAP = + exports.WASI_WHENCE_END = + exports.WASI_WHENCE_CUR = + exports.WASI_WHENCE_SET = + exports.WASI_STDERR_FILENO = + exports.WASI_STDOUT_FILENO = + exports.WASI_STDIN_FILENO = + exports.WASI_DIRCOOKIE_START = + exports.WASI_PREOPENTYPE_DIR = + exports.WASI_O_TRUNC = + exports.WASI_O_EXCL = + exports.WASI_O_DIRECTORY = + exports.WASI_O_CREAT = + exports.WASI_FILESTAT_SET_MTIM_NOW = + exports.WASI_FILESTAT_SET_MTIM = + exports.WASI_FILESTAT_SET_ATIM_NOW = + exports.WASI_FILESTAT_SET_ATIM = + exports.WASI_EVENTTYPE_FD_WRITE = + exports.WASI_EVENTTYPE_FD_READ = + exports.WASI_EVENTTYPE_CLOCK = + exports.WASI_CLOCK_THREAD_CPUTIME_ID = + exports.WASI_CLOCK_PROCESS_CPUTIME_ID = + exports.WASI_CLOCK_MONOTONIC = + exports.WASI_CLOCK_REALTIME = + exports.RIGHTS_TTY_INHERITING = + exports.RIGHTS_TTY_BASE = + exports.RIGHTS_SOCKET_INHERITING = + exports.RIGHTS_SOCKET_BASE = + exports.RIGHTS_DIRECTORY_INHERITING = + exports.RIGHTS_DIRECTORY_BASE = + exports.RIGHTS_REGULAR_FILE_INHERITING = + void 0; + exports.WASI_ESUCCESS = 0; + exports.WASI_E2BIG = 1; + exports.WASI_EACCES = 2; + exports.WASI_EADDRINUSE = 3; + exports.WASI_EADDRNOTAVAIL = 4; + exports.WASI_EAFNOSUPPORT = 5; + exports.WASI_EAGAIN = 6; + exports.WASI_EALREADY = 7; + exports.WASI_EBADF = 8; + exports.WASI_EBADMSG = 9; + exports.WASI_EBUSY = 10; + exports.WASI_ECANCELED = 11; + exports.WASI_ECHILD = 12; + exports.WASI_ECONNABORTED = 13; + exports.WASI_ECONNREFUSED = 14; + exports.WASI_ECONNRESET = 15; + exports.WASI_EDEADLK = 16; + exports.WASI_EDESTADDRREQ = 17; + exports.WASI_EDOM = 18; + exports.WASI_EDQUOT = 19; + exports.WASI_EEXIST = 20; + exports.WASI_EFAULT = 21; + exports.WASI_EFBIG = 22; + exports.WASI_EHOSTUNREACH = 23; + exports.WASI_EIDRM = 24; + exports.WASI_EILSEQ = 25; + exports.WASI_EINPROGRESS = 26; + exports.WASI_EINTR = 27; + exports.WASI_EINVAL = 28; + exports.WASI_EIO = 29; + exports.WASI_EISCONN = 30; + exports.WASI_EISDIR = 31; + exports.WASI_ELOOP = 32; + exports.WASI_EMFILE = 33; + exports.WASI_EMLINK = 34; + exports.WASI_EMSGSIZE = 35; + exports.WASI_EMULTIHOP = 36; + exports.WASI_ENAMETOOLONG = 37; + exports.WASI_ENETDOWN = 38; + exports.WASI_ENETRESET = 39; + exports.WASI_ENETUNREACH = 40; + exports.WASI_ENFILE = 41; + exports.WASI_ENOBUFS = 42; + exports.WASI_ENODEV = 43; + exports.WASI_ENOENT = 44; + exports.WASI_ENOEXEC = 45; + exports.WASI_ENOLCK = 46; + exports.WASI_ENOLINK = 47; + exports.WASI_ENOMEM = 48; + exports.WASI_ENOMSG = 49; + exports.WASI_ENOPROTOOPT = 50; + exports.WASI_ENOSPC = 51; + exports.WASI_ENOSYS = 52; + exports.WASI_ENOTCONN = 53; + exports.WASI_ENOTDIR = 54; + exports.WASI_ENOTEMPTY = 55; + exports.WASI_ENOTRECOVERABLE = 56; + exports.WASI_ENOTSOCK = 57; + exports.WASI_ENOTSUP = 58; + exports.WASI_ENOTTY = 59; + exports.WASI_ENXIO = 60; + exports.WASI_EOVERFLOW = 61; + exports.WASI_EOWNERDEAD = 62; + exports.WASI_EPERM = 63; + exports.WASI_EPIPE = 64; + exports.WASI_EPROTO = 65; + exports.WASI_EPROTONOSUPPORT = 66; + exports.WASI_EPROTOTYPE = 67; + exports.WASI_ERANGE = 68; + exports.WASI_EROFS = 69; + exports.WASI_ESPIPE = 70; + exports.WASI_ESRCH = 71; + exports.WASI_ESTALE = 72; + exports.WASI_ETIMEDOUT = 73; + exports.WASI_ETXTBSY = 74; + exports.WASI_EXDEV = 75; + exports.WASI_ENOTCAPABLE = 76; + exports.WASI_SIGABRT = 0; + exports.WASI_SIGALRM = 1; + exports.WASI_SIGBUS = 2; + exports.WASI_SIGCHLD = 3; + exports.WASI_SIGCONT = 4; + exports.WASI_SIGFPE = 5; + exports.WASI_SIGHUP = 6; + exports.WASI_SIGILL = 7; + exports.WASI_SIGINT = 8; + exports.WASI_SIGKILL = 9; + exports.WASI_SIGPIPE = 10; + exports.WASI_SIGQUIT = 11; + exports.WASI_SIGSEGV = 12; + exports.WASI_SIGSTOP = 13; + exports.WASI_SIGTERM = 14; + exports.WASI_SIGTRAP = 15; + exports.WASI_SIGTSTP = 16; + exports.WASI_SIGTTIN = 17; + exports.WASI_SIGTTOU = 18; + exports.WASI_SIGURG = 19; + exports.WASI_SIGUSR1 = 20; + exports.WASI_SIGUSR2 = 21; + exports.WASI_SIGVTALRM = 22; + exports.WASI_SIGXCPU = 23; + exports.WASI_SIGXFSZ = 24; + exports.WASI_FILETYPE_UNKNOWN = 0; + exports.WASI_FILETYPE_BLOCK_DEVICE = 1; + exports.WASI_FILETYPE_CHARACTER_DEVICE = 2; + exports.WASI_FILETYPE_DIRECTORY = 3; + exports.WASI_FILETYPE_REGULAR_FILE = 4; + exports.WASI_FILETYPE_SOCKET_DGRAM = 5; + exports.WASI_FILETYPE_SOCKET_STREAM = 6; + exports.WASI_FILETYPE_SYMBOLIC_LINK = 7; + exports.WASI_FDFLAG_APPEND = 1; + exports.WASI_FDFLAG_DSYNC = 2; + exports.WASI_FDFLAG_NONBLOCK = 4; + exports.WASI_FDFLAG_RSYNC = 8; + exports.WASI_FDFLAG_SYNC = 16; + exports.WASI_RIGHT_FD_DATASYNC = BigInt(1); + exports.WASI_RIGHT_FD_READ = BigInt(2); + exports.WASI_RIGHT_FD_SEEK = BigInt(4); + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS = BigInt(8); + exports.WASI_RIGHT_FD_SYNC = BigInt(16); + exports.WASI_RIGHT_FD_TELL = BigInt(32); + exports.WASI_RIGHT_FD_WRITE = BigInt(64); + exports.WASI_RIGHT_FD_ADVISE = BigInt(128); + exports.WASI_RIGHT_FD_ALLOCATE = BigInt(256); + exports.WASI_RIGHT_PATH_CREATE_DIRECTORY = BigInt(512); + exports.WASI_RIGHT_PATH_CREATE_FILE = BigInt(1024); + exports.WASI_RIGHT_PATH_LINK_SOURCE = BigInt(2048); + exports.WASI_RIGHT_PATH_LINK_TARGET = BigInt(4096); + exports.WASI_RIGHT_PATH_OPEN = BigInt(8192); + exports.WASI_RIGHT_FD_READDIR = BigInt(16384); + exports.WASI_RIGHT_PATH_READLINK = BigInt(32768); + exports.WASI_RIGHT_PATH_RENAME_SOURCE = BigInt(65536); + exports.WASI_RIGHT_PATH_RENAME_TARGET = BigInt(131072); + exports.WASI_RIGHT_PATH_FILESTAT_GET = BigInt(262144); + exports.WASI_RIGHT_PATH_FILESTAT_SET_SIZE = BigInt(524288); + exports.WASI_RIGHT_PATH_FILESTAT_SET_TIMES = BigInt(1048576); + exports.WASI_RIGHT_FD_FILESTAT_GET = BigInt(2097152); + exports.WASI_RIGHT_FD_FILESTAT_SET_SIZE = BigInt(4194304); + exports.WASI_RIGHT_FD_FILESTAT_SET_TIMES = BigInt(8388608); + exports.WASI_RIGHT_PATH_SYMLINK = BigInt(16777216); + exports.WASI_RIGHT_PATH_REMOVE_DIRECTORY = BigInt(33554432); + exports.WASI_RIGHT_PATH_UNLINK_FILE = BigInt(67108864); + exports.WASI_RIGHT_POLL_FD_READWRITE = BigInt(134217728); + exports.WASI_RIGHT_SOCK_SHUTDOWN = BigInt(268435456); + exports.RIGHTS_ALL = + exports.WASI_RIGHT_FD_DATASYNC | + exports.WASI_RIGHT_FD_READ | + exports.WASI_RIGHT_FD_SEEK | + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + exports.WASI_RIGHT_FD_SYNC | + exports.WASI_RIGHT_FD_TELL | + exports.WASI_RIGHT_FD_WRITE | + exports.WASI_RIGHT_FD_ADVISE | + exports.WASI_RIGHT_FD_ALLOCATE | + exports.WASI_RIGHT_PATH_CREATE_DIRECTORY | + exports.WASI_RIGHT_PATH_CREATE_FILE | + exports.WASI_RIGHT_PATH_LINK_SOURCE | + exports.WASI_RIGHT_PATH_LINK_TARGET | + exports.WASI_RIGHT_PATH_OPEN | + exports.WASI_RIGHT_FD_READDIR | + exports.WASI_RIGHT_PATH_READLINK | + exports.WASI_RIGHT_PATH_RENAME_SOURCE | + exports.WASI_RIGHT_PATH_RENAME_TARGET | + exports.WASI_RIGHT_PATH_FILESTAT_GET | + exports.WASI_RIGHT_PATH_FILESTAT_SET_SIZE | + exports.WASI_RIGHT_PATH_FILESTAT_SET_TIMES | + exports.WASI_RIGHT_FD_FILESTAT_GET | + exports.WASI_RIGHT_FD_FILESTAT_SET_TIMES | + exports.WASI_RIGHT_FD_FILESTAT_SET_SIZE | + exports.WASI_RIGHT_PATH_SYMLINK | + exports.WASI_RIGHT_PATH_UNLINK_FILE | + exports.WASI_RIGHT_PATH_REMOVE_DIRECTORY | + exports.WASI_RIGHT_POLL_FD_READWRITE | + exports.WASI_RIGHT_SOCK_SHUTDOWN; + exports.RIGHTS_BLOCK_DEVICE_BASE = exports.RIGHTS_ALL; + exports.RIGHTS_BLOCK_DEVICE_INHERITING = exports.RIGHTS_ALL; + exports.RIGHTS_CHARACTER_DEVICE_BASE = exports.RIGHTS_ALL; + exports.RIGHTS_CHARACTER_DEVICE_INHERITING = exports.RIGHTS_ALL; + exports.RIGHTS_REGULAR_FILE_BASE = + exports.WASI_RIGHT_FD_DATASYNC | + exports.WASI_RIGHT_FD_READ | + exports.WASI_RIGHT_FD_SEEK | + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + exports.WASI_RIGHT_FD_SYNC | + exports.WASI_RIGHT_FD_TELL | + exports.WASI_RIGHT_FD_WRITE | + exports.WASI_RIGHT_FD_ADVISE | + exports.WASI_RIGHT_FD_ALLOCATE | + exports.WASI_RIGHT_FD_FILESTAT_GET | + exports.WASI_RIGHT_FD_FILESTAT_SET_SIZE | + exports.WASI_RIGHT_FD_FILESTAT_SET_TIMES | + exports.WASI_RIGHT_POLL_FD_READWRITE; + exports.RIGHTS_REGULAR_FILE_INHERITING = BigInt(0); + exports.RIGHTS_DIRECTORY_BASE = + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + exports.WASI_RIGHT_FD_SYNC | + exports.WASI_RIGHT_FD_ADVISE | + exports.WASI_RIGHT_PATH_CREATE_DIRECTORY | + exports.WASI_RIGHT_PATH_CREATE_FILE | + exports.WASI_RIGHT_PATH_LINK_SOURCE | + exports.WASI_RIGHT_PATH_LINK_TARGET | + exports.WASI_RIGHT_PATH_OPEN | + exports.WASI_RIGHT_FD_READDIR | + exports.WASI_RIGHT_PATH_READLINK | + exports.WASI_RIGHT_PATH_RENAME_SOURCE | + exports.WASI_RIGHT_PATH_RENAME_TARGET | + exports.WASI_RIGHT_PATH_FILESTAT_GET | + exports.WASI_RIGHT_PATH_FILESTAT_SET_SIZE | + exports.WASI_RIGHT_PATH_FILESTAT_SET_TIMES | + exports.WASI_RIGHT_FD_FILESTAT_GET | + exports.WASI_RIGHT_FD_FILESTAT_SET_TIMES | + exports.WASI_RIGHT_PATH_SYMLINK | + exports.WASI_RIGHT_PATH_UNLINK_FILE | + exports.WASI_RIGHT_PATH_REMOVE_DIRECTORY | + exports.WASI_RIGHT_POLL_FD_READWRITE; + exports.RIGHTS_DIRECTORY_INHERITING = + exports.RIGHTS_DIRECTORY_BASE | exports.RIGHTS_REGULAR_FILE_BASE; + exports.RIGHTS_SOCKET_BASE = + exports.WASI_RIGHT_FD_READ | + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + exports.WASI_RIGHT_FD_WRITE | + exports.WASI_RIGHT_FD_FILESTAT_GET | + exports.WASI_RIGHT_POLL_FD_READWRITE | + exports.WASI_RIGHT_SOCK_SHUTDOWN; + exports.RIGHTS_SOCKET_INHERITING = exports.RIGHTS_ALL; + exports.RIGHTS_TTY_BASE = + exports.WASI_RIGHT_FD_READ | + exports.WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + exports.WASI_RIGHT_FD_WRITE | + exports.WASI_RIGHT_FD_FILESTAT_GET | + exports.WASI_RIGHT_POLL_FD_READWRITE; + exports.RIGHTS_TTY_INHERITING = BigInt(0); + exports.WASI_CLOCK_REALTIME = 0; + exports.WASI_CLOCK_MONOTONIC = 1; + exports.WASI_CLOCK_PROCESS_CPUTIME_ID = 2; + exports.WASI_CLOCK_THREAD_CPUTIME_ID = 3; + exports.WASI_EVENTTYPE_CLOCK = 0; + exports.WASI_EVENTTYPE_FD_READ = 1; + exports.WASI_EVENTTYPE_FD_WRITE = 2; + exports.WASI_FILESTAT_SET_ATIM = 1 << 0; + exports.WASI_FILESTAT_SET_ATIM_NOW = 1 << 1; + exports.WASI_FILESTAT_SET_MTIM = 1 << 2; + exports.WASI_FILESTAT_SET_MTIM_NOW = 1 << 3; + exports.WASI_O_CREAT = 1 << 0; + exports.WASI_O_DIRECTORY = 1 << 1; + exports.WASI_O_EXCL = 1 << 2; + exports.WASI_O_TRUNC = 1 << 3; + exports.WASI_PREOPENTYPE_DIR = 0; + exports.WASI_DIRCOOKIE_START = 0; + exports.WASI_STDIN_FILENO = 0; + exports.WASI_STDOUT_FILENO = 1; + exports.WASI_STDERR_FILENO = 2; + exports.WASI_WHENCE_SET = 0; + exports.WASI_WHENCE_CUR = 1; + exports.WASI_WHENCE_END = 2; + exports.ERROR_MAP = { + E2BIG: exports.WASI_E2BIG, + EACCES: exports.WASI_EACCES, + EADDRINUSE: exports.WASI_EADDRINUSE, + EADDRNOTAVAIL: exports.WASI_EADDRNOTAVAIL, + EAFNOSUPPORT: exports.WASI_EAFNOSUPPORT, + EALREADY: exports.WASI_EALREADY, + EAGAIN: exports.WASI_EAGAIN, + EBADF: exports.WASI_EBADF, + EBADMSG: exports.WASI_EBADMSG, + EBUSY: exports.WASI_EBUSY, + ECANCELED: exports.WASI_ECANCELED, + ECHILD: exports.WASI_ECHILD, + ECONNABORTED: exports.WASI_ECONNABORTED, + ECONNREFUSED: exports.WASI_ECONNREFUSED, + ECONNRESET: exports.WASI_ECONNRESET, + EDEADLOCK: exports.WASI_EDEADLK, + EDESTADDRREQ: exports.WASI_EDESTADDRREQ, + EDOM: exports.WASI_EDOM, + EDQUOT: exports.WASI_EDQUOT, + EEXIST: exports.WASI_EEXIST, + EFAULT: exports.WASI_EFAULT, + EFBIG: exports.WASI_EFBIG, + EHOSTDOWN: exports.WASI_EHOSTUNREACH, + EHOSTUNREACH: exports.WASI_EHOSTUNREACH, + EIDRM: exports.WASI_EIDRM, + EILSEQ: exports.WASI_EILSEQ, + EINPROGRESS: exports.WASI_EINPROGRESS, + EINTR: exports.WASI_EINTR, + EINVAL: exports.WASI_EINVAL, + EIO: exports.WASI_EIO, + EISCONN: exports.WASI_EISCONN, + EISDIR: exports.WASI_EISDIR, + ELOOP: exports.WASI_ELOOP, + EMFILE: exports.WASI_EMFILE, + EMLINK: exports.WASI_EMLINK, + EMSGSIZE: exports.WASI_EMSGSIZE, + EMULTIHOP: exports.WASI_EMULTIHOP, + ENAMETOOLONG: exports.WASI_ENAMETOOLONG, + ENETDOWN: exports.WASI_ENETDOWN, + ENETRESET: exports.WASI_ENETRESET, + ENETUNREACH: exports.WASI_ENETUNREACH, + ENFILE: exports.WASI_ENFILE, + ENOBUFS: exports.WASI_ENOBUFS, + ENODEV: exports.WASI_ENODEV, + ENOENT: exports.WASI_ENOENT, + ENOEXEC: exports.WASI_ENOEXEC, + ENOLCK: exports.WASI_ENOLCK, + ENOLINK: exports.WASI_ENOLINK, + ENOMEM: exports.WASI_ENOMEM, + ENOMSG: exports.WASI_ENOMSG, + ENOPROTOOPT: exports.WASI_ENOPROTOOPT, + ENOSPC: exports.WASI_ENOSPC, + ENOSYS: exports.WASI_ENOSYS, + ENOTCONN: exports.WASI_ENOTCONN, + ENOTDIR: exports.WASI_ENOTDIR, + ENOTEMPTY: exports.WASI_ENOTEMPTY, + ENOTRECOVERABLE: exports.WASI_ENOTRECOVERABLE, + ENOTSOCK: exports.WASI_ENOTSOCK, + ENOTTY: exports.WASI_ENOTTY, + ENXIO: exports.WASI_ENXIO, + EOVERFLOW: exports.WASI_EOVERFLOW, + EOWNERDEAD: exports.WASI_EOWNERDEAD, + EPERM: exports.WASI_EPERM, + EPIPE: exports.WASI_EPIPE, + EPROTO: exports.WASI_EPROTO, + EPROTONOSUPPORT: exports.WASI_EPROTONOSUPPORT, + EPROTOTYPE: exports.WASI_EPROTOTYPE, + ERANGE: exports.WASI_ERANGE, + EROFS: exports.WASI_EROFS, + ESPIPE: exports.WASI_ESPIPE, + ESRCH: exports.WASI_ESRCH, + ESTALE: exports.WASI_ESTALE, + ETIMEDOUT: exports.WASI_ETIMEDOUT, + ETXTBSY: exports.WASI_ETXTBSY, + EXDEV: exports.WASI_EXDEV, + }; + exports.SIGNAL_MAP = { + [exports.WASI_SIGHUP]: "SIGHUP", + [exports.WASI_SIGINT]: "SIGINT", + [exports.WASI_SIGQUIT]: "SIGQUIT", + [exports.WASI_SIGILL]: "SIGILL", + [exports.WASI_SIGTRAP]: "SIGTRAP", + [exports.WASI_SIGABRT]: "SIGABRT", + [exports.WASI_SIGBUS]: "SIGBUS", + [exports.WASI_SIGFPE]: "SIGFPE", + [exports.WASI_SIGKILL]: "SIGKILL", + [exports.WASI_SIGUSR1]: "SIGUSR1", + [exports.WASI_SIGSEGV]: "SIGSEGV", + [exports.WASI_SIGUSR2]: "SIGUSR2", + [exports.WASI_SIGPIPE]: "SIGPIPE", + [exports.WASI_SIGALRM]: "SIGALRM", + [exports.WASI_SIGTERM]: "SIGTERM", + [exports.WASI_SIGCHLD]: "SIGCHLD", + [exports.WASI_SIGCONT]: "SIGCONT", + [exports.WASI_SIGSTOP]: "SIGSTOP", + [exports.WASI_SIGTSTP]: "SIGTSTP", + [exports.WASI_SIGTTIN]: "SIGTTIN", + [exports.WASI_SIGTTOU]: "SIGTTOU", + [exports.WASI_SIGURG]: "SIGURG", + [exports.WASI_SIGXCPU]: "SIGXCPU", + [exports.WASI_SIGXFSZ]: "SIGXFSZ", + [exports.WASI_SIGVTALRM]: "SIGVTALRM", + }; + }, +}); + +// node_modules/wasi-js/dist/wasi.js +var require_wasi = __commonJS({ + "node_modules/wasi-js/dist/wasi.js"(exports) { + var __importDefault = + (exports && exports.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.SOCKET_DEFAULT_RIGHTS = void 0; + var log = () => {}; + var logOpen = () => {}; + var SC_OPEN_MAX = 32768; + var types_1 = require_types(); + + var constants_1 = require_constants(); + var STDIN_DEFAULT_RIGHTS = + constants_1.WASI_RIGHT_FD_DATASYNC | + constants_1.WASI_RIGHT_FD_READ | + constants_1.WASI_RIGHT_FD_SYNC | + constants_1.WASI_RIGHT_FD_ADVISE | + constants_1.WASI_RIGHT_FD_FILESTAT_GET | + constants_1.WASI_RIGHT_POLL_FD_READWRITE; + var STDOUT_DEFAULT_RIGHTS = + constants_1.WASI_RIGHT_FD_DATASYNC | + constants_1.WASI_RIGHT_FD_WRITE | + constants_1.WASI_RIGHT_FD_SYNC | + constants_1.WASI_RIGHT_FD_ADVISE | + constants_1.WASI_RIGHT_FD_FILESTAT_GET | + constants_1.WASI_RIGHT_POLL_FD_READWRITE; + var STDERR_DEFAULT_RIGHTS = STDOUT_DEFAULT_RIGHTS; + exports.SOCKET_DEFAULT_RIGHTS = + constants_1.WASI_RIGHT_FD_DATASYNC | + constants_1.WASI_RIGHT_FD_READ | + constants_1.WASI_RIGHT_FD_WRITE | + constants_1.WASI_RIGHT_FD_ADVISE | + constants_1.WASI_RIGHT_FD_FILESTAT_GET | + constants_1.WASI_RIGHT_POLL_FD_READWRITE | + constants_1.WASI_RIGHT_FD_FDSTAT_SET_FLAGS; + var msToNs = (ms) => { + const msInt = Math.trunc(ms); + const decimal = BigInt(Math.round((ms - msInt) * 1e6)); + const ns = BigInt(msInt) * BigInt(1e6); + return ns + decimal; + }; + var nsToMs = (ns) => { + if (typeof ns === "number") { + ns = Math.trunc(ns); + } + const nsInt = BigInt(ns); + return Number(nsInt / BigInt(1e6)); + }; + var wrap = + (f) => + (...args) => { + try { + return f(...args); + } catch (err) { + let e = err; + while (e.prev != null) { + e = e.prev; + } + if (e?.code && typeof e?.code === "string") { + return constants_1.ERROR_MAP[e.code] || constants_1.WASI_EINVAL; + } + if (e instanceof types_1.WASIError) { + return e.errno; + } + throw e; + } + }; + var stat = (wasi, fd) => { + const entry = wasi.FD_MAP.get(fd); + if (!entry) { + throw new types_1.WASIError(constants_1.WASI_EBADF); + } + if (entry.filetype === void 0) { + const stats = wasi.fstatSync(entry.real); + const { filetype, rightsBase, rightsInheriting } = + translateFileAttributes(wasi, fd, stats); + entry.filetype = filetype; + if (!entry.rights) { + entry.rights = { + base: rightsBase, + inheriting: rightsInheriting, + }; + } + } + return entry; + }; + var translateFileAttributes = (wasi, fd, stats) => { + switch (true) { + case stats.isBlockDevice(): + return { + filetype: constants_1.WASI_FILETYPE_BLOCK_DEVICE, + rightsBase: constants_1.RIGHTS_BLOCK_DEVICE_BASE, + rightsInheriting: constants_1.RIGHTS_BLOCK_DEVICE_INHERITING, + }; + case stats.isCharacterDevice(): { + const filetype = constants_1.WASI_FILETYPE_CHARACTER_DEVICE; + if (fd !== void 0 && wasi.bindings.isTTY(fd)) { + return { + filetype, + rightsBase: constants_1.RIGHTS_TTY_BASE, + rightsInheriting: constants_1.RIGHTS_TTY_INHERITING, + }; + } + return { + filetype, + rightsBase: constants_1.RIGHTS_CHARACTER_DEVICE_BASE, + rightsInheriting: constants_1.RIGHTS_CHARACTER_DEVICE_INHERITING, + }; + } + case stats.isDirectory(): + return { + filetype: constants_1.WASI_FILETYPE_DIRECTORY, + rightsBase: constants_1.RIGHTS_DIRECTORY_BASE, + rightsInheriting: constants_1.RIGHTS_DIRECTORY_INHERITING, + }; + case stats.isFIFO(): + return { + filetype: constants_1.WASI_FILETYPE_SOCKET_STREAM, + rightsBase: constants_1.RIGHTS_SOCKET_BASE, + rightsInheriting: constants_1.RIGHTS_SOCKET_INHERITING, + }; + case stats.isFile(): + return { + filetype: constants_1.WASI_FILETYPE_REGULAR_FILE, + rightsBase: constants_1.RIGHTS_REGULAR_FILE_BASE, + rightsInheriting: constants_1.RIGHTS_REGULAR_FILE_INHERITING, + }; + case stats.isSocket(): + return { + filetype: constants_1.WASI_FILETYPE_SOCKET_STREAM, + rightsBase: constants_1.RIGHTS_SOCKET_BASE, + rightsInheriting: constants_1.RIGHTS_SOCKET_INHERITING, + }; + case stats.isSymbolicLink(): + return { + filetype: constants_1.WASI_FILETYPE_SYMBOLIC_LINK, + rightsBase: BigInt(0), + rightsInheriting: BigInt(0), + }; + default: + return { + filetype: constants_1.WASI_FILETYPE_UNKNOWN, + rightsBase: BigInt(0), + rightsInheriting: BigInt(0), + }; + } + }; + var warnedAboutSleep = false; + + var defaultConfig; + function getDefaults() { + if (defaultConfig) return defaultConfig; + + const defaultBindings = { + hrtime: () => process.hrtime.bigint(), + exit: (code) => { + process.exit(code); + }, + kill: (signal) => { + process.kill(process.pid, signal); + }, + randomFillSync: (array) => crypto.getRandomValues(array), + isTTY: (fd) => import.meta.require("node:tty").isatty(fd), + fs: Bun.fs(), + path: import.meta.require("node:path"), + }; + + return (defaultConfig = { + args: [], + env: {}, + preopens: {}, + bindings: defaultBindings, + sleep: (ms) => { + Bun.sleepSync(ms); + }, + }); + } + + var WASI = class WASI { + constructor(wasiConfig = {}) { + const defaultConfig = getDefaults(); + this.lastStdin = 0; + this.sleep = wasiConfig.sleep || defaultConfig.sleep; + this.getStdin = wasiConfig.getStdin; + this.sendStdout = wasiConfig.sendStdout; + this.sendStderr = wasiConfig.sendStderr; + let preopens = wasiConfig.preopens ?? defaultConfig.preopens; + this.env = wasiConfig.env ?? defaultConfig.env; + + const args = wasiConfig.args ?? defaultConfig.args; + this.memory = void 0; + this.view = void 0; + this.bindings = wasiConfig.bindings || defaultConfig.bindings; + const bindings = this.bindings; + const fs = bindings.fs; + this.FD_MAP = /* @__PURE__ */ new Map([ + [ + constants_1.WASI_STDIN_FILENO, + { + real: 0, + filetype: constants_1.WASI_FILETYPE_CHARACTER_DEVICE, + rights: { + base: STDIN_DEFAULT_RIGHTS, + inheriting: BigInt(0), + }, + path: "/dev/stdin", + }, + ], + [ + constants_1.WASI_STDOUT_FILENO, + { + real: 1, + filetype: constants_1.WASI_FILETYPE_CHARACTER_DEVICE, + rights: { + base: STDOUT_DEFAULT_RIGHTS, + inheriting: BigInt(0), + }, + path: "/dev/stdout", + }, + ], + [ + constants_1.WASI_STDERR_FILENO, + { + real: 2, + filetype: constants_1.WASI_FILETYPE_CHARACTER_DEVICE, + rights: { + base: STDERR_DEFAULT_RIGHTS, + inheriting: BigInt(0), + }, + path: "/dev/stderr", + }, + ], + ]); + const path = bindings.path; + for (const [k, v] of Object.entries(preopens)) { + const real = fs.openSync(v, nodeFsConstants.O_RDONLY); + const newfd = this.getUnusedFileDescriptor(); + this.FD_MAP.set(newfd, { + real, + filetype: constants_1.WASI_FILETYPE_DIRECTORY, + rights: { + base: constants_1.RIGHTS_DIRECTORY_BASE, + inheriting: constants_1.RIGHTS_DIRECTORY_INHERITING, + }, + fakePath: k, + path: v, + }); + } + const getiovs = (iovs, iovsLen) => { + this.refreshMemory(); + + const { view, memory } = this; + const { buffer } = memory; + const { byteLength } = buffer; + + if (iovsLen === 1) { + const ptr = iovs; + const buf = view.getUint32(ptr, true); + let bufLen = view.getUint32(ptr + 4, true); + + if (bufLen > byteLength - buf) { + console.log({ + buf, + bufLen, + total_memory: byteLength, + }); + log("getiovs: warning -- truncating buffer to fit in memory"); + bufLen = Math.min(bufLen, Math.max(0, byteLength - buf)); + } + try { + return [new Uint8Array(buffer, buf, bufLen)]; + } catch (err) { + console.warn("WASI.getiovs -- invalid buffer", err); + throw new types_1.WASIError(constants_1.WASI_EINVAL); + } + } + + // Avoid referencing Array because materializing the Array constructor can show up in profiling + const buffers = []; + buffers.length = iovsLen; + + for (let i = 0, ptr = iovs; i < iovsLen; i++, ptr += 8) { + const buf = view.getUint32(ptr, true); + let bufLen = view.getUint32(ptr + 4, true); + + if (bufLen > byteLength - buf) { + console.log({ + buf, + bufLen, + total_memory: byteLength, + }); + log("getiovs: warning -- truncating buffer to fit in memory"); + bufLen = Math.min(bufLen, Math.max(0, byteLength - buf)); + } + try { + buffers[i] = new Uint8Array(buffer, buf, bufLen); + } catch (err) { + console.warn("WASI.getiovs -- invalid buffer", err); + throw new types_1.WASIError(constants_1.WASI_EINVAL); + } + } + return buffers; + }; + const CHECK_FD = (fd, rights) => { + const stats = stat(this, fd); + if ( + rights !== BigInt(0) && + (stats.rights.base & rights) === BigInt(0) + ) { + throw new types_1.WASIError(constants_1.WASI_EPERM); + } + return stats; + }; + const CPUTIME_START = bindings.hrtime(); + const now = (clockId) => { + switch (clockId) { + case constants_1.WASI_CLOCK_MONOTONIC: + return bindings.hrtime(); + case constants_1.WASI_CLOCK_REALTIME: + return msToNs(Date.now()); + case constants_1.WASI_CLOCK_PROCESS_CPUTIME_ID: + case constants_1.WASI_CLOCK_THREAD_CPUTIME_ID: + return bindings.hrtime() - CPUTIME_START; + default: + return null; + } + }; + this.wasiImport = { + args_get: (argv, argvBuf) => { + this.refreshMemory(); + let coffset = argv; + let offset = argvBuf; + args.forEach((a) => { + this.view.setUint32(coffset, offset, true); + coffset += 4; + offset += Buffer.from(this.memory.buffer).write(`${a}\0`, offset); + }); + return constants_1.WASI_ESUCCESS; + }, + args_sizes_get: (argc, argvBufSize) => { + this.refreshMemory(); + this.view.setUint32(argc, args.length, true); + const size = args.reduce( + (acc, a) => acc + Buffer.byteLength(a) + 1, + 0, + ); + this.view.setUint32(argvBufSize, size, true); + return constants_1.WASI_ESUCCESS; + }, + environ_get: (environ, environBuf) => { + this.refreshMemory(); + let coffset = environ; + let offset = environBuf; + Object.entries(this.env).forEach(([key, value]) => { + this.view.setUint32(coffset, offset, true); + coffset += 4; + offset += Buffer.from(this.memory.buffer).write( + `${key}=${value}\0`, + offset, + ); + }); + return constants_1.WASI_ESUCCESS; + }, + environ_sizes_get: (environCount, environBufSize) => { + this.refreshMemory(); + const envProcessed = Object.entries(this.env).map( + ([key, value]) => `${key}=${value}\0`, + ); + const size = envProcessed.reduce( + (acc, e) => acc + Buffer.byteLength(e), + 0, + ); + this.view.setUint32(environCount, envProcessed.length, true); + this.view.setUint32(environBufSize, size, true); + return constants_1.WASI_ESUCCESS; + }, + clock_res_get: (clockId, resolution) => { + let res; + switch (clockId) { + case constants_1.WASI_CLOCK_MONOTONIC: + case constants_1.WASI_CLOCK_PROCESS_CPUTIME_ID: + case constants_1.WASI_CLOCK_THREAD_CPUTIME_ID: { + res = BigInt(1); + break; + } + case constants_1.WASI_CLOCK_REALTIME: { + res = BigInt(1e3); + break; + } + } + if (!res) { + throw Error("invalid clockId"); + } + this.view.setBigUint64(resolution, res); + return constants_1.WASI_ESUCCESS; + }, + clock_time_get: (clockId, _precision, time) => { + this.refreshMemory(); + const n = now(clockId); + if (n === null) { + return constants_1.WASI_EINVAL; + } + this.view.setBigUint64(time, BigInt(n), true); + return constants_1.WASI_ESUCCESS; + }, + fd_advise: wrap((fd, _offset, _len, _advice) => { + CHECK_FD(fd, constants_1.WASI_RIGHT_FD_ADVISE); + return constants_1.WASI_ENOSYS; + }), + fd_allocate: wrap((fd, _offset, _len) => { + CHECK_FD(fd, constants_1.WASI_RIGHT_FD_ALLOCATE); + return constants_1.WASI_ENOSYS; + }), + fd_close: wrap((fd) => { + const stats = CHECK_FD(fd, BigInt(0)); + fs.closeSync(stats.real); + this.FD_MAP.delete(fd); + return constants_1.WASI_ESUCCESS; + }), + fd_datasync: wrap((fd) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_DATASYNC); + fs.fdatasyncSync(stats.real); + return constants_1.WASI_ESUCCESS; + }), + fd_fdstat_get: wrap((fd, bufPtr) => { + const stats = CHECK_FD(fd, BigInt(0)); + this.refreshMemory(); + if (stats.filetype == null) { + throw Error("stats.filetype must be set"); + } + this.view.setUint8(bufPtr, stats.filetype); + this.view.setUint16(bufPtr + 2, 0, true); + this.view.setUint16(bufPtr + 4, 0, true); + this.view.setBigUint64(bufPtr + 8, BigInt(stats.rights.base), true); + this.view.setBigUint64( + bufPtr + 8 + 8, + BigInt(stats.rights.inheriting), + true, + ); + return constants_1.WASI_ESUCCESS; + }), + fd_fdstat_set_flags: wrap((fd, flags) => { + CHECK_FD(fd, constants_1.WASI_RIGHT_FD_FDSTAT_SET_FLAGS); + if (this.wasiImport.sock_fcntlSetFlags(fd, flags) == 0) { + return constants_1.WASI_ESUCCESS; + } + return constants_1.WASI_ENOSYS; + }), + fd_fdstat_set_rights: wrap((fd, fsRightsBase, fsRightsInheriting) => { + const stats = CHECK_FD(fd, BigInt(0)); + const nrb = stats.rights.base | fsRightsBase; + if (nrb > stats.rights.base) { + return constants_1.WASI_EPERM; + } + const nri = stats.rights.inheriting | fsRightsInheriting; + if (nri > stats.rights.inheriting) { + return constants_1.WASI_EPERM; + } + stats.rights.base = fsRightsBase; + stats.rights.inheriting = fsRightsInheriting; + return constants_1.WASI_ESUCCESS; + }), + fd_filestat_get: wrap((fd, bufPtr) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_FILESTAT_GET); + const rstats = this.fstatSync(stats.real); + this.refreshMemory(); + this.view.setBigUint64(bufPtr, BigInt(rstats.dev), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, BigInt(rstats.ino), true); + bufPtr += 8; + if (stats.filetype == null) { + throw Error("stats.filetype must be set"); + } + this.view.setUint8(bufPtr, stats.filetype); + bufPtr += 8; + this.view.setBigUint64(bufPtr, BigInt(rstats.nlink), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, BigInt(rstats.size), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, msToNs(rstats.atimeMs), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, msToNs(rstats.mtimeMs), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, msToNs(rstats.ctimeMs), true); + return constants_1.WASI_ESUCCESS; + }), + fd_filestat_set_size: wrap((fd, stSize) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_FD_FILESTAT_SET_SIZE, + ); + fs.ftruncateSync(stats.real, Number(stSize)); + return constants_1.WASI_ESUCCESS; + }), + fd_filestat_set_times: wrap((fd, stAtim, stMtim, fstflags) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_FD_FILESTAT_SET_TIMES, + ); + const rstats = this.fstatSync(stats.real); + let atim = rstats.atime; + let mtim = rstats.mtime; + const n = nsToMs(now(constants_1.WASI_CLOCK_REALTIME)); + const atimflags = + constants_1.WASI_FILESTAT_SET_ATIM | + constants_1.WASI_FILESTAT_SET_ATIM_NOW; + if ((fstflags & atimflags) === atimflags) { + return constants_1.WASI_EINVAL; + } + const mtimflags = + constants_1.WASI_FILESTAT_SET_MTIM | + constants_1.WASI_FILESTAT_SET_MTIM_NOW; + if ((fstflags & mtimflags) === mtimflags) { + return constants_1.WASI_EINVAL; + } + if ( + (fstflags & constants_1.WASI_FILESTAT_SET_ATIM) === + constants_1.WASI_FILESTAT_SET_ATIM + ) { + atim = nsToMs(stAtim); + } else if ( + (fstflags & constants_1.WASI_FILESTAT_SET_ATIM_NOW) === + constants_1.WASI_FILESTAT_SET_ATIM_NOW + ) { + atim = n; + } + if ( + (fstflags & constants_1.WASI_FILESTAT_SET_MTIM) === + constants_1.WASI_FILESTAT_SET_MTIM + ) { + mtim = nsToMs(stMtim); + } else if ( + (fstflags & constants_1.WASI_FILESTAT_SET_MTIM_NOW) === + constants_1.WASI_FILESTAT_SET_MTIM_NOW + ) { + mtim = n; + } + fs.futimesSync(stats.real, new Date(atim), new Date(mtim)); + return constants_1.WASI_ESUCCESS; + }), + fd_prestat_get: wrap((fd, bufPtr) => { + const stats = CHECK_FD(fd, BigInt(0)); + this.refreshMemory(); + this.view.setUint8(bufPtr, constants_1.WASI_PREOPENTYPE_DIR); + this.view.setUint32( + bufPtr + 4, + Buffer.byteLength(stats.fakePath ?? stats.path ?? ""), + true, + ); + return constants_1.WASI_ESUCCESS; + }), + fd_prestat_dir_name: wrap((fd, pathPtr, pathLen) => { + const stats = CHECK_FD(fd, BigInt(0)); + this.refreshMemory(); + Buffer.from(this.memory.buffer).write( + stats.fakePath ?? stats.path ?? "", + pathPtr, + pathLen, + "utf8", + ); + return constants_1.WASI_ESUCCESS; + }), + fd_pwrite: wrap((fd, iovs, iovsLen, offset, nwritten) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_FD_WRITE | constants_1.WASI_RIGHT_FD_SEEK, + ); + let written = 0; + getiovs(iovs, iovsLen).forEach((iov) => { + let w = 0; + while (w < iov.byteLength) { + w += fs.writeSync( + stats.real, + iov, + w, + iov.byteLength - w, + Number(offset) + written + w, + ); + } + written += w; + }); + this.view.setUint32(nwritten, written, true); + return constants_1.WASI_ESUCCESS; + }), + fd_write: wrap((fd, iovs, iovsLen, nwritten) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_WRITE); + const IS_STDOUT = fd == constants_1.WASI_STDOUT_FILENO; + const IS_STDERR = fd == constants_1.WASI_STDERR_FILENO; + let written = 0; + getiovs(iovs, iovsLen).forEach((iov) => { + if (iov.byteLength == 0) return; + if (IS_STDOUT && this.sendStdout != null) { + this.sendStdout(iov); + written += iov.byteLength; + } else if (IS_STDERR && this.sendStderr != null) { + this.sendStderr(iov); + written += iov.byteLength; + } else { + let w = 0; + while (w < iov.byteLength) { + const i = fs.writeSync( + stats.real, + iov, + w, + iov.byteLength - w, + stats.offset ? Number(stats.offset) : null, + ); + if (stats.offset) stats.offset += BigInt(i); + w += i; + } + written += w; + } + }); + this.view.setUint32(nwritten, written, true); + return constants_1.WASI_ESUCCESS; + }), + fd_pread: wrap((fd, iovs, iovsLen, offset, nread) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_FD_READ | constants_1.WASI_RIGHT_FD_SEEK, + ); + let read = 0; + outer: for (const iov of getiovs(iovs, iovsLen)) { + let r = 0; + while (r < iov.byteLength) { + const length = iov.byteLength - r; + const rr = fs.readSync( + stats.real, + iov, + r, + iov.byteLength - r, + Number(offset) + read + r, + ); + r += rr; + read += rr; + if (rr === 0 || rr < length) { + break outer; + } + } + read += r; + } + this.view.setUint32(nread, read, true); + return constants_1.WASI_ESUCCESS; + }), + fd_read: wrap((fd, iovs, iovsLen, nread) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_READ); + const IS_STDIN = fd == constants_1.WASI_STDIN_FILENO; + let read = 0; + outer: for (const iov of getiovs(iovs, iovsLen)) { + let r = 0; + while (r < iov.byteLength) { + let length = iov.byteLength - r; + let position = + IS_STDIN || stats.offset === void 0 + ? null + : Number(stats.offset); + let rr = 0; + if (IS_STDIN) { + if (this.getStdin != null) { + if (this.stdinBuffer == null) { + this.stdinBuffer = this.getStdin(); + } + if (this.stdinBuffer != null) { + rr = this.stdinBuffer.copy(iov); + if (rr == this.stdinBuffer.length) { + this.stdinBuffer = void 0; + } else { + this.stdinBuffer = this.stdinBuffer.slice(rr); + } + if (rr > 0) { + this.lastStdin = new Date().valueOf(); + } + } + } else { + if (this.sleep == null && !warnedAboutSleep) { + warnedAboutSleep = true; + console.log( + "(cpu waiting for stdin: please define a way to sleep!) ", + ); + } + try { + rr = fs.readSync(stats.real, iov, r, length, position); + } catch (_err) {} + if (rr == 0) { + this.shortPause(); + } else { + this.lastStdin = new Date().valueOf(); + } + } + } else { + rr = fs.readSync(stats.real, iov, r, length, position); + } + if (stats.filetype == constants_1.WASI_FILETYPE_REGULAR_FILE) { + stats.offset = + (stats.offset ? stats.offset : BigInt(0)) + BigInt(rr); + } + r += rr; + read += rr; + if (rr === 0 || rr < length) { + break outer; + } + } + } + this.view.setUint32(nread, read, true); + return constants_1.WASI_ESUCCESS; + }), + fd_readdir: wrap((fd, bufPtr, bufLen, cookie, bufusedPtr) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_READDIR); + this.refreshMemory(); + const entries = fs.readdirSync(stats.path, { withFileTypes: true }); + const startPtr = bufPtr; + for (let i = Number(cookie); i < entries.length; i += 1) { + const entry = entries[i]; + let nameLength = Buffer.byteLength(entry.name); + if (bufPtr - startPtr > bufLen) { + break; + } + this.view.setBigUint64(bufPtr, BigInt(i + 1), true); + bufPtr += 8; + if (bufPtr - startPtr > bufLen) { + break; + } + const rstats = fs.lstatSync(path.resolve(stats.path, entry.name)); + this.view.setBigUint64(bufPtr, BigInt(rstats.ino), true); + bufPtr += 8; + if (bufPtr - startPtr > bufLen) { + break; + } + this.view.setUint32(bufPtr, nameLength, true); + bufPtr += 4; + if (bufPtr - startPtr > bufLen) { + break; + } + let filetype; + switch (true) { + case rstats.isBlockDevice(): + filetype = constants_1.WASI_FILETYPE_BLOCK_DEVICE; + break; + case rstats.isCharacterDevice(): + filetype = constants_1.WASI_FILETYPE_CHARACTER_DEVICE; + break; + case rstats.isDirectory(): + filetype = constants_1.WASI_FILETYPE_DIRECTORY; + break; + case rstats.isFIFO(): + filetype = constants_1.WASI_FILETYPE_SOCKET_STREAM; + break; + case rstats.isFile(): + filetype = constants_1.WASI_FILETYPE_REGULAR_FILE; + break; + case rstats.isSocket(): + filetype = constants_1.WASI_FILETYPE_SOCKET_STREAM; + break; + case rstats.isSymbolicLink(): + filetype = constants_1.WASI_FILETYPE_SYMBOLIC_LINK; + break; + default: + filetype = constants_1.WASI_FILETYPE_UNKNOWN; + break; + } + this.view.setUint8(bufPtr, filetype); + bufPtr += 1; + bufPtr += 3; + if (bufPtr + nameLength >= startPtr + bufLen) { + break; + } + let memory_buffer = Buffer.from(this.memory.buffer); + memory_buffer.write(entry.name, bufPtr); + bufPtr += nameLength; + } + const bufused = bufPtr - startPtr; + this.view.setUint32(bufusedPtr, Math.min(bufused, bufLen), true); + return constants_1.WASI_ESUCCESS; + }), + fd_renumber: wrap((from, to) => { + CHECK_FD(from, BigInt(0)); + CHECK_FD(to, BigInt(0)); + fs.closeSync(this.FD_MAP.get(from).real); + this.FD_MAP.set(from, this.FD_MAP.get(to)); + this.FD_MAP.delete(to); + return constants_1.WASI_ESUCCESS; + }), + fd_seek: wrap((fd, offset, whence, newOffsetPtr) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_SEEK); + this.refreshMemory(); + switch (whence) { + case constants_1.WASI_WHENCE_CUR: + stats.offset = + (stats.offset ? stats.offset : BigInt(0)) + BigInt(offset); + break; + case constants_1.WASI_WHENCE_END: + const { size } = this.fstatSync(stats.real); + stats.offset = BigInt(size) + BigInt(offset); + break; + case constants_1.WASI_WHENCE_SET: + stats.offset = BigInt(offset); + break; + } + if (stats.offset == null) { + throw Error("stats.offset must be defined"); + } + this.view.setBigUint64(newOffsetPtr, stats.offset, true); + return constants_1.WASI_ESUCCESS; + }), + fd_tell: wrap((fd, offsetPtr) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_TELL); + this.refreshMemory(); + if (!stats.offset) { + stats.offset = BigInt(0); + } + this.view.setBigUint64(offsetPtr, stats.offset, true); + return constants_1.WASI_ESUCCESS; + }), + fd_sync: wrap((fd) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_SYNC); + fs.fsyncSync(stats.real); + return constants_1.WASI_ESUCCESS; + }), + path_create_directory: wrap((fd, pathPtr, pathLen) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_PATH_CREATE_DIRECTORY, + ); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + fs.mkdirSync(path.resolve(stats.path, p)); + return constants_1.WASI_ESUCCESS; + }), + path_filestat_get: wrap((fd, flags, pathPtr, pathLen, bufPtr) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_PATH_FILESTAT_GET, + ); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + let rstats; + if (flags) { + rstats = fs.statSync(path.resolve(stats.path, p)); + } else { + rstats = fs.lstatSync(path.resolve(stats.path, p)); + } + this.view.setBigUint64(bufPtr, BigInt(rstats.dev), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, BigInt(rstats.ino), true); + bufPtr += 8; + this.view.setUint8( + bufPtr, + translateFileAttributes(this, void 0, rstats).filetype, + ); + bufPtr += 8; + this.view.setBigUint64(bufPtr, BigInt(rstats.nlink), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, BigInt(rstats.size), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, msToNs(rstats.atimeMs), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, msToNs(rstats.mtimeMs), true); + bufPtr += 8; + this.view.setBigUint64(bufPtr, msToNs(rstats.ctimeMs), true); + return constants_1.WASI_ESUCCESS; + }), + path_filestat_set_times: wrap( + (fd, _dirflags, pathPtr, pathLen, stAtim, stMtim, fstflags) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_PATH_FILESTAT_SET_TIMES, + ); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const rstats = this.fstatSync(stats.real); + let atim = rstats.atime; + let mtim = rstats.mtime; + const n = nsToMs(now(constants_1.WASI_CLOCK_REALTIME)); + const atimflags = + constants_1.WASI_FILESTAT_SET_ATIM | + constants_1.WASI_FILESTAT_SET_ATIM_NOW; + if ((fstflags & atimflags) === atimflags) { + return constants_1.WASI_EINVAL; + } + const mtimflags = + constants_1.WASI_FILESTAT_SET_MTIM | + constants_1.WASI_FILESTAT_SET_MTIM_NOW; + if ((fstflags & mtimflags) === mtimflags) { + return constants_1.WASI_EINVAL; + } + if ( + (fstflags & constants_1.WASI_FILESTAT_SET_ATIM) === + constants_1.WASI_FILESTAT_SET_ATIM + ) { + atim = nsToMs(stAtim); + } else if ( + (fstflags & constants_1.WASI_FILESTAT_SET_ATIM_NOW) === + constants_1.WASI_FILESTAT_SET_ATIM_NOW + ) { + atim = n; + } + if ( + (fstflags & constants_1.WASI_FILESTAT_SET_MTIM) === + constants_1.WASI_FILESTAT_SET_MTIM + ) { + mtim = nsToMs(stMtim); + } else if ( + (fstflags & constants_1.WASI_FILESTAT_SET_MTIM_NOW) === + constants_1.WASI_FILESTAT_SET_MTIM_NOW + ) { + mtim = n; + } + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + fs.utimesSync( + path.resolve(stats.path, p), + new Date(atim), + new Date(mtim), + ); + return constants_1.WASI_ESUCCESS; + }, + ), + path_link: wrap( + ( + oldFd, + _oldFlags, + oldPath, + oldPathLen, + newFd, + newPath, + newPathLen, + ) => { + const ostats = CHECK_FD( + oldFd, + constants_1.WASI_RIGHT_PATH_LINK_SOURCE, + ); + const nstats = CHECK_FD( + newFd, + constants_1.WASI_RIGHT_PATH_LINK_TARGET, + ); + if (!ostats.path || !nstats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const op = Buffer.from( + this.memory.buffer, + oldPath, + oldPathLen, + ).toString(); + const np = Buffer.from( + this.memory.buffer, + newPath, + newPathLen, + ).toString(); + fs.linkSync( + path.resolve(ostats.path, op), + path.resolve(nstats.path, np), + ); + return constants_1.WASI_ESUCCESS; + }, + ), + path_open: wrap( + ( + dirfd, + _dirflags, + pathPtr, + pathLen, + oflags, + fsRightsBase, + fsRightsInheriting, + fsFlags, + fdPtr, + ) => { + try { + const stats = CHECK_FD(dirfd, constants_1.WASI_RIGHT_PATH_OPEN); + fsRightsBase = BigInt(fsRightsBase); + fsRightsInheriting = BigInt(fsRightsInheriting); + const read = + (fsRightsBase & + (constants_1.WASI_RIGHT_FD_READ | + constants_1.WASI_RIGHT_FD_READDIR)) !== + BigInt(0); + const write = + (fsRightsBase & + (constants_1.WASI_RIGHT_FD_DATASYNC | + constants_1.WASI_RIGHT_FD_WRITE | + constants_1.WASI_RIGHT_FD_ALLOCATE | + constants_1.WASI_RIGHT_FD_FILESTAT_SET_SIZE)) !== + BigInt(0); + let noflags; + if (write && read) { + noflags = nodeFsConstants.O_RDWR; + } else if (read) { + noflags = nodeFsConstants.O_RDONLY; + } else if (write) { + noflags = nodeFsConstants.O_WRONLY; + } + let neededBase = + fsRightsBase | constants_1.WASI_RIGHT_PATH_OPEN; + let neededInheriting = fsRightsBase | fsRightsInheriting; + if ((oflags & constants_1.WASI_O_CREAT) !== 0) { + noflags |= nodeFsConstants.O_CREAT; + neededBase |= constants_1.WASI_RIGHT_PATH_CREATE_FILE; + } + if ((oflags & constants_1.WASI_O_DIRECTORY) !== 0) { + noflags |= nodeFsConstants.O_DIRECTORY; + } + if ((oflags & constants_1.WASI_O_EXCL) !== 0) { + noflags |= nodeFsConstants.O_EXCL; + } + if ((oflags & constants_1.WASI_O_TRUNC) !== 0) { + noflags |= nodeFsConstants.O_TRUNC; + neededBase |= constants_1.WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + if ((fsFlags & constants_1.WASI_FDFLAG_APPEND) !== 0) { + noflags |= nodeFsConstants.O_APPEND; + } + if ((fsFlags & constants_1.WASI_FDFLAG_DSYNC) !== 0) { + if (nodeFsConstants.O_DSYNC) { + noflags |= nodeFsConstants.O_DSYNC; + } else { + noflags |= nodeFsConstants.O_SYNC; + } + neededInheriting |= constants_1.WASI_RIGHT_FD_DATASYNC; + } + if ((fsFlags & constants_1.WASI_FDFLAG_NONBLOCK) !== 0) { + noflags |= nodeFsConstants.O_NONBLOCK; + } + if ((fsFlags & constants_1.WASI_FDFLAG_RSYNC) !== 0) { + if (nodeFsConstants.O_RSYNC) { + noflags |= nodeFsConstants.O_RSYNC; + } else { + noflags |= nodeFsConstants.O_SYNC; + } + neededInheriting |= constants_1.WASI_RIGHT_FD_SYNC; + } + if ((fsFlags & constants_1.WASI_FDFLAG_SYNC) !== 0) { + noflags |= nodeFsConstants.O_SYNC; + neededInheriting |= constants_1.WASI_RIGHT_FD_SYNC; + } + if ( + write && + (noflags & + (nodeFsConstants.O_APPEND | nodeFsConstants.O_TRUNC)) === + 0 + ) { + neededInheriting |= constants_1.WASI_RIGHT_FD_SEEK; + } + this.refreshMemory(); + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + if (p == "dev/tty") { + this.view.setUint32( + fdPtr, + constants_1.WASI_STDIN_FILENO, + true, + ); + return constants_1.WASI_ESUCCESS; + } + logOpen("path_open", p); + if (p.startsWith("proc/")) { + throw new types_1.WASIError(constants_1.WASI_EBADF); + } + const fullUnresolved = path.resolve(stats.path, p); + let full; + try { + full = fs.realpathSync(fullUnresolved); + } catch (e) { + if (e?.code === "ENOENT") { + full = fullUnresolved; + } else { + throw e; + } + } + let isDirectory; + if (write) { + try { + isDirectory = fs.statSync(full).isDirectory(); + } catch (_err) {} + } + let realfd; + if (!write && isDirectory) { + realfd = fs.openSync(full, nodeFsConstants.O_RDONLY); + } else { + realfd = fs.openSync(full, noflags); + } + const newfd = this.getUnusedFileDescriptor(); + this.FD_MAP.set(newfd, { + real: realfd, + filetype: void 0, + rights: { + base: neededBase, + inheriting: neededInheriting, + }, + path: full, + }); + stat(this, newfd); + this.view.setUint32(fdPtr, newfd, true); + } catch (e) { + console.error(e); + } + return constants_1.WASI_ESUCCESS; + }, + ), + path_readlink: wrap((fd, pathPtr, pathLen, buf, bufLen, bufused) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_READLINK); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + const full = path.resolve(stats.path, p); + const r = fs.readlinkSync(full); + const used = Buffer.from(this.memory.buffer).write(r, buf, bufLen); + this.view.setUint32(bufused, used, true); + return constants_1.WASI_ESUCCESS; + }), + path_remove_directory: wrap((fd, pathPtr, pathLen) => { + const stats = CHECK_FD( + fd, + constants_1.WASI_RIGHT_PATH_REMOVE_DIRECTORY, + ); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + fs.rmdirSync(path.resolve(stats.path, p)); + return constants_1.WASI_ESUCCESS; + }), + path_rename: wrap( + (oldFd, oldPath, oldPathLen, newFd, newPath, newPathLen) => { + const ostats = CHECK_FD( + oldFd, + constants_1.WASI_RIGHT_PATH_RENAME_SOURCE, + ); + const nstats = CHECK_FD( + newFd, + constants_1.WASI_RIGHT_PATH_RENAME_TARGET, + ); + if (!ostats.path || !nstats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const op = Buffer.from( + this.memory.buffer, + oldPath, + oldPathLen, + ).toString(); + const np = Buffer.from( + this.memory.buffer, + newPath, + newPathLen, + ).toString(); + fs.renameSync( + path.resolve(ostats.path, op), + path.resolve(nstats.path, np), + ); + return constants_1.WASI_ESUCCESS; + }, + ), + path_symlink: wrap((oldPath, oldPathLen, fd, newPath, newPathLen) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_SYMLINK); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const op = Buffer.from( + this.memory.buffer, + oldPath, + oldPathLen, + ).toString(); + const np = Buffer.from( + this.memory.buffer, + newPath, + newPathLen, + ).toString(); + fs.symlinkSync(op, path.resolve(stats.path, np)); + return constants_1.WASI_ESUCCESS; + }), + path_unlink_file: wrap((fd, pathPtr, pathLen) => { + const stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_UNLINK_FILE); + if (!stats.path) { + return constants_1.WASI_EINVAL; + } + this.refreshMemory(); + const p = Buffer.from( + this.memory.buffer, + pathPtr, + pathLen, + ).toString(); + fs.unlinkSync(path.resolve(stats.path, p)); + return constants_1.WASI_ESUCCESS; + }), + poll_oneoff: (sin, sout, nsubscriptions, neventsPtr) => { + let nevents = 0; + let name = ""; + let waitTimeNs = BigInt(0); + let fd = -1; + let fd_type = "read"; + let fd_timeout_ms = 0; + const startNs = BigInt(bindings.hrtime()); + this.refreshMemory(); + let last_sin = sin; + for (let i = 0; i < nsubscriptions; i += 1) { + const userdata = this.view.getBigUint64(sin, true); + sin += 8; + const type = this.view.getUint8(sin); + sin += 1; + sin += 7; + if (log.enabled) { + if (type == constants_1.WASI_EVENTTYPE_CLOCK) { + name = "poll_oneoff (type=WASI_EVENTTYPE_CLOCK): "; + } else if (type == constants_1.WASI_EVENTTYPE_FD_READ) { + name = "poll_oneoff (type=WASI_EVENTTYPE_FD_READ): "; + } else { + name = "poll_oneoff (type=WASI_EVENTTYPE_FD_WRITE): "; + } + log(name); + } + switch (type) { + case constants_1.WASI_EVENTTYPE_CLOCK: { + const clockid = this.view.getUint32(sin, true); + sin += 4; + sin += 4; + const timeout = this.view.getBigUint64(sin, true); + sin += 8; + sin += 8; + const subclockflags = this.view.getUint16(sin, true); + sin += 2; + sin += 6; + const absolute = subclockflags === 1; + if (log.enabled) { + log(name, { clockid, timeout, absolute }); + } + if (!absolute) { + fd_timeout_ms = Number(timeout / BigInt(1e6)); + } + let e = constants_1.WASI_ESUCCESS; + const t = now(clockid); + if (t == null) { + e = constants_1.WASI_EINVAL; + } else { + const end = absolute ? timeout : t + timeout; + const waitNs = end - t; + if (waitNs > waitTimeNs) { + waitTimeNs = waitNs; + } + } + this.view.setBigUint64(sout, userdata, true); + sout += 8; + this.view.setUint16(sout, e, true); + sout += 2; + this.view.setUint8(sout, constants_1.WASI_EVENTTYPE_CLOCK); + sout += 1; + sout += 5; + nevents += 1; + break; + } + case constants_1.WASI_EVENTTYPE_FD_READ: + case constants_1.WASI_EVENTTYPE_FD_WRITE: { + fd = this.view.getUint32(sin, true); + fd_type = + type == constants_1.WASI_EVENTTYPE_FD_READ + ? "read" + : "write"; + sin += 4; + log(name, "fd =", fd); + sin += 28; + this.view.setBigUint64(sout, userdata, true); + sout += 8; + this.view.setUint16(sout, constants_1.WASI_ENOSYS, true); + sout += 2; + this.view.setUint8(sout, type); + sout += 1; + sout += 5; + nevents += 1; + if ( + fd == constants_1.WASI_STDIN_FILENO && + constants_1.WASI_EVENTTYPE_FD_READ == type + ) { + this.shortPause(); + } + break; + } + default: + return constants_1.WASI_EINVAL; + } + if (sin - last_sin != 48) { + console.warn("*** BUG in wasi-js in poll_oneoff ", { + i, + sin, + last_sin, + diff: sin - last_sin, + }); + } + last_sin = sin; + } + this.view.setUint32(neventsPtr, nevents, true); + if (nevents == 2 && fd >= 0) { + const r = this.wasiImport.sock_pollSocket( + fd, + fd_type, + fd_timeout_ms, + ); + if (r != constants_1.WASI_ENOSYS) { + return r; + } + } + if (waitTimeNs > 0) { + waitTimeNs -= BigInt(bindings.hrtime()) - startNs; + if (waitTimeNs >= 1e6) { + if (this.sleep == null && !warnedAboutSleep) { + warnedAboutSleep = true; + console.log( + "(100% cpu burning waiting for stdin: please define a way to sleep!) ", + ); + } + if (this.sleep != null) { + const ms = nsToMs(waitTimeNs); + this.sleep(ms); + } else { + const end = BigInt(bindings.hrtime()) + waitTimeNs; + while (BigInt(bindings.hrtime()) < end) {} + } + } + } + return constants_1.WASI_ESUCCESS; + }, + proc_exit: (rval) => { + bindings.exit(rval); + return constants_1.WASI_ESUCCESS; + }, + proc_raise: (sig) => { + if (!(sig in constants_1.SIGNAL_MAP)) { + return constants_1.WASI_EINVAL; + } + bindings.kill(constants_1.SIGNAL_MAP[sig]); + return constants_1.WASI_ESUCCESS; + }, + random_get: (bufPtr, bufLen) => { + this.refreshMemory(); + crypto.getRandomValues(this.memory.buffer, bufPtr, bufLen); + return bufLen; + }, + sched_yield() { + return constants_1.WASI_ESUCCESS; + }, + sock_recv() { + return constants_1.WASI_ENOSYS; + }, + sock_send() { + return constants_1.WASI_ENOSYS; + }, + sock_shutdown() { + return constants_1.WASI_ENOSYS; + }, + sock_fcntlSetFlags(_fd, _flags) { + return constants_1.WASI_ENOSYS; + }, + sock_pollSocket(_fd, _eventtype, _timeout_ms) { + return constants_1.WASI_ENOSYS; + }, + }; + if (log.enabled) { + Object.keys(this.wasiImport).forEach((key) => { + const prevImport = this.wasiImport[key]; + this.wasiImport[key] = function (...args2) { + log(key, args2); + try { + let result = prevImport(...args2); + log("result", result); + return result; + } catch (e) { + log("error: ", e); + throw e; + } + }; + }); + } + } + getState() { + return { env: this.env, FD_MAP: this.FD_MAP, bindings: bindings }; + } + setState(state) { + this.env = state.env; + this.FD_MAP = state.FD_MAP; + bindings = state.bindings; + } + fstatSync(real_fd) { + if (real_fd <= 2) { + try { + return bindings.fs.fstatSync(real_fd); + } catch (_) { + const now = new Date(); + return { + dev: 0, + mode: 8592, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + blksize: 65536, + ino: 0, + size: 0, + blocks: 0, + atimeMs: now.valueOf(), + mtimeMs: now.valueOf(), + ctimeMs: now.valueOf(), + birthtimeMs: 0, + atime: new Date(), + mtime: new Date(), + ctime: new Date(), + birthtime: new Date(0), + }; + } + } + return bindings.fs.fstatSync(real_fd); + } + shortPause() { + if (this.sleep == null) return; + const now = new Date().valueOf(); + if (now - this.lastStdin > 2e3) { + this.sleep(50); + } + } + getUnusedFileDescriptor(start = 3) { + let fd = start; + while (this.FD_MAP.has(fd)) { + fd += 1; + } + if (fd > SC_OPEN_MAX) { + throw Error("no available file descriptors"); + } + return fd; + } + refreshMemory() { + if (!this.view || this.view.buffer.byteLength === 0) { + this.view = new DataView(this.memory.buffer); + } + } + setMemory(memory) { + this.memory = memory; + } + start(instance, memory) { + const exports2 = instance.exports; + if (exports2 === null || typeof exports2 !== "object") { + throw new Error( + `instance.exports must be an Object. Received ${exports2}.`, + ); + } + if (memory == null) { + memory = exports2.memory; + if (!(memory instanceof WebAssembly.Memory)) { + throw new Error( + `instance.exports.memory must be a WebAssembly.Memory. Recceived ${memory}.`, + ); + } + } + this.setMemory(memory); + if (exports2._start) { + exports2._start(); + } + } + getImports(module2) { + let namespace = null; + const imports = WebAssembly.Module.imports(module2); + + for (let imp of imports) { + if (imp.kind !== "function") { + continue; + } + if (!imp.module.startsWith("wasi_")) { + continue; + } + + namespace = imp.module; + break; + } + + switch (namespace) { + case "wasi_unstable": + return { + wasi_unstable: this.wasiImport, + }; + case "wasi_snapshot_preview1": + return { + wasi_snapshot_preview1: this.wasiImport, + }; + default: { + throw new Error( + "No WASI namespace found. Only wasi_unstable and wasi_snapshot_preview1 are supported.\n\nList of imports:\n\n" + + imports + .map( + ({ name, kind, module }) => `${module}:${name} (${kind})`, + ) + .join("\n") + + "\n", + ); + } + } + } + initWasiFdInfo() { + if (this.env["WASI_FD_INFO"] != null) { + const fdInfo = JSON.parse(this.env["WASI_FD_INFO"]); + for (const wasi_fd in fdInfo) { + console.log(wasi_fd); + const fd = parseInt(wasi_fd); + if (this.FD_MAP.has(fd)) { + continue; + } + const real = fdInfo[wasi_fd]; + try { + this.fstatSync(real); + } catch (_err) { + console.log("discarding ", { wasi_fd, real }); + continue; + } + const file = { + real, + filetype: constants_1.WASI_FILETYPE_SOCKET_STREAM, + rights: { + base: STDIN_DEFAULT_RIGHTS, + inheriting: BigInt(0), + }, + }; + this.FD_MAP.set(fd, file); + } + console.log("after initWasiFdInfo: ", this.FD_MAP); + console.log("fdInfo = ", fdInfo); + } else { + console.log("no WASI_FD_INFO"); + } + } + }; + exports.default = WASI; + }, +}); +const WASIExport = require_wasi(); +const WASI = WASIExport.default; +WASIExport[Symbol.for("CommonJS")] = 0; +export { WASIExport as WASI }; +export default WASIExport; diff --git a/src/bundler.zig b/src/bundler.zig index ec3de573b..336723da3 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -1404,6 +1404,17 @@ pub const Bundler = struct { .ts, .tsx, => { + // wasm magic number + if (source.isWebAssembly()) { + return ParseResult{ + .source = source, + .input_fd = input_fd, + .loader = .wasm, + .empty = true, + .ast = js_ast.Ast.empty, + }; + } + const platform = bundler.options.platform; var jsx = this_parse.jsx; @@ -1518,7 +1529,7 @@ pub const Bundler = struct { }, .wasm => { if (bundler.options.platform.isBun()) { - if (source.contents.len < 4 or @bitCast(u32, source.contents[0..4].*) != @bitCast(u32, [4]u8{ 0, 'a', 's', 'm' })) { + if (!source.isWebAssembly()) { bundler.log.addErrorFmt( null, logger.Loc.Empty, diff --git a/src/cli.zig b/src/cli.zig index 01adffee8..4b4f87ae9 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -1243,7 +1243,7 @@ pub const Command = struct { const force_using_bun = ctx.debug.run_in_bun; var did_check = false; if (default_loader) |loader| { - if (loader.isJavaScriptLike()) { + if (loader.isJavaScriptLike() or loader == .wasm) { was_js_like = true; if (maybeOpenWithBunJS(&ctx)) { return; diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index dc0138310..b6784cdf2 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -857,7 +857,7 @@ pub const RunCommand = struct { possibly_open_with_bun_js: { if (!force_using_bun) { if (options.defaultLoaders.get(std.fs.path.extension(script_name_to_search))) |load| { - if (!load.isJavaScriptLike()) + if (!(load.isJavaScriptLike() or load == .wasm)) break :possibly_open_with_bun_js; } else { break :possibly_open_with_bun_js; @@ -898,9 +898,9 @@ pub const RunCommand = struct { }; var shebang: string = shebang_buf[0..shebang_size]; + shebang = std.mem.trim(u8, shebang, " \r\n\t"); if (shebang.len == 0) break :possibly_open_with_bun_js; - if (strings.hasPrefixComptime(shebang, "#!")) { const first_arg: string = if (std.os.argv.len > 0) bun.span(std.os.argv[0]) else ""; const filename = std.fs.path.basename(first_arg); diff --git a/src/deps/.gitignore b/src/deps/.gitignore new file mode 100644 index 000000000..19e1bced9 --- /dev/null +++ b/src/deps/.gitignore @@ -0,0 +1 @@ +*.wasm diff --git a/src/logger.zig b/src/logger.zig index 5e4e92406..bd2c81c31 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -1144,6 +1144,13 @@ pub const Source = struct { contents: string, contents_is_recycled: bool = false, + pub fn isWebAssembly(this: *const Source) bool { + if (this.contents.len < 4) return false; + + const bytes = @bitCast(u32, this.contents[0..4].*); + return bytes == 0x6d736100; // "\0asm" + } + pub const ErrorPosition = struct { line_start: usize, line_end: usize, diff --git a/src/string_immutable.zig b/src/string_immutable.zig index db00508e1..b02cf1249 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -838,6 +838,17 @@ pub inline fn append(allocator: std.mem.Allocator, self: string, other: string) return buf; } +pub inline fn append3(allocator: std.mem.Allocator, self: string, other: string, third: string) ![]u8 { + var buf = try allocator.alloc(u8, self.len + other.len + third.len); + if (self.len > 0) + @memcpy(buf.ptr, self.ptr, self.len); + if (other.len > 0) + @memcpy(buf.ptr + self.len, other.ptr, other.len); + if (third.len > 0) + @memcpy(buf.ptr + self.len + other.len, third.ptr, third.len); + return buf; +} + pub inline fn joinBuf(out: []u8, parts: anytype, comptime parts_len: usize) []u8 { var remain = out; var count: usize = 0; |