diff --git a/.gitmodules b/.gitmodules index c9f05a1..2d9d707 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,7 @@ path = test/wasi-testsuite url = https://github.com/WebAssembly/wasi-testsuite branch = prod/testsuite-base +[submodule "examples/wasi_multi_threads_rustc/rust_wasm"] + path = examples/wasi_multi_threads_rustc/rust_wasm + url = https://github.com/oligamiq/rust_wasm + sharrow = true diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ce072c8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.tabSize": 2 +} diff --git a/examples/package-lock.json b/examples/package-lock.json index 9dd8988..d01cb3c 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -1,43 +1,53 @@ { - "name": "browser_wasi_shim_examples", - "version": "0.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "browser_wasi_shim_examples", - "version": "0.0.0", - "license": "MIT OR Apache-2.0", - "dependencies": { - "xterm": "^4.18.0", - "xterm-addon-fit": "^0.5.0" - } - }, - "node_modules/xterm": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.18.0.tgz", - "integrity": "sha512-JQoc1S0dti6SQfI0bK1AZvGnAxH4MVw45ZPFSO6FHTInAiau3Ix77fSxNx3mX4eh9OL4AYa8+4C8f5UvnSfppQ==" - }, - "node_modules/xterm-addon-fit": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz", - "integrity": "sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==", - "peerDependencies": { - "xterm": "^4.0.0" - } - } - }, - "dependencies": { - "xterm": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.18.0.tgz", - "integrity": "sha512-JQoc1S0dti6SQfI0bK1AZvGnAxH4MVw45ZPFSO6FHTInAiau3Ix77fSxNx3mX4eh9OL4AYa8+4C8f5UvnSfppQ==" - }, - "xterm-addon-fit": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz", - "integrity": "sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==", - "requires": {} - } - } + "name": "browser_wasi_shim_examples", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "browser_wasi_shim_examples", + "version": "0.0.0", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@oligami/shared-object": "^0.1.1", + "browser_wasi_shim_examples": "file:", + "xterm": "^4.18.0", + "xterm-addon-fit": "^0.5.0" + } + }, + "node_modules/@oligami/shared-object": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@oligami/shared-object/-/shared-object-0.1.1.tgz", + "integrity": "sha512-1N5k8IL+JjD0lPiLR+NXT7BFzvnlPaBioMry9RZOL+4YI54TpW4fmkgys9rlyVo4hZB0DL5t8pgzXxq+IIO7fg==", + "license": "MIT", + "dependencies": { + "shared-object": "file:" + } + }, + "node_modules/browser_wasi_shim_examples": { + "resolved": "", + "link": true + }, + "node_modules/shared-object": { + "resolved": "node_modules/@oligami/shared-object", + "link": true + }, + "node_modules/xterm": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz", + "integrity": "sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", + "license": "MIT" + }, + "node_modules/xterm-addon-fit": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz", + "integrity": "sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", + "license": "MIT", + "peerDependencies": { + "xterm": "^4.0.0" + } + } + } } diff --git a/examples/package.json b/examples/package.json index cd22ce6..e06354d 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,20 +1,22 @@ { - "name": "browser_wasi_shim_examples", - "publish": false, - "version": "0.0.0", - "license": "MIT OR Apache-2.0", - "description": "", - "repository": { - "type": "git", - "url": "git+https://github.com/bjorn3/browser_wasi_shim.git" - }, - "author": "bjorn3", - "bugs": { - "url": "https://github.com/bjorn3/browser_wasi_shim/issues" - }, - "homepage": "https://github.com/bjorn3/browser_wasi_shim#readme", - "dependencies": { - "xterm": "^4.18.0", - "xterm-addon-fit": "^0.5.0" - } + "name": "browser_wasi_shim_examples", + "publish": false, + "version": "0.0.0", + "license": "MIT OR Apache-2.0", + "description": "", + "repository": { + "type": "git", + "url": "git+https://github.com/bjorn3/browser_wasi_shim.git" + }, + "author": "bjorn3", + "bugs": { + "url": "https://github.com/bjorn3/browser_wasi_shim/issues" + }, + "homepage": "https://github.com/bjorn3/browser_wasi_shim#readme", + "dependencies": { + "@oligami/shared-object": "^0.1.1", + "browser_wasi_shim_examples": "file:", + "xterm": "^4.18.0", + "xterm-addon-fit": "^0.5.0" + } } diff --git a/examples/wasi_multi_threads/index.html b/examples/wasi_multi_threads/index.html new file mode 100644 index 0000000..c3ae7e3 --- /dev/null +++ b/examples/wasi_multi_threads/index.html @@ -0,0 +1,38 @@ + + + + + +

+ #### +

+ + + + diff --git a/examples/wasi_multi_threads/main.rs b/examples/wasi_multi_threads/main.rs new file mode 100644 index 0000000..86d14dd --- /dev/null +++ b/examples/wasi_multi_threads/main.rs @@ -0,0 +1,13 @@ +fn main() { + println!("Hello, world!"); + + let _: std::thread::JoinHandle<()> = std::thread::spawn(|| { + for i in 1..1000 { + println!("hi number {} from the spawned thread!", i); + } + }); + + for i in 1..1000 { + println!("hi number {} from the main thread!", i); + } +} diff --git a/examples/wasi_multi_threads/multi_thread_echo.wasm b/examples/wasi_multi_threads/multi_thread_echo.wasm new file mode 100644 index 0000000..894878b Binary files /dev/null and b/examples/wasi_multi_threads/multi_thread_echo.wasm differ diff --git a/examples/wasi_multi_threads/thread_spawn.js b/examples/wasi_multi_threads/thread_spawn.js new file mode 100644 index 0000000..3761fb9 --- /dev/null +++ b/examples/wasi_multi_threads/thread_spawn.js @@ -0,0 +1,5 @@ +import { thread_spawn_on_worker } from "../../dist/wasi_farm/shared_array_buffer/thread_spawn.js"; + +self.onmessage = (event) => { + thread_spawn_on_worker(event.data); +} diff --git a/examples/wasi_multi_threads/worker.js b/examples/wasi_multi_threads/worker.js new file mode 100644 index 0000000..913a976 --- /dev/null +++ b/examples/wasi_multi_threads/worker.js @@ -0,0 +1,32 @@ +import { WASIFarmAnimal } from "../../dist/index.js"; + +self.onmessage = async (e) => { + const { wasi_ref } = e.data; + + const wasm = await WebAssembly.compileStreaming(fetch("./multi_thread_echo.wasm")); + + const wasi = new WASIFarmAnimal( + wasi_ref, + [], // args + [], // env + { + debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: (new URL("./thread_spawn.js", import.meta.url)).href, + // thread_spawn_worker_url: "./thread_spawn.js", + thread_spawn_wasm: wasm, + } + ); + + await wasi.wait_worker_background_worker(); + + let inst = await WebAssembly.instantiate(wasm, { + "env": { + memory: wasi.get_share_memory(), + }, + "wasi": wasi.wasiThreadImport, + "wasi_snapshot_preview1": wasi.wasiImport, + }); + + wasi.start(inst); +} diff --git a/examples/wasi_multi_threads_channel/channel.wasm b/examples/wasi_multi_threads_channel/channel.wasm new file mode 100644 index 0000000..369c3b5 Binary files /dev/null and b/examples/wasi_multi_threads_channel/channel.wasm differ diff --git a/examples/wasi_multi_threads_channel/index.html b/examples/wasi_multi_threads_channel/index.html new file mode 100644 index 0000000..44539a0 --- /dev/null +++ b/examples/wasi_multi_threads_channel/index.html @@ -0,0 +1,28 @@ + + + + + +

+ #### +

+ + + + diff --git a/examples/wasi_multi_threads_channel/main.rs b/examples/wasi_multi_threads_channel/main.rs new file mode 100644 index 0000000..216800c --- /dev/null +++ b/examples/wasi_multi_threads_channel/main.rs @@ -0,0 +1,16 @@ +// https://doc.rust-lang.org/book/ch16-02-message-passing.html + +use std::sync::mpsc; +use std::thread; + +fn main() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let val = String::from("hi"); + tx.send(val).unwrap(); + }); + + let received = rx.recv().unwrap(); + println!("Got: {received}"); +} diff --git a/examples/wasi_multi_threads_channel/thread_spawn.js b/examples/wasi_multi_threads_channel/thread_spawn.js new file mode 100644 index 0000000..3761fb9 --- /dev/null +++ b/examples/wasi_multi_threads_channel/thread_spawn.js @@ -0,0 +1,5 @@ +import { thread_spawn_on_worker } from "../../dist/wasi_farm/shared_array_buffer/thread_spawn.js"; + +self.onmessage = (event) => { + thread_spawn_on_worker(event.data); +} diff --git a/examples/wasi_multi_threads_channel/worker.js b/examples/wasi_multi_threads_channel/worker.js new file mode 100644 index 0000000..c91ecb4 --- /dev/null +++ b/examples/wasi_multi_threads_channel/worker.js @@ -0,0 +1,32 @@ +import { WASIFarmAnimal } from "../../dist/index.js"; + +self.onmessage = async (e) => { + const { wasi_ref } = e.data; + + const wasm = await WebAssembly.compileStreaming(fetch("./channel.wasm")); + + const wasi = new WASIFarmAnimal( + wasi_ref, + [], // args + [], // env + { + debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: (new URL("./thread_spawn.js", import.meta.url)).href, + // thread_spawn_worker_url: "./thread_spawn.js", + thread_spawn_wasm: wasm, + } + ); + + await wasi.wait_worker_background_worker(); + + let inst = await WebAssembly.instantiate(wasm, { + "env": { + memory: wasi.get_share_memory(), + }, + "wasi": wasi.wasiThreadImport, + "wasi_snapshot_preview1": wasi.wasiImport, + }); + + wasi.start(inst); +} diff --git a/examples/wasi_multi_threads_rustc/cargo.js b/examples/wasi_multi_threads_rustc/cargo.js new file mode 100644 index 0000000..b55ea3d --- /dev/null +++ b/examples/wasi_multi_threads_rustc/cargo.js @@ -0,0 +1,58 @@ +import { WASIFarmAnimal } from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +let wasi; +let wasm; +let shared; + +onmessage = async function (e) { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/rustc_llvm_with_lld/cargo_opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + [ + "RUST_MIN_STACK=16777216", + "HOME=/home/wasi", + "RUST_LOG=debug", + "RUST_BACKTRACE=full", + // This is made up of forced patches. Usually not available. + "RUSTC_SYSROOT=/sysroot-with-lld", + "RUSTFLAGS=", + ], // env + { + // debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.js", import.meta.url) + .href, + // thread_spawn_worker_url: "./thread_spawn.js", + thread_spawn_wasm: wasm, + extend_imports: true, + }, + ); + + await wasi.wait_worker_background_worker(); + + wasi.get_share_memory().grow(200); + + console.log("Waiting for worker background worker..."); + + await promise; + + shared = new SharedObject.SharedObject((...args) => { + console.log("wasi.start"); + wasi.args = ["cargo", ...args]; + wasi.block_start_on_thread(); + console.log("wasi.start done"); + }, "cargo"); + + postMessage({ ready: true }); + } +}; diff --git a/examples/wasi_multi_threads_rustc/cat.js b/examples/wasi_multi_threads_rustc/cat.js new file mode 100644 index 0000000..fdd91d1 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/cat.js @@ -0,0 +1,47 @@ +import { strace, WASIFarmAnimal } from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +let wasi; +let inst; +let wasm; + +let shared; + +onmessage = async function (e) { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming(fetch("./cat.wasm")); + + wasi = new WASIFarmAnimal( + wasi_refs, + ["cat"], // args + [], // env + { + debug: false, + }, + ); + + // Memory is rewritten at this time. + inst = await WebAssembly.instantiate(wasm, { + wasi_snapshot_preview1: wasi.wasiImport, + }); + + await promise; + + const memory_reset = inst.exports.memory.buffer; + const memory_reset_view = new Uint8Array(memory_reset).slice(); + + shared = new SharedObject.SharedObject((...args) => { + // If I don't reset memory, I get some kind of error. + wasi.args = ["cat", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + }, "cat"); + + postMessage({ ready: true }); + } +}; diff --git a/examples/wasi_multi_threads_rustc/cat.rs b/examples/wasi_multi_threads_rustc/cat.rs new file mode 100644 index 0000000..147cb14 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/cat.rs @@ -0,0 +1,20 @@ +// cat alt + +use std::io::BufRead; + +pub fn main() { + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + eprintln!("Usage: {} ", args[0]); + std::process::exit(1); + } + + let path = &args[1]; + let file = std::fs::File::open(path).unwrap(); + let reader = std::io::BufReader::new(file); + for line in reader.lines() { + println!("{}", line.unwrap()); + } +} + +// rustc cat.rs --target=wasm32-wasip1 diff --git a/examples/wasi_multi_threads_rustc/cat.wasm b/examples/wasi_multi_threads_rustc/cat.wasm new file mode 100644 index 0000000..3278929 Binary files /dev/null and b/examples/wasi_multi_threads_rustc/cat.wasm differ diff --git a/examples/wasi_multi_threads_rustc/clang.js b/examples/wasi_multi_threads_rustc/clang.js new file mode 100644 index 0000000..e306138 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/clang.js @@ -0,0 +1,78 @@ +import { strace, WASIFarmAnimal } from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +let wasi; +let inst; +let wasm; + +let shared_clang; +let shared_tools; +let shared_wasm_ld; + +onmessage = async function (e) { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/llvm-tools/llvm-opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + ["llvm"], // args + [], // env + { + debug: false, + }, + ); + + // Memory is rewritten at this time. + // inst = await WebAssembly.instantiate(wasm, { + // wasi_snapshot_preview1: wasi.wasiImport, + // }); + inst = await WebAssembly.instantiate(wasm, { + wasi_snapshot_preview1: strace(wasi.wasiImport, []), + }); + + console.log("wasi.start, inst", inst); + + await promise; + + const memory_reset = inst.exports.memory.buffer; + const memory_reset_view = new Uint8Array(memory_reset).slice(); + + shared_clang = new SharedObject.SharedObject((...args) => { + console.log("clang args", args); + // If I don't reset memory, I get some kind of error. + wasi.args = ["llvm", "clang", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + console.log("clang wasi.start done"); + }, "clang"); + + shared_tools = new SharedObject.SharedObject((...args) => { + console.log("tools args", args); + // If I don't reset memory, I get some kind of error. + wasi.args = ["llvm-tools", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + console.log("tools wasi.start done"); + }, "llvm-tools"); + + shared_wasm_ld = new SharedObject.SharedObject((...args) => { + console.log("wasm-ld args", args); + // If I don't reset memory, I get some kind of error. + wasi.args = ["llvm-tools", "wasm-ld", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + console.log("wasm-ld wasi.start done"); + }, "wasm-ld"); + + postMessage({ ready: true }); + } +}; diff --git a/examples/wasi_multi_threads_rustc/depend_clang_files.js b/examples/wasi_multi_threads_rustc/depend_clang_files.js new file mode 100644 index 0000000..969c184 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/depend_clang_files.js @@ -0,0 +1,80 @@ +import { + PreopenDirectory, + WASIFarm, + Directory, + File, +} from "../../dist/index.js"; + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const wasi_libs = new Map(); +for (const file of [ + "crt1-command.o", + "crt1-reactor.o", + "crt1.o", + "libc-printscan-long-double.a", + "libc-printscan-no-floating-point.a", + "libc.a", + "libc.imports", + "libc++.a", + "libc++.modules.json", + "libc++abi.a", + "libc++experimental.a", + "libcrypt.a", + "libdl.a", + "libm.a", + "libpthread.a", + "libresolv.a", + "librt.a", + "libsetjmp.a", + "libutil.a", + "libwasi-emulated-getpid.a", + "libwasi-emulated-mman.a", + "libwasi-emulated-process-clocks.a", + "libwasi-emulated-signal.a", + "libxnet.a", +]) { + wasi_libs.set( + file, + await load_external_file( + `./rust_wasm/llvm-tools/dist/lib/wasm32-wasip1/${file}`, + ), + ); +} + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + new PreopenDirectory("/sysroot-clang", [ + [ + "lib", + new Directory([ + [ + "wasm32-unknown-wasip1", + new Directory([ + [ + "libclang_rt.builtins.a", + await load_external_file( + "./rust_wasm/llvm-tools/dist/lib/wasm32-unknown-wasip1/libclang_rt.builtins.a", + ), + ], + ]), + ], + ["wasm32-wasip1", wasi_libs], + ]), + ], + ]), + ], + { + // allocator_size: 1024 * 1024 * 1024, + // debug: true, + }, +); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/examples/wasi_multi_threads_rustc/depend_rustc_files.js b/examples/wasi_multi_threads_rustc/depend_rustc_files.js new file mode 100644 index 0000000..b1ea3fe --- /dev/null +++ b/examples/wasi_multi_threads_rustc/depend_rustc_files.js @@ -0,0 +1,154 @@ +import { + PreopenDirectory, + WASIFarm, + Directory, + File, +} from "../../dist/index.js"; + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const linux_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-b8754aeb03c02354.rlib", + "libadler-05c3545f6cd12159.rlib", + "liballoc-0dab879bc41cd6bd.rlib", + "libcfg_if-c7fd2cef50341546.rlib", + "libcompiler_builtins-a99947d020d809d6.rlib", + "libcore-4b8e8a815d049db3.rlib", + "libgetopts-bbb75529e85d129d.rlib", + "libgimli-598847d27d7a3cbf.rlib", + "libhashbrown-d2ff91fdf93cacb2.rlib", + "liblibc-dc63949c664c3fce.rlib", + "libmemchr-2d3a423be1a6cb96.rlib", + "libminiz_oxide-b109506a0ccc4c6a.rlib", + "libobject-7b48def7544c748b.rlib", + "libpanic_abort-c93441899b93b849.rlib", + "libpanic_unwind-11d9ba05b60bf694.rlib", + "libproc_macro-1a7f7840bb9983dc.rlib", + "librustc_demangle-59342a335246393d.rlib", + "librustc_std_workspace_alloc-552b185085090ff6.rlib", + "librustc_std_workspace_core-5d8a121daa7eeaa9.rlib", + "librustc_std_workspace_std-97f43841ce452f7d.rlib", + "libstd-bdedb7706a556da2.rlib", + "libstd-bdedb7706a556da2.so", + "libstd_detect-cca21eebc4281add.rlib", + "libsysroot-f654e185be3ffebd.rlib", + "libtest-f06fa3fbc201c558.rlib", + "libunicode_width-19a0dcd589fa0877.rlib", + "libunwind-747b693f90af9445.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm/dist/lib/rustlib/x86_64-unknown-linux-gnu/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-a47658bebc67c3a1.rlib", + "libadler-38ddbcf07afd45fc.rlib", + "liballoc-1fc4f6ca1d836e4c.rlib", + "libcfg_if-fd15f5d506df7899.rlib", + "libcompiler_builtins-3dc6223f56552b05.rlib", + "libcore-0ec7cb16e8553802.rlib", + "libgetopts-6248a91c42a854a0.rlib", + "libgimli-4425159eeeeb18dd.rlib", + "libhashbrown-243f98c4e4e641ea.rlib", + "liblibc-9149392e3841960d.rlib", + "libmemchr-9ac950afd37fa4c7.rlib", + "libminiz_oxide-91aaa0ee7402d39e.rlib", + "libobject-361b96ef5df8a7f9.rlib", + "libpanic_abort-f91052098501e46b.rlib", + "libpanic_unwind-fc376dcf47815f10.rlib", + "libproc_macro-9cab37e4d11f0e52.rlib", + "librustc_demangle-1af142f261139812.rlib", + "librustc_std_workspace_alloc-f0d62212c413dd0e.rlib", + "librustc_std_workspace_core-ea396731d16229a8.rlib", + "librustc_std_workspace_std-7434133be68a4a89.rlib", + "libstd_detect-083332b3c8180bc9.rlib", + "libstd-5ddf10249e9580fe.rlib", + "libsysroot-8b3608099dad3b42.rlib", + "libtest-8ebd431ae5608538.rlib", + "libunicode_width-7e2396fcd7049a8b.rlib", + "libunwind-e7408208cf4a3c79.rlib", + "libwasi-f0b9e157c50fe586.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm/dist/lib/rustlib/wasm32-wasip1-threads/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_self_contained_promise = (async () => { + const dir = new Map(); + for (const file of ["crt1-command.o", "crt1-reactor.o", "libc.a"]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm/dist/lib/rustlib/wasm32-wasip1-threads/lib/self-contained/${file}`, + ), + ); + } + return dir; +})(); + +const [linux_libs, threads_libs, threads_self_contained, components] = + await Promise.all([ + linux_libs_promise, + threads_libs_promise, + threads_self_contained_promise, + await load_external_file( + "./rust_wasm/rustc_llvm/dist/lib/rustlib/components", + ), + ]); + +threads_libs.set("self-contained", new Directory(threads_self_contained)); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + new PreopenDirectory("/sysroot", [ + [ + "lib", + new Directory([ + [ + "rustlib", + new Directory([ + ["components", components], + [ + "wasm32-wasip1-threads", + new Directory([["lib", new Directory(threads_libs)]]), + ], + [ + "x86_64-unknown-linux-gnu", + new Directory([["lib", new Directory(linux_libs)]]), + ], + ]), + ], + ]), + ], + ]), + ], + { + allocator_size: 1024 * 1024 * 1024, + // debug: true, + }, +); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/examples/wasi_multi_threads_rustc/depend_rustc_with_lld.js b/examples/wasi_multi_threads_rustc/depend_rustc_with_lld.js new file mode 100644 index 0000000..fb302ce --- /dev/null +++ b/examples/wasi_multi_threads_rustc/depend_rustc_with_lld.js @@ -0,0 +1,154 @@ +import { + PreopenDirectory, + WASIFarm, + Directory, + File, +} from "../../dist/index.js"; + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const linux_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-d2445d88f0df8258.rlib", + "libadler-6095b59b8443d84e.rlib", + "liballoc-19b196c8c1c1b105.rlib", + "libcfg_if-d04d665f43a7a5c9.rlib", + "libcompiler_builtins-f3e5bc67a3085e50.rlib", + "libcore-24518d8502db248c.rlib", + "libgetopts-4bb89b05e2b4cc6e.rlib", + "libgimli-85e2d283537da979.rlib", + "libhashbrown-1436b1713a4e6650.rlib", + "liblibc-a012fa771333437a.rlib", + "libmemchr-b50e0c33c9e9768d.rlib", + "libminiz_oxide-937a56bed56199f4.rlib", + "libobject-4e591f1863579f49.rlib", + "libpanic_abort-90cfe20cb97a5e4c.rlib", + "libpanic_unwind-9ad8c03a583f4dc7.rlib", + "libproc_macro-32b9efef039a24fe.rlib", + "librustc_demangle-ca292a161705fdb4.rlib", + "librustc_std_workspace_alloc-bba69521e853a996.rlib", + "librustc_std_workspace_core-3da34b7f5e59869a.rlib", + "librustc_std_workspace_std-c35ef78edc033606.rlib", + "libstd_detect-776a1ebea822ca12.rlib", + "libstd-6924b036e1bec7ce.rlib", + "libstd-6924b036e1bec7ce.so", + "libsysroot-0b7644c6027c414e.rlib", + "libtest-3d5766d8038a0e74.rlib", + "libunicode_width-dff2b02e7e936b79.rlib", + "libunwind-c99628283276f21f.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/x86_64-unknown-linux-gnu/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-02cc3b87379ea949.rlib", + "libgimli-a80bd3f5fe54def6.rlib", + "libpanic_unwind-05c50d12758b6d01.rlib", + "libstd_detect-a196185ed7f17cfc.rlib", + "libadler-a6b90b86640ec179.rlib", + "libhashbrown-35a68b834152af94.rlib", + "libproc_macro-2aa22b2ed111e644.rlib", + "libsysroot-856cc79af2fd1ee2.rlib", + "liballoc-be9b2b68a2d6adbd.rlib", + "liblibc-374b1b6cc5790f18.rlib", + "librustc_demangle-710d955f336192eb.rlib", + "libtest-4fcaddbdfa4f37f9.rlib", + "libcfg_if-75a956684c8aceef.rlib", + "libmemchr-b8580e949eadd97a.rlib", + "librustc_std_workspace_alloc-9c960f87e9d5a453.rlib", + "libunicode_width-be3becba43cd1b78.rlib", + "libcompiler_builtins-3a0795e4489d8e8b.rlib", + "libminiz_oxide-0bc4b046969a6755.rlib", + "librustc_std_workspace_core-4e237761d66d6cde.rlib", + "libunwind-2e570680d1c4cd0a.rlib", + "libcore-3e70038323b3a06e.rlib", + "libobject-3d83ea5d7ed5636f.rlib", + "librustc_std_workspace_std-5aa56e0a1970dc72.rlib", + "libwasi-f7d0229d2fe97cfd.rlib", + "libgetopts-b3d0219ad62c74a7.rlib", + "libpanic_abort-431cc2501a123c59.rlib", + "libstd-c7f97b33ddfcbfbf.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/wasm32-wasip1/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_self_contained_promise = (async () => { + const dir = new Map(); + for (const file of ["crt1-command.o", "crt1-reactor.o", "libc.a"]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/wasm32-wasip1/lib/self-contained/${file}`, + ), + ); + } + return dir; +})(); + +const [linux_libs, threads_libs, threads_self_contained, components] = + await Promise.all([ + linux_libs_promise, + threads_libs_promise, + threads_self_contained_promise, + await load_external_file( + "./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/components", + ), + ]); + +threads_libs.set("self-contained", new Directory(threads_self_contained)); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + new PreopenDirectory("/sysroot-with-lld", [ + [ + "lib", + new Directory([ + [ + "rustlib", + new Directory([ + ["components", components], + [ + "wasm32-wasip1", + new Directory([["lib", new Directory(threads_libs)]]), + ], + [ + "x86_64-unknown-linux-gnu", + new Directory([["lib", new Directory(linux_libs)]]), + ], + ]), + ], + ]), + ], + ]), + ], + { + allocator_size: 1024 * 1024 * 1024, + // debug: true, + }, +); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/examples/wasi_multi_threads_rustc/index.html b/examples/wasi_multi_threads_rustc/index.html new file mode 100644 index 0000000..8883c82 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/index.html @@ -0,0 +1,76 @@ + + + + + +
+ + + + + + + + + + + diff --git a/examples/wasi_multi_threads_rustc/rust_wasm b/examples/wasi_multi_threads_rustc/rust_wasm new file mode 160000 index 0000000..bff31e3 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/rust_wasm @@ -0,0 +1 @@ +Subproject commit bff31e3be735bcb22fef2a9e24a5143a872f6f23 diff --git a/examples/wasi_multi_threads_rustc/rustc.js b/examples/wasi_multi_threads_rustc/rustc.js new file mode 100644 index 0000000..1dd1bf8 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/rustc.js @@ -0,0 +1,48 @@ +import { WASIFarmAnimal } from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +let wasi; +let wasm; +let shared; + +onmessage = async function (e) { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/rustc_llvm/rustc_opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + ["RUST_MIN_STACK=16777216"], // env + { + // debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.js", import.meta.url) + .href, + // thread_spawn_worker_url: "./thread_spawn.js", + thread_spawn_wasm: wasm, + }, + ); + + await wasi.wait_worker_background_worker(); + + wasi.get_share_memory().grow(200); + + console.log("Waiting for worker background worker..."); + + await promise; + + shared = new SharedObject.SharedObject((...args) => { + wasi.args = ["rustc", ...args]; + wasi.block_start_on_thread(); + console.log("wasi.start done"); + }, "rustc"); + + postMessage({ ready: true }); + } +}; diff --git a/examples/wasi_multi_threads_rustc/rustc_with_lld.js b/examples/wasi_multi_threads_rustc/rustc_with_lld.js new file mode 100644 index 0000000..58633d8 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/rustc_with_lld.js @@ -0,0 +1,48 @@ +import { WASIFarmAnimal } from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +let wasi; +let wasm; +let shared; + +onmessage = async function (e) { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/rustc_llvm_with_lld/rustc_opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + ["RUST_MIN_STACK=16777216"], // env + { + // debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.js", import.meta.url) + .href, + // thread_spawn_worker_url: "./thread_spawn.js", + thread_spawn_wasm: wasm, + }, + ); + + await wasi.wait_worker_background_worker(); + + wasi.get_share_memory().grow(200); + + console.log("Waiting for worker background worker..."); + + await promise; + + shared = new SharedObject.SharedObject((...args) => { + wasi.args = ["rustc_with_lld", ...args]; + wasi.block_start_on_thread(); + console.log("wasi.start done"); + }, "rustc_with_lld"); + + postMessage({ ready: true }); + } +}; diff --git a/examples/wasi_multi_threads_rustc/save_stdout.js b/examples/wasi_multi_threads_rustc/save_stdout.js new file mode 100644 index 0000000..4739ac3 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/save_stdout.js @@ -0,0 +1,54 @@ +import { WASIFarm, Fd } from "../../dist/index.js"; + +await import("../node_modules/@oligami/shared-object/dist/index.js"); + +const term = new SharedObject.SharedObjectRef("term").proxy(); + +class Stdout extends Fd { + buffer = new Uint8Array(0); + + async fd_write(data /*: Uint8Array*/) /*: {ret: number, nwritten: number}*/ { + const new_buffer = new Uint8Array(this.buffer.length + data.length); + new_buffer.set(this.buffer); + new_buffer.set(data, this.buffer.length); + this.buffer = new_buffer; + + await term.writeUtf8(data); + + return { ret: 0, nwritten: data.byteLength }; + } +} + +const std_out = new Stdout(); + +const std_err = new Stdout(); + +const shared_std_out = new SharedObject.SharedObject( + { + get() { + return new TextDecoder().decode(std_out.buffer); + }, + reset() { + std_out.buffer = new Uint8Array(0); + }, + }, + "std_out_keep", +); + +const shared_std_err = new SharedObject.SharedObject( + { + get() { + return new TextDecoder().decode(std_err.buffer); + }, + reset() { + std_err.buffer = new Uint8Array(0); + }, + }, + "std_err_keep", +); + +const farm = new WASIFarm(undefined, std_out, std_err); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/examples/wasi_multi_threads_rustc/thread_spawn.js b/examples/wasi_multi_threads_rustc/thread_spawn.js new file mode 100644 index 0000000..bed9b51 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/thread_spawn.js @@ -0,0 +1,5 @@ +import { thread_spawn_on_worker } from "../../dist/index.js"; + +self.onmessage = async (event) => { + await thread_spawn_on_worker(event.data); +}; diff --git a/examples/wasi_multi_threads_rustc/tmp_dir.js b/examples/wasi_multi_threads_rustc/tmp_dir.js new file mode 100644 index 0000000..d243fff --- /dev/null +++ b/examples/wasi_multi_threads_rustc/tmp_dir.js @@ -0,0 +1,84 @@ +import { + PreopenDirectory, + WASIFarm, + File, + Directory, +} from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +const root_dir = new PreopenDirectory("/", [ + [ + "hello.rs", + new File( + new TextEncoder("utf-8").encode( + `fn main() { println!("Hello World!"); }`, + ), + ), + ], + ["sysroot", new Directory([])], + ["sysroot-with-lld", new Directory([])], + ["tmp", new Directory([])], +]); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + // new PreopenDirectory(".", [ + // ["tmp-tmp", new File(new TextEncoder("utf-8").encode("Hello World!"))], + // ["tmp-dir", new Directory([])], + // ]), + // new PreopenDirectory("tmp-dir", [ + // [ + // "tmp-dir_inner", + // new Directory([ + // [ + // "tmp-dir_inner-file", + // new File(new TextEncoder("utf-8").encode("Hello World!!!!!")), + // ], + // ]), + // ], + // ]), + new PreopenDirectory("/tmp", []), + root_dir, + new PreopenDirectory("~", [ + [ + "####.rs", + new File( + new TextEncoder("utf-8").encode( + `fn main() { println!("Hello World!"); }`, + ), + ), + ], + ["sysroot", new Directory([])], + ]), + ], + // { debug: true }, +); + +const ret = await farm.get_ref(); + +await promise; + +const shared = new SharedObject.SharedObject( + { + get_file(path_str) { + console.log(root_dir); + const path = { + parts: [path_str], + is_dir: false, + }; + const { ret, entry } = root_dir.dir.get_entry_for_path(path); + if (ret !== 0) { + throw new Error(`get_file: ${path_str} failed`); + } + return entry.data; + }, + }, + "root_dir", +); + +postMessage({ wasi_ref: ret }); diff --git a/examples/wasi_multi_threads_rustc/tre_opt.wasm b/examples/wasi_multi_threads_rustc/tre_opt.wasm new file mode 100644 index 0000000..67f06df Binary files /dev/null and b/examples/wasi_multi_threads_rustc/tre_opt.wasm differ diff --git a/examples/wasi_multi_threads_rustc/tree.js b/examples/wasi_multi_threads_rustc/tree.js new file mode 100644 index 0000000..a2bc7e0 --- /dev/null +++ b/examples/wasi_multi_threads_rustc/tree.js @@ -0,0 +1,47 @@ +import { strace, WASIFarmAnimal } from "../../dist/index.js"; + +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); + +let wasi; +let inst; +let wasm; + +let shared; + +onmessage = async function (e) { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming(fetch("./tre_opt.wasm")); + + wasi = new WASIFarmAnimal( + wasi_refs, + ["tre"], // args + [], // env + { + debug: false, + }, + ); + + // Memory is rewritten at this time. + inst = await WebAssembly.instantiate(wasm, { + wasi_snapshot_preview1: wasi.wasiImport, + }); + + await promise; + + const memory_reset = inst.exports.memory.buffer; + const memory_reset_view = new Uint8Array(memory_reset).slice(); + + shared = new SharedObject.SharedObject((...args) => { + // If I don't reset memory, I get some kind of error. + wasi.args = ["tre", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + }, "tree"); + + postMessage({ ready: true }); + } +}; diff --git a/examples/wasi_multi_threads_rustc/worker.js b/examples/wasi_multi_threads_rustc/worker.js new file mode 100644 index 0000000..b0e11cf --- /dev/null +++ b/examples/wasi_multi_threads_rustc/worker.js @@ -0,0 +1,415 @@ +const { promise, resolve } = Promise.withResolvers(); +import("../node_modules/@oligami/shared-object/dist/index.js").then(resolve); +import { WASIFarmAnimal } from "../../dist/index.js"; + +let tree; +let term; +let rustc; +let rustc_with_lld; +let clang; +let llvm_tools; +let wasm_ld; +let cargo; +let std_out_keep; +let std_err_keep; +let root_dir; +let cat; + +const blueText = "\x1b[34m"; +const resetText = "\x1b[0m"; + +self.onmessage = async (e) => { + const { wasi_ref } = e.data; + + const { + promise: depend_rustc_files_promise, + resolve: depend_rustc_files_resolve, + } = Promise.withResolvers(); + const { + promise: depend_rustc_with_lld_promise, + resolve: depend_rustc_with_lld_resolve, + } = Promise.withResolvers(); + const { + promise: depend_clang_files_promise, + resolve: depend_clang_files_resolve, + } = Promise.withResolvers(); + const { promise: tmp_dir_promise, resolve: tmp_dir_resolve } = + Promise.withResolvers(); + const { promise: save_stdout_promise, resolve: save_stdout_resolve } = + Promise.withResolvers(); + + const depend_rustc_files_manage_worker = new Worker("depend_rustc_files.js", { + type: "module", + }); + depend_rustc_files_manage_worker.onmessage = (e) => { + depend_rustc_files_resolve(e.data); + }; + + const depend_rustc_with_lld_manage_worker = new Worker( + "depend_rustc_with_lld.js", + { + type: "module", + }, + ); + depend_rustc_with_lld_manage_worker.onmessage = (e) => { + depend_rustc_with_lld_resolve(e.data); + }; + + const depend_clang_files_manage_worker = new Worker("depend_clang_files.js", { + type: "module", + }); + depend_clang_files_manage_worker.onmessage = (e) => { + depend_clang_files_resolve(e.data); + }; + + const tmp_dir_manage_worker = new Worker("tmp_dir.js", { + type: "module", + }); + tmp_dir_manage_worker.onmessage = (e) => { + tmp_dir_resolve(e.data); + }; + + const save_stdout_manage_worker = new Worker("save_stdout.js", { + type: "module", + }); + save_stdout_manage_worker.onmessage = (e) => { + save_stdout_resolve(e.data); + }; + + const [ + depend_rustc_files, + depend_rustc_with_lld, + depend_clang_files, + tmp_dir, + save_stdout, + ] = await Promise.all([ + depend_rustc_files_promise, + depend_rustc_with_lld_promise, + depend_clang_files_promise, + tmp_dir_promise, + save_stdout_promise, + ]); + const { wasi_ref: wasi_ref_depend_rustc_files } = depend_rustc_files; + const { wasi_ref: wasi_ref_depend_rustc_with_lld_files } = + depend_rustc_with_lld; + const { wasi_ref: wasi_ref_depend_clang_files } = depend_clang_files; + const { wasi_ref: wasi_ref_tmp_dir } = tmp_dir; + const { wasi_ref: wasi_ref_save_stdout } = save_stdout; + + const wasi_refs = [ + wasi_ref_depend_rustc_files, + wasi_ref_depend_rustc_with_lld_files, + wasi_ref_depend_clang_files, + wasi_ref_tmp_dir, + wasi_ref, + ]; + + const { promise: tree_promise, resolve: tree_resolve } = + Promise.withResolvers(); + const { promise: rustc_promise, resolve: rustc_resolve } = + Promise.withResolvers(); + const { promise: rustc_with_lld_promise, resolve: rustc_with_lld_resolve } = + Promise.withResolvers(); + const { promise: clang_promise, resolve: clang_resolve } = + Promise.withResolvers(); + const { promise: cargo_promise, resolve: cargo_resolve } = + Promise.withResolvers(); + const { promise: cat_promise, resolve: cat_resolve } = + Promise.withResolvers(); + + const tree_worker = new Worker("tree.js", { + type: "module", + }); + tree_worker.onmessage = (e) => { + console.log("tree onmessage"); + tree_resolve(e.data); + }; + + const rustc_worker = new Worker("rustc.js", { + type: "module", + }); + rustc_worker.onmessage = (e) => { + console.log("rustc onmessage"); + rustc_resolve(e.data); + }; + + const rustc_with_lld_worker = new Worker("rustc_with_lld.js", { + type: "module", + }); + rustc_with_lld_worker.onmessage = (e) => { + console.log("rustc_with_lld onmessage"); + rustc_with_lld_resolve(e.data); + }; + + const clang_worker = new Worker("clang.js", { + type: "module", + }); + clang_worker.onmessage = (e) => { + console.log("clang onmessage"); + clang_resolve(e.data); + }; + + const cargo_worker = new Worker("cargo.js", { + type: "module", + }); + cargo_worker.onmessage = (e) => { + console.log("cargo onmessage"); + cargo_resolve(e.data); + }; + + const cat_worker = new Worker("cat.js", { + type: "module", + }); + cat_worker.onmessage = (e) => { + console.log("cat onmessage"); + cat_resolve(e.data); + }; + + tree_worker.postMessage({ + wasi_refs, + }); + rustc_worker.postMessage({ + wasi_refs: [wasi_ref_save_stdout, ...wasi_refs], + }); + rustc_with_lld_worker.postMessage({ + wasi_refs, + }); + clang_worker.postMessage({ + wasi_refs, + }); + cargo_worker.postMessage({ + wasi_refs, + }); + cat_worker.postMessage({ + wasi_refs, + }); + + console.log("Waiting for tree and rustc to finish..."); + + await Promise.all([ + tree_promise, + rustc_promise, + rustc_with_lld_promise, + clang_promise, + cargo_promise, + cat_promise, + ]); + + console.log("Sending run message..."); + + await promise; + + tree = new SharedObject.SharedObjectRef("tree").proxy(); + + term = new SharedObject.SharedObjectRef("term").proxy(); + + rustc = new SharedObject.SharedObjectRef("rustc").proxy(); + + rustc_with_lld = new SharedObject.SharedObjectRef("rustc_with_lld").proxy(); + + clang = new SharedObject.SharedObjectRef("clang").proxy(); + + llvm_tools = new SharedObject.SharedObjectRef("llvm-tools").proxy(); + + wasm_ld = new SharedObject.SharedObjectRef("wasm-ld").proxy(); + + std_out_keep = new SharedObject.SharedObjectRef("std_out_keep").proxy(); + + std_err_keep = new SharedObject.SharedObjectRef("std_err_keep").proxy(); + + root_dir = new SharedObject.SharedObjectRef("root_dir").proxy(); + + cargo = new SharedObject.SharedObjectRef("cargo").proxy(); + + cat = new SharedObject.SharedObjectRef("cat").proxy(); + + // cargo -h + await term.writeln(`\n$${blueText} cargo -h${resetText}`); + await cargo("-h"); + + // cargo new -h + await term.writeln(`\n$${blueText} cargo new -h${resetText}`); + await cargo("new", "-h"); + + // cargo new --bin + await term.writeln(`\n$${blueText} cargo new --bin helloworld${resetText}`); + await cargo("new", "--bin", "helloworld"); + + // cat /helloworld/src/main.rs + await term.writeln(`\n$${blueText} cat /helloworld/src/main.rs${resetText}`); + await cat("/helloworld/src/main.rs"); + + // cat /helloworld/Cargo.toml + await term.writeln(`\n$${blueText} cat /helloworld/Cargo.toml${resetText}`); + await cat("/helloworld/Cargo.toml"); + + // cargo run -h + await term.writeln(`\n$${blueText} cargo run -h${resetText}`); + await cargo("run", "-h"); + + // cargo run --manifest-path /helloworld/Cargo.toml --jobs 1 -- --sysroot /sysroot-with-lld + await term.writeln( + `\n$${blueText} cargo run --manifest-path /helloworld/Cargo.toml --jobs 1`, + ); + await cargo( + "run", + "--manifest-path", + "/helloworld/Cargo.toml", + "--jobs", + "1", + ); + + // llvm-tools + await term.writeln(`$${blueText} llvm-tools${resetText}`); + await llvm_tools(); + + // clang -h + await term.writeln(`\n$${blueText} clang --help${resetText}`); + await clang("--help"); + + // clang -v + await term.writeln(`\n$${blueText} clang -v${resetText}`); + await clang("-v"); + + // wasm-ld --help + await term.writeln(`\n$${blueText} wasm-ld --help${resetText}`); + await wasm_ld("--help"); + + // wasm-ld -v + await term.writeln(`\n$${blueText} wasm-ld -v${resetText}`); + await wasm_ld("-v"); + + // tree -h + await term.writeln(`\n$${blueText} tree -h${resetText}`); + await tree("-h"); + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // rustc -h + await std_out_keep.reset(); + await std_err_keep.reset(); + await term.writeln(`\n$${blueText} rustc -h${resetText}`); + await rustc("-h"); + const rustc_help = await std_out_keep.get(); + const rustc_help_err = await std_err_keep.get(); + console.log(rustc_help); + console.warn(rustc_help_err); + + // rustc /hello.rs --sysroot /sysroot --target wasm32-wasip1-threads -Csave-temps --out-dir /tmp + await term.writeln( + `\n$${blueText} rustc /hello.rs --sysroot /sysroot --target wasm32-wasip1-threads -Csave-temps --out-dir /tmp${resetText}`, + ); + try { + await std_out_keep.reset(); + await std_err_keep.reset(); + await rustc( + "/hello.rs", + "--sysroot", + "/sysroot", + "--target", + "wasm32-wasip1-threads", + "-Csave-temps", + "--out-dir", + "/tmp", + ); + } catch (e) { + console.error(e); + } + const out = await std_out_keep.get(); + const err = await std_err_keep.get(); + console.log(out); + console.warn(err); + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // If the glob pattern is used, + // it seems to be passed directly to path_open, + // so it is necessary to specify it carefully. + if (!err.includes("error: could not exec the linker")) { + throw new Error("cannot get lld arguments"); + } + // extract lld arguments line + const lld_args_and_etc = err + .split("\n") + .find((line) => line.includes("rust-lld") && line.includes("note: ")); + const lld_args_str = lld_args_and_etc.split('"rust-lld"')[1].trim(); + const lld_args_with_wrap = lld_args_str.split(" "); + let lld_args = lld_args_with_wrap.map((arg) => arg.slice(1, -1)); + // rm -flavor wasm + lld_args = lld_args.filter((arg) => arg !== "-flavor" && arg !== "wasm"); + // rm -Wl + lld_args = lld_args.map((arg) => { + if (arg.includes("-Wl,")) { + return arg.slice(4); + } + return arg; + }); + console.log(lld_args); + + await term.writeln( + `\n$${blueText} wasm-ld ${lld_args.join(" ")}${resetText}`, + ); + try { + await wasm_ld(lld_args); + } catch (error) { + console.error(error); + const redText = "\x1b[31m"; + const boldText = "\x1b[1m"; + + const message = `${boldText}${redText}Error: ${error.message}${resetText}\n`; + const stack = `${redText}Stack Trace: ${error.stack}${resetText}`; + + await term.writeln(message + stack); + } + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // rustc_with_lld /hello.rs --sysroot /sysroot --target wasm32-wasip1 + await term.writeln( + `\n$${blueText} rustc_with_lld /hello.rs --sysroot /sysroot-with-lld --target wasm32-wasip1${resetText}`, + ); + try { + await rustc_with_lld( + "/hello.rs", + "--sysroot", + "/sysroot-with-lld", + "--target", + "wasm32-wasip1", + ); + } catch (e) { + console.error(e); + } + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // run /hello.wasm + await term.writeln(`\n$${blueText} run /hello.wasm${resetText}`); + const created_wasm_buffer = await root_dir.get_file("hello.wasm"); + const created_wasm = await WebAssembly.compile(created_wasm_buffer); + const wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + [], // env + { + debug: false, + }, + ); + + // Memory is rewritten at this time. + const inst = await WebAssembly.instantiate(created_wasm, { + wasi_snapshot_preview1: wasi.wasiImport, + }); + + wasi.start(inst); + + // all done + await term.writeln(`\n$${blueText} All done!${resetText}`); +}; diff --git a/examples/wasi_workers/echo_and_rewrite.wasm b/examples/wasi_workers/echo_and_rewrite.wasm new file mode 100644 index 0000000..1f4e413 Binary files /dev/null and b/examples/wasi_workers/echo_and_rewrite.wasm differ diff --git a/examples/wasi_workers/index.html b/examples/wasi_workers/index.html new file mode 100644 index 0000000..f59f8d3 --- /dev/null +++ b/examples/wasi_workers/index.html @@ -0,0 +1,43 @@ + + + + + +

+ #### +

+ + + + diff --git a/examples/wasi_workers/main.rs b/examples/wasi_workers/main.rs new file mode 100644 index 0000000..41a744c --- /dev/null +++ b/examples/wasi_workers/main.rs @@ -0,0 +1,52 @@ +fn main() { + // ファイル名を受けとる + let args: Vec = std::env::args().collect(); + let filename = &args[1]; + + println!("reading file: {}", filename); + + // ファイルを読み込む + let file = std::fs::read_to_string(filename).expect("ファイルが読み込めませんでした"); + + // ファイルの内容を表示 + println!("{}", file); + + // ファイルの内容を書き換え + // 二つ目の引数の文字列を二つ目の文字列に書き換える + let replaced = file.replace(&args[2], &args[3]); + + // 書き換えた内容を表示 + println!("{}", replaced); + + println!("random replace start"); + + let start = std::env::args().nth(4).unwrap().parse::().unwrap(); + let end = std::env::args().nth(5).unwrap().parse::().unwrap(); + + // 新しいfileを作る + let new_file = format!("{}-{}~{}.txt", filename, start, end); + std::fs::write(&new_file, "$$$$$$$$$").expect("ファイルが書き込めませんでした"); + + let loop_n = std::env::args().nth(6).unwrap_or("100".to_string()).parse::().unwrap(); + + // loop { + for _ in 0..loop_n { + // ファイルを読み込む + let file = std::fs::read_to_string(filename).expect("ファイルが読み込めませんでした"); + + // ランダムな数値を生成 + let random = rand::random::() % (end - start) + start; + + // 生成した数値をkファイルの内容に書き換える + let replaced = format!("{}, {}", file, random); + + // 書き換えた内容を表示 + println!("{}", replaced); + + // ファイルを書き換える + std::fs::write(filename, &replaced).expect("ファイルが書き込めませんでした"); + + // 1秒待つ + // std::thread::sleep(std::time::Duration::from_secs(1)); + } +} diff --git a/examples/wasi_workers/worker.js b/examples/wasi_workers/worker.js new file mode 100644 index 0000000..b443ab3 --- /dev/null +++ b/examples/wasi_workers/worker.js @@ -0,0 +1,20 @@ + +import { File, OpenFile, ConsoleStdout, PreopenDirectory, WASIFarm } from "../../dist/index.js"; + +let wasi_farm; +(async () => { + wasi_farm = new WASIFarm( + undefined, + ConsoleStdout.lineBuffered(msg => console.log(`[WASI stdout on worker] ${msg}`)), + undefined, + [ + new PreopenDirectory("hello2", [ + ["hello2.txt", new File(new TextEncoder().encode("Hello, world!!!!!"))], + ]), + ], + { debug: true }, + ); + console.log("WASI farm created"); + let wasi_ref = await wasi_farm.get_ref(); + self.postMessage({ wasi_ref }); +})(); diff --git a/examples/wasi_workers/worker1.js b/examples/wasi_workers/worker1.js new file mode 100644 index 0000000..7c1c9e2 --- /dev/null +++ b/examples/wasi_workers/worker1.js @@ -0,0 +1,19 @@ +import { WASIFarmAnimal } from "../../dist/wasi_farm/animals.js"; + +self.onmessage = async function (e) { + const { wasi_ref, wasi_ref2 } = e.data; + + const wasi = new WASIFarmAnimal( + [wasi_ref, wasi_ref2], + ["echo_and_rewrite", "hello2/hello2.txt", "world", "new_world", "0", "100", "100"], // args + [""], // env + // options + ); + let wasm = await fetch("./echo_and_rewrite.wasm"); + let buff = await wasm.arrayBuffer(); + let { instance } = await WebAssembly.instantiate(buff, { + "wasi_snapshot_preview1": wasi.wasiImport, + }); + wasi.start(instance); +} + diff --git a/examples/wasi_workers/worker2.js b/examples/wasi_workers/worker2.js new file mode 100644 index 0000000..8064cb3 --- /dev/null +++ b/examples/wasi_workers/worker2.js @@ -0,0 +1,18 @@ +import { WASIFarmAnimal } from "../../dist/wasi_farm/animals.js"; + +self.onmessage = async function (e) { + const { wasi_ref, wasi_ref2 } = e.data; + + const wasi = new WASIFarmAnimal( + [wasi_ref, wasi_ref2], + ["echo_and_rewrite", "hello.txt", "world", "new_world", "100", "200"], // args + [""], // env + // options + ); + let wasm = await fetch("./echo_and_rewrite.wasm"); + let buff = await wasm.arrayBuffer(); + let { instance } = await WebAssembly.instantiate(buff, { + "wasi_snapshot_preview1": wasi.wasiImport, + }); + wasi.start(instance); +} diff --git a/examples/wasi_workers/worker3.js b/examples/wasi_workers/worker3.js new file mode 100644 index 0000000..400608c --- /dev/null +++ b/examples/wasi_workers/worker3.js @@ -0,0 +1,18 @@ +import { WASIFarmAnimal } from "../../dist/wasi_farm/animals.js"; + +self.onmessage = async function (e) { + const { wasi_ref, wasi_ref2 } = e.data; + + const wasi = new WASIFarmAnimal( + [wasi_ref2, wasi_ref], + ["echo_and_rewrite", "hello.txt", "world", "new_world", "200", "300"], // args + [""], // env + // options + ); + let wasm = await fetch("./echo_and_rewrite.wasm"); + let buff = await wasm.arrayBuffer(); + let { instance } = await WebAssembly.instantiate(buff, { + "wasi_snapshot_preview1": wasi.wasiImport, + }); + wasi.start(instance); +} diff --git a/examples/wasi_workers_rustc/rustc.html b/examples/wasi_workers_rustc/rustc.html new file mode 100644 index 0000000..a3b081c --- /dev/null +++ b/examples/wasi_workers_rustc/rustc.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + +
+
+

+ Note: the failure to invoke the linker at the end is expected. + WASI doesn't have a way to invoke external processes and rustc doesn't have a builtin linker. + This demo highlights how far `rustc` can get on this polyfill before failing due to other reasons. +

+ + + diff --git a/examples/wasi_workers_rustc/worker.js b/examples/wasi_workers_rustc/worker.js new file mode 100644 index 0000000..a251f88 --- /dev/null +++ b/examples/wasi_workers_rustc/worker.js @@ -0,0 +1,59 @@ +import { strace, WASIFarmAnimal } from "../../dist/index.js"; + +console.log("worker.js"); + +onmessage = async function (e) { + let wasm = await WebAssembly.compileStreaming(fetch("../wasm-rustc/bin/rustc.wasm")); + + console.log("worker.js onmessage", e.data); + + const { wasi_ref } = e.data; + + let args = ["rustc", "/hello.rs", "--sysroot", "/sysroot", "--target", "x86_64-unknown-linux-gnu", "-Cpanic=abort", "-Ccodegen-units=1"]; + let env = ["RUSTC_LOG=info"]; + + console.log("wasi_ref", wasi_ref); + + const w = new WASIFarmAnimal( + wasi_ref, + args, + env, + { + debug: true + } + ); + + let next_thread_id = 1; + + let inst = await WebAssembly.instantiate(wasm, { + "env": { memory: new WebAssembly.Memory({ initial: 256, maximum: 16384, shared: true }) }, + "wasi": { + "thread-spawn": function(start_arg) { + console.log("thread-spawn", start_arg); + + let thread_id = next_thread_id++; + inst.exports.wasi_thread_start(thread_id, start_arg); + return thread_id; + } + }, + "wasi_snapshot_preview1": strace(w.wasiImport, ["fd_prestat_get"]), + }); + + postMessage({ + "term": "\x1B[93mExecuting\x1B[0m", + }); + console.log(inst.exports); + try { w.start(inst); } catch(e) { + postMessage({ + term: "Exception: " + e.message, + }); + // /*term.writeln("backtrace:"); term.writeln(e.stack);*/ + } + postMessage({ + "term": "\x1B[92mDone\x1B[0m", + }); + + console.log(fds); + console.log(fds[5].dir); + console.log(fds[5].dir.contents.get("hello.hello.65c991d23c885d45-cgu.0.rcgu.o").data); +} diff --git a/examples/wasi_workers_single/echo_and_rewrite.wasm b/examples/wasi_workers_single/echo_and_rewrite.wasm new file mode 100644 index 0000000..d03ed2d Binary files /dev/null and b/examples/wasi_workers_single/echo_and_rewrite.wasm differ diff --git a/examples/wasi_workers_single/index.html b/examples/wasi_workers_single/index.html new file mode 100644 index 0000000..0dd4721 --- /dev/null +++ b/examples/wasi_workers_single/index.html @@ -0,0 +1,40 @@ + + + + + +

+ #### +

+ + + + diff --git a/package-lock.json b/package-lock.json index 6891208..7967411 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@bjorn3/browser_wasi_shim", "version": "0.3.0", "license": "MIT OR Apache-2.0", + "dependencies": { + "@bjorn3/browser_wasi_shim": "file:" + }, "devDependencies": { "@swc/cli": "^0.1.62", "@swc/core": "^1.3.37", @@ -16,23 +19,17 @@ "eslint": "^8.50.0", "playwright": "^1.40.1", "prettier": "^3.0.3", - "typescript": "^4.9.5" + "typescript": "^5.6.2" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/@bjorn3/browser_wasi_shim": { + "resolved": "", + "link": true }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -44,19 +41,17 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.11.1", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.4", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -75,34 +70,71 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "version": "8.57.1", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.13.0", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -112,16 +144,14 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "version": "2.0.3", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", - "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", "dev": true, + "license": "MIT", "dependencies": { "bin-check": "^4.1.0", "bin-version-check": "^5.0.0", @@ -138,9 +168,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -151,18 +180,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -173,9 +200,8 @@ }, "node_modules/@sindresorhus/is": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -184,14 +210,14 @@ } }, "node_modules/@swc/cli": { - "version": "0.1.62", - "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.62.tgz", - "integrity": "sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==", + "version": "0.1.65", "dev": true, + "license": "MIT", "dependencies": { "@mole-inc/bin-wrapper": "^8.0.1", "commander": "^7.1.0", "fast-glob": "^3.2.5", + "minimatch": "^9.0.3", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" @@ -215,11 +241,14 @@ } }, "node_modules/@swc/core": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.37.tgz", - "integrity": "sha512-VOFlEQ1pReOM73N9A7R8rt561GU8Rxsq833jiimWDUB2sXEN3V6n6wFTgYmZuMz2T4/R0cQA1nV48KkaT4gkFw==", + "version": "1.7.26", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" + }, "engines": { "node": ">=10" }, @@ -228,138 +257,33 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.37", - "@swc/core-darwin-x64": "1.3.37", - "@swc/core-linux-arm-gnueabihf": "1.3.37", - "@swc/core-linux-arm64-gnu": "1.3.37", - "@swc/core-linux-arm64-musl": "1.3.37", - "@swc/core-linux-x64-gnu": "1.3.37", - "@swc/core-linux-x64-musl": "1.3.37", - "@swc/core-win32-arm64-msvc": "1.3.37", - "@swc/core-win32-ia32-msvc": "1.3.37", - "@swc/core-win32-x64-msvc": "1.3.37" - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.37.tgz", - "integrity": "sha512-iIyVqqioUpVeT/hbBVfkrsjfCyL4idNH+LVKGmoTAWaTTSB0+UNhNuA7Wh2CqIHWh1Mv7IlumitWPcqsVDdoEw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.37.tgz", - "integrity": "sha512-dao5nXPWKxtaxqak4ZkRyBoApNIelW/glantQhPhj0FjMjuIQc+v03ldJ8XDByWOG+6xuVUTheANCtEccxoQBw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.37.tgz", - "integrity": "sha512-/mVrc8H/f062CUkqKGmBiil2VIYu4mKawHxERfeP1y38X5K/OwjG5s9MgO9TVxy+Ly6vejwj70kRhSa3hVp1Bw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.37.tgz", - "integrity": "sha512-eRQ3KaZI0j5LidTfOIi/kUVOOMuVmw1HCdt/Z1TAUKoHMLVxY8xcJ3pEE3/+ednI60EmHpwpJRs6LelXyL6uzQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.37.tgz", - "integrity": "sha512-w2BRLODyxNQY2rfHZMZ5ir6QrrnGBPlnIslTrgKmVbn1OjZoxUCtuqhrYnCmybaAc4DOkeH02TqynEFXrm+EMw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.37.tgz", - "integrity": "sha512-CfoH8EsZJZ9kunjMUjBNYD5fFuO86zw+K/o4wEw72Yg6ZEiqPmeIlCKU8tpTv4sK+CbhUXrmVzMB5tqsb2jALQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" + "@swc/core-darwin-arm64": "1.7.26", + "@swc/core-darwin-x64": "1.7.26", + "@swc/core-linux-arm-gnueabihf": "1.7.26", + "@swc/core-linux-arm64-gnu": "1.7.26", + "@swc/core-linux-arm64-musl": "1.7.26", + "@swc/core-linux-x64-gnu": "1.7.26", + "@swc/core-linux-x64-musl": "1.7.26", + "@swc/core-win32-arm64-msvc": "1.7.26", + "@swc/core-win32-ia32-msvc": "1.7.26", + "@swc/core-win32-x64-msvc": "1.7.26" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } } }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.37.tgz", - "integrity": "sha512-9YPrHYNdoG7PK11gV51GfL45biI2dic+YTqHUDKyykemsD7Ot1zUFX7Ty//pdvpKcKSff6SrHbfFACD5ziNirA==", + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.7.26", "cpu": [ "x64" ], "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.37.tgz", - "integrity": "sha512-h17Ek8/wCDje6BrXOvCXBM80oBRmTSMMdLyt87whTl5xqYlWYYs9oQIzZndNRTlNpTgjGO8Ns2eo4kwVxIkBIA==", - "cpu": [ - "arm64" - ], - "dev": true, + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -368,43 +292,23 @@ "node": ">=10" } }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.37.tgz", - "integrity": "sha512-1BR175E1olGy/zdt94cgdb6ps/lBNissAOaxyBk8taFpcjy3zpdP30yAoH0GIsC6isnZ5JfArbOJNRXXO5tE0Q==", - "cpu": [ - "ia32" - ], + "node_modules/@swc/counter": { + "version": "0.1.3", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } + "license": "Apache-2.0" }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.37", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.37.tgz", - "integrity": "sha512-1siDQ7dccQ1pesJmgAL3BUBbRPtfbNInOWnZOkiie/DfFqGQ117QKnCVyjUvwFKfTQx1+3UUTDmMSlRd00SlXg==", - "cpu": [ - "x64" - ], + "node_modules/@swc/types": { + "version": "0.1.12", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" } }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, + "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.0" }, @@ -414,15 +318,13 @@ }, "node_modules/@tokenizer/token": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/cacheable-request": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", @@ -431,58 +333,54 @@ } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true + "version": "4.0.4", + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", - "dev": true + "version": "7.0.15", + "dev": true, + "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { - "version": "18.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz", - "integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==", - "dev": true + "version": "22.5.5", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "version": "1.0.3", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", - "dev": true + "version": "7.5.8", + "dev": true, + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "version": "6.21.0", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -508,15 +406,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "version": "6.21.0", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -536,13 +433,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "version": "6.21.0", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -553,13 +449,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "version": "6.21.0", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -580,10 +475,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "version": "6.21.0", "dev": true, + "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -593,16 +487,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "version": "6.21.0", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -619,18 +513,31 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "version": "6.21.0", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -645,12 +552,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "version": "6.21.0", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -661,11 +567,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.12.1", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -675,18 +585,16 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -700,18 +608,16 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -724,8 +630,6 @@ }, "node_modules/arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ { @@ -740,34 +644,31 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-union": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bin-check": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^0.7.0", "executable": "^4.1.0" @@ -778,9 +679,8 @@ }, "node_modules/bin-version": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", - "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^5.0.0", "find-versions": "^5.0.0" @@ -793,14 +693,13 @@ } }, "node_modules/bin-version-check": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.0.0.tgz", - "integrity": "sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==", + "version": "5.1.0", "dev": true, + "license": "MIT", "dependencies": { "bin-version": "^6.0.0", - "semver": "^7.3.5", - "semver-truncate": "^2.0.0" + "semver": "^7.5.3", + "semver-truncate": "^3.0.0" }, "engines": { "node": ">=12" @@ -809,25 +708,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bin-version/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/bin-version/node_modules/execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -846,11 +730,10 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/bin-version/node_modules/get-stream": { + "node_modules/bin-version/node_modules/execa/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -858,11 +741,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bin-version/node_modules/is-stream": { + "node_modules/bin-version/node_modules/execa/node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -870,11 +752,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bin-version/node_modules/npm-run-path": { + "node_modules/bin-version/node_modules/execa/node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -882,68 +763,20 @@ "node": ">=8" } }, - "node_modules/bin-version/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bin-version/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bin-version/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bin-version/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -951,18 +784,16 @@ }, "node_modules/cacheable-lookup": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.6.0" } }, "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "7.0.4", "dev": true, + "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -978,9 +809,8 @@ }, "node_modules/cacheable-request/node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -993,18 +823,16 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1018,9 +846,8 @@ }, "node_modules/clone-response": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -1028,11 +855,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1042,30 +876,26 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/content-disposition": { "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -1074,23 +904,24 @@ } }, "node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "version": "7.0.3", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1103,9 +934,8 @@ }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -1116,38 +946,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/defer-to-connect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -1157,9 +972,8 @@ }, "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -1169,38 +983,36 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "version": "4.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "version": "8.57.1", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -1244,9 +1056,8 @@ }, "node_modules/eslint-scope": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -1260,9 +1071,8 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1270,94 +1080,30 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", "dev": true, + "license": "ISC", "dependencies": { - "shebang-regex": "^3.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/eslint/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/eslint/node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/espree": { "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -1371,10 +1117,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -1384,9 +1129,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -1396,27 +1140,24 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/execa": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -1430,11 +1171,50 @@ "node": ">=4" } }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/execa/node_modules/cross-spawn/node_modules/shebang-command": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/cross-spawn/node_modules/shebang-command/node_modules/shebang-regex": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/cross-spawn/node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^2.2.0" }, @@ -1444,9 +1224,8 @@ }, "node_modules/ext-list": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "^1.28.0" }, @@ -1456,9 +1235,8 @@ }, "node_modules/ext-name": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, + "license": "MIT", "dependencies": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -1469,15 +1247,13 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -1489,32 +1265,39 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -1524,9 +1307,8 @@ }, "node_modules/file-type": { "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", "dev": true, + "license": "MIT", "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0-alpha.9", @@ -1541,9 +1323,8 @@ }, "node_modules/filename-reserved-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", - "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -1553,9 +1334,8 @@ }, "node_modules/filenamify": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", - "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", "dev": true, + "license": "MIT", "dependencies": { "filename-reserved-regex": "^3.0.0", "strip-outer": "^2.0.0", @@ -1569,10 +1349,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1582,9 +1361,8 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1598,9 +1376,8 @@ }, "node_modules/find-versions": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", "dev": true, + "license": "MIT", "dependencies": { "semver-regex": "^4.0.5" }, @@ -1612,59 +1389,40 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.2.0", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "version": "3.3.1", + "dev": true, + "license": "ISC" }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } + "license": "ISC" }, "node_modules/get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1681,22 +1439,40 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", "dev": true, + "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob/node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/globals": { - "version": "13.22.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", - "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", + "version": "13.24.0", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -1709,9 +1485,8 @@ }, "node_modules/globby": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -1729,9 +1504,8 @@ }, "node_modules/got": { "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -1754,30 +1528,26 @@ }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/http-cache-semantics": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http2-wrapper": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -1788,17 +1558,14 @@ }, "node_modules/human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -1813,22 +1580,21 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -1842,18 +1608,16 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1861,24 +1625,21 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1888,51 +1649,45 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1942,36 +1697,31 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -1982,9 +1732,8 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -1997,24 +1746,21 @@ }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, + "license": "ISC", "dependencies": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -2022,26 +1768,23 @@ }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -2049,61 +1792,60 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.53.0", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "3.1.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "dev": true, + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-url": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2113,9 +1855,8 @@ }, "node_modules/npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^2.0.0" }, @@ -2123,20 +1864,26 @@ "node": ">=4" } }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -2148,17 +1895,16 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", "dev": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -2166,9 +1912,8 @@ }, "node_modules/os-filter-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", "dev": true, + "license": "MIT", "dependencies": { "arch": "^2.1.0" }, @@ -2178,27 +1923,24 @@ }, "node_modules/p-cancelable": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2211,9 +1953,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -2226,9 +1967,8 @@ }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -2238,45 +1978,40 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "version": "3.1.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "version": "5.2.0", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -2287,9 +2022,8 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2299,57 +2033,52 @@ }, "node_modules/pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/playwright": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz", - "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==", + "version": "1.47.2", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.40.1" + "playwright-core": "1.47.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz", - "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==", + "version": "1.47.2", "dev": true, + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.3.3", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -2362,33 +2091,28 @@ }, "node_modules/pseudomap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -2403,13 +2127,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/quick-lru": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2418,10 +2142,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "version": "3.6.2", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2433,9 +2156,8 @@ }, "node_modules/readable-web-to-node-stream": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "^3.6.0" }, @@ -2449,24 +2171,21 @@ }, "node_modules/resolve-alpn": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/responselike": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, + "license": "MIT", "dependencies": { "lowercase-keys": "^2.0.0" }, @@ -2476,9 +2195,8 @@ }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -2486,9 +2204,8 @@ }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -2501,8 +2218,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -2518,14 +2233,13 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -2540,16 +2254,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.3", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2559,9 +2270,8 @@ }, "node_modules/semver-regex": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2570,85 +2280,55 @@ } }, "node_modules/semver-truncate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-2.0.0.tgz", - "integrity": "sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==", + "version": "3.0.0", "dev": true, + "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "semver": "^7.3.5" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semver-truncate/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" + "node": ">=12" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/sort-keys": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-obj": "^1.0.0" }, @@ -2658,9 +2338,8 @@ }, "node_modules/sort-keys-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", "dev": true, + "license": "MIT", "dependencies": { "sort-keys": "^1.0.0" }, @@ -2670,27 +2349,24 @@ }, "node_modules/source-map": { "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2700,27 +2376,24 @@ }, "node_modules/strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -2730,9 +2403,8 @@ }, "node_modules/strip-outer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", - "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2741,16 +2413,15 @@ } }, "node_modules/strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "version": "7.1.1", "dev": true, + "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" + "peek-readable": "^5.1.3" }, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "type": "github", @@ -2759,9 +2430,8 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2771,15 +2441,13 @@ }, "node_modules/text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -2789,9 +2457,8 @@ }, "node_modules/token-types": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", "dev": true, + "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" @@ -2806,9 +2473,8 @@ }, "node_modules/trim-repeated": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", - "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^5.0.0" }, @@ -2816,13 +2482,23 @@ "node": ">=12" } }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -2830,9 +2506,8 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -2842,9 +2517,8 @@ }, "node_modules/type-fest": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -2853,62 +2527,71 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.2", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "dev": true, + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index f9610a1..5684b24 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,13 @@ "description": "A pure javascript shim for WASI", "type": "module", "scripts": { - "build": "swc src -d dist -s true && tsc --emitDeclarationOnly", + "build": "npm run worker && swc src -d dist -s true && tsc --emitDeclarationOnly", "prepare": "swc src -d dist && tsc --emitDeclarationOnly", "test:node": "./test/run-testsuite.sh node", "test:browser": "playwright-core install && ./test/run-testsuite.sh browser", "test": "npm run test:node && npm run test:browser", - "check": "tsc --noEmit && prettier src -c && eslint src/" + "check": "tsc --noEmit && prettier src -c && eslint src/", + "worker": "spack --config ./src/wasi_farm/shared_array_buffer/worker_background/spack.config.cjs && node src/wasi_farm/shared_array_buffer/worker_background/minify.js" }, "repository": { "type": "git", @@ -39,6 +40,9 @@ "eslint": "^8.50.0", "playwright": "^1.40.1", "prettier": "^3.0.3", - "typescript": "^4.9.5" + "typescript": "^5.6.2" + }, + "dependencies": { + "@bjorn3/browser_wasi_shim": "file:" } } diff --git a/src/fd.ts b/src/fd.ts index 4e3b6f5..a9dd115 100644 --- a/src/fd.ts +++ b/src/fd.ts @@ -78,6 +78,7 @@ export abstract class Fd { mtim: bigint, fst_flags: number, ): number { + console.warn("path_filestat_set_times not implemented"); return wasi.ERRNO_NOTSUP; } path_link(path: string, inode: Inode, allow_dir: boolean): number { @@ -117,11 +118,18 @@ export abstract class Fd { } export abstract class Inode { + file_stat: wasi.Filestat; + constructor(file_stat: wasi.Filestat) { + this.file_stat = file_stat; + } + abstract path_open( oflags: number, fs_rights_base: bigint, fd_flags: number, ): { ret: number; fd_obj: Fd | null }; - abstract stat(): wasi.Filestat; + stat(): wasi.Filestat { + return this.file_stat; + } } diff --git a/src/fs_mem.ts b/src/fs_mem.ts index 6f540ee..fad93bf 100644 --- a/src/fs_mem.ts +++ b/src/fs_mem.ts @@ -119,6 +119,16 @@ export class OpenFile extends Fd { fd_filestat_get(): { ret: number; filestat: wasi.Filestat } { return { ret: 0, filestat: this.file.stat() }; } + + fd_filestat_set_times(atim: bigint, mtim: bigint, fst_flags: number): number { + if (fst_flags & wasi.FSTFLAGS_ATIM) { + this.file.file_stat.atim = atim; + } + if (fst_flags & wasi.FSTFLAGS_MTIM) { + this.file.file_stat.mtim = mtim; + } + return wasi.ERRNO_SUCCESS; + } } export class OpenDirectory extends Fd { @@ -128,7 +138,7 @@ export class OpenDirectory extends Fd { super(); this.dir = dir; } - + OpenDirectory; // eslint-disable-next-line @typescript-eslint/no-unused-vars fd_seek(offset: bigint, whence: number): { ret: number; offset: bigint } { return { ret: wasi.ERRNO_BADF, offset: 0n }; @@ -199,6 +209,31 @@ export class OpenDirectory extends Fd { return { ret: 0, filestat: entry.stat() }; } + path_filestat_set_times( + flags: number, + path_str: string, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number { + const { ret: path_err, path } = Path.from(path_str); + if (path == null) { + return path_err; + } + + const { ret, entry } = this.dir.get_entry_for_path(path); + if (entry == null) { + return ret; + } + + if (fst_flags & wasi.FSTFLAGS_ATIM) { + entry.file_stat.atim = atim; + entry.file_stat.mtim = mtim; + } + + return wasi.ERRNO_SUCCESS; + } + path_lookup( path_str: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -471,7 +506,12 @@ export class File extends Inode { readonly: boolean; }>, ) { - super(); + super( + new wasi.Filestat( + wasi.FILETYPE_REGULAR_FILE, + BigInt(new Uint8Array(data).byteLength), + ), + ); this.data = new Uint8Array(data); this.readonly = !!options?.readonly; } @@ -496,12 +536,20 @@ export class File extends Inode { return { ret: wasi.ERRNO_SUCCESS, fd_obj: file }; } - get size(): bigint { - return BigInt(this.data.byteLength); + path_filestat_set_times( + flags: number, + path_str: string, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number { + this.file_stat.atim = atim; + this.file_stat.mtim = mtim; + return wasi.ERRNO_SUCCESS; } - stat(): wasi.Filestat { - return new wasi.Filestat(wasi.FILETYPE_REGULAR_FILE, this.size); + get size(): bigint { + return BigInt(this.data.byteLength); } } @@ -549,7 +597,7 @@ export class Directory extends Inode { contents: Map; constructor(contents: Map | [string, Inode][]) { - super(); + super(new wasi.Filestat(wasi.FILETYPE_DIRECTORY, 0n)); if (contents instanceof Array) { this.contents = new Map(contents); } else { @@ -562,10 +610,6 @@ export class Directory extends Inode { return { ret: wasi.ERRNO_SUCCESS, fd_obj: new OpenDirectory(this) }; } - stat(): wasi.Filestat { - return new wasi.Filestat(wasi.FILETYPE_DIRECTORY, 0n); - } - get_entry_for_path(path: Path): { ret: number; entry: Inode | null } { let entry: Inode = this; for (const component of path.parts) { @@ -576,7 +620,7 @@ export class Directory extends Inode { if (child !== undefined) { entry = child; } else { - debug.log(component); + debug.log("no such entry", component); return { ret: wasi.ERRNO_NOENT, entry: null }; } } diff --git a/src/fs_opfs.ts b/src/fs_opfs.ts index 7cdde8d..91f602c 100644 --- a/src/fs_opfs.ts +++ b/src/fs_opfs.ts @@ -28,7 +28,9 @@ export class SyncOPFSFile extends Inode { readonly: boolean; }>, ) { - super(); + super( + new wasi.Filestat(wasi.FILETYPE_REGULAR_FILE, BigInt(handle.getSize())), + ); this.handle = handle; this.readonly = !!options?.readonly; } @@ -56,10 +58,6 @@ export class SyncOPFSFile extends Inode { get size(): bigint { return BigInt(this.handle.getSize()); } - - stat(): wasi.Filestat { - return new wasi.Filestat(wasi.FILETYPE_REGULAR_FILE, this.size); - } } export class OpenSyncOPFSFile extends Fd { diff --git a/src/index.ts b/src/index.ts index 5f80030..a3ed42a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,8 @@ import WASI, { WASIProcExit } from "./wasi.js"; export { WASI, WASIProcExit }; - +import { WASIFarm, WASIFarmRef, WASIFarmAnimal } from "./wasi_farm/index.js"; +export { WASIFarm, WASIFarmRef, WASIFarmAnimal }; +export { thread_spawn_on_worker } from "./wasi_farm/index.js"; export { Fd, Inode } from "./fd.js"; export { File, diff --git a/src/serialize_error.ts b/src/serialize_error.ts new file mode 100644 index 0000000..0672412 --- /dev/null +++ b/src/serialize_error.ts @@ -0,0 +1,23 @@ +export type SerializedError = { + message: string; + name: string; + stack: string; + cause?: unknown; +}; + +export const serialize = (error: Error): SerializedError => { + return { + message: error.message, + name: error.name, + stack: error.stack, + cause: error.cause, + }; +}; + +export const deserialize = (serializedError: SerializedError): Error => { + const error = new Error(serializedError.message); + error.name = serializedError.name; + error.stack = serializedError.stack.replace(/.wasm:/g, ".wasm\n"); + error.cause = serializedError.cause; + return error; +}; diff --git a/src/wasi.ts b/src/wasi.ts index 4cb409b..b5e2428 100644 --- a/src/wasi.ts +++ b/src/wasi.ts @@ -16,10 +16,10 @@ export class WASIProcExit extends Error { } export default class WASI { - args: Array = []; - env: Array = []; - fds: Array = []; - inst: { exports: { memory: WebAssembly.Memory } }; + private args: Array = []; + private env: Array = []; + private fds: Array = []; + private inst: { exports: { memory: WebAssembly.Memory } }; // eslint-disable-next-line @typescript-eslint/no-explicit-any wasiImport: { [key: string]: (...args: Array) => unknown }; diff --git a/src/wasi_farm/animals.ts b/src/wasi_farm/animals.ts new file mode 100644 index 0000000..c835c3c --- /dev/null +++ b/src/wasi_farm/animals.ts @@ -0,0 +1,1470 @@ +import { debug } from "../debug.js"; +import { type Options, WASIProcExit } from "../wasi.js"; +import * as wasi from "../wasi_defs.js"; +import type { WASIFarmRef } from "./ref.js"; +import type { WASIFarmRefObject } from "./ref.js"; +import type { FdCloseSender } from "./sender.js"; +import { WASIFarmRefUseArrayBuffer } from "./shared_array_buffer/index.js"; +import type { WASIFarmRefUseArrayBufferObject } from "./shared_array_buffer/index.js"; +import { ThreadSpawner } from "./shared_array_buffer/index.js"; + +export class WASIFarmAnimal { + args: Array; + env: Array; + + private wasi_farm_refs: WASIFarmRef[]; + + private id_in_wasi_farm_ref: Array; + + private extend_imports: boolean; + + inst: { exports: { memory: WebAssembly.Memory } }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + wasiImport: { [key: string]: (...args: Array) => unknown }; + + wasiThreadImport: { + "thread-spawn": (start_arg: number) => number; + }; + + extendImport: { + fetch_open: ( + url_ptr: number, + url_len: number, + method_ptr: number, + method_len: number, + serialized_headers_ptr: number, + serialized_headers_len: number, + body_ptr: number, + body_len: number, + ) => number; + }; + + private can_array_buffer; + + private can_thread_spawn: boolean; + + private thread_spawner: ThreadSpawner; + + async wait_worker_background_worker(): Promise { + await this.thread_spawner.wait_worker_background_worker(); + } + + check_worker_background_worker(): void { + this.thread_spawner.check_worker_background_worker(); + } + + // Each process has a specific fd that it can access. + // If it does not exist in the map, it cannot be accessed. + // child process can access parent process's fd. + // so, it is necessary to manage the fd on global scope. + // [fd, wasi_ref_n] + protected fd_map: Array<[number, number]>; + + protected get_fd_and_wasi_ref( + fd: number, + ): [number | undefined, WASIFarmRef | undefined] { + const mapped_fd_and_wasi_ref_n = this.fd_map[fd]; + if (!mapped_fd_and_wasi_ref_n) { + // console.log("fd", fd, "is not found"); + return [undefined, undefined]; + } + const [mapped_fd, wasi_ref_n] = mapped_fd_and_wasi_ref_n; + // console.log("fd", fd, "is found", "mapped_fd", mapped_fd, "wasi_ref_n", wasi_ref_n); + return [mapped_fd, this.wasi_farm_refs[wasi_ref_n]]; + } + + protected get_fd_and_wasi_ref_n( + fd: number, + ): [number | undefined, number | undefined] { + const mapped_fd_and_wasi_ref_n = this.fd_map[fd]; + if (!mapped_fd_and_wasi_ref_n) { + // console.log("fd", fd, "is not found"); + return [undefined, undefined]; + } + const [mapped_fd, wasi_ref_n] = mapped_fd_and_wasi_ref_n; + // console.log("fd", fd, "is found", "mapped_fd", mapped_fd, "wasi_ref_n", wasi_ref_n); + return [mapped_fd, wasi_ref_n]; + } + + /// Start a WASI command + start(instance: { + // FIXME v0.3: close opened Fds after execution + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }) { + this.inst = instance; + + try { + instance.exports._start(); + + if (this.can_thread_spawn) { + this.thread_spawner.done_notify(0); + } + + return 0; + } catch (e) { + if (e instanceof WASIProcExit) { + if (this.can_thread_spawn) { + this.thread_spawner.done_notify(e.code); + } + + return e.code; + } + throw e; + } + } + + /// Start a WASI command on a thread + /// If a module has child threads and a child thread throws an error, + /// the main thread should also be stopped, + /// but there is no way to stop it, + /// so the entire worker is stopped. + /// If it is not necessary, do not use it. + /// Custom imports are not implemented, + /// function because it cannot be passed to other threads. + /// If the sharedObject library someday supports synchronization, it could be used to support this. + async async_start_on_thread(): Promise { + if (!this.can_thread_spawn) { + throw new Error("thread_spawn is not supported"); + } + + await this.wait_worker_background_worker(); + + if (this.inst) { + throw new Error("what happened?"); + } + + const view = new Uint8Array(this.get_share_memory().buffer); + view.fill(0); + + await this.thread_spawner.async_start_on_thread( + this.args, + this.env, + this.extend_imports, + ); + + const code = await this.thread_spawner.async_wait_done_or_error(); + + return code; + } + + block_start_on_thread(): number { + if (!this.can_thread_spawn) { + throw new Error("thread_spawn is not supported"); + } + + console.log("block_start_on_thread"); + + this.check_worker_background_worker(); + + console.log("block_start_on_thread"); + + if (this.inst) { + throw new Error("what happened?"); + } + + const view = new Uint8Array(this.get_share_memory().buffer); + view.fill(0); + + console.log("block_start_on_thread: start"); + + this.thread_spawner.block_start_on_thread( + this.args, + this.env, + this.extend_imports, + ); + + console.log("block_start_on_thread: wait"); + + const code = this.thread_spawner.block_wait_done_or_error(); + + console.log("block_start_on_thread: done"); + + return code; + } + + wasi_thread_start( + instance: { + exports: { + memory: WebAssembly.Memory; + wasi_thread_start: (thread_id: number, start_arg: number) => void; + }; + }, + thread_id: number, + start_arg: number, + ) { + this.inst = instance; + try { + instance.exports.wasi_thread_start(thread_id, start_arg); + return 0; + } catch (e) { + if (e instanceof WASIProcExit) { + return e.code; + } + throw e; + } + } + + /// Initialize a WASI reactor + initialize(instance: { + exports: { memory: WebAssembly.Memory; _initialize?: () => unknown }; + }) { + this.inst = instance; + if (instance.exports._initialize) { + instance.exports._initialize(); + } + } + + private mapping_fds( + wasi_farm_refs: Array, + override_fd_maps?: Array, + ) { + this.fd_map = [undefined, undefined, undefined]; + + // console.log("wasi_farm_refs", wasi_farm_refs); + for (let i = 0; i < wasi_farm_refs.length; i++) { + // console.log("fd_map", [...this.fd_map]); + + const wasi_farm_ref = wasi_farm_refs[i]; + // console.log("override_fd_map", wasi_farm_ref.default_fds); + const override_fd_map = override_fd_maps + ? override_fd_maps[i] + : wasi_farm_ref.default_fds; + // console.log("override_fd_map", override_fd_map); + const stdin = wasi_farm_ref.get_stdin(); + const stdout = wasi_farm_ref.get_stdout(); + const stderr = wasi_farm_ref.get_stderr(); + // console.log("stdin", stdin, "stdout", stdout, "stderr", stderr); + if (stdin !== undefined) { + if (this.fd_map[0] === undefined) { + if (override_fd_map.includes(stdin)) { + this.fd_map[0] = [stdin, i]; + } + } + } + if (stdout !== undefined) { + // console.log("stdout", stdout, i, "override_fd_map", override_fd_map); + if (this.fd_map[1] === undefined) { + if (override_fd_map.includes(stdout)) { + // console.log("stdout defined"); + this.fd_map[1] = [stdout, i]; + } + } + } + if (stderr !== undefined) { + if (this.fd_map[2] === undefined) { + if (override_fd_map.includes(stderr)) { + this.fd_map[2] = [stderr, i]; + } + } + } + for (const j of override_fd_map) { + if (j === stdin || j === stdout || j === stderr) { + continue; + } + this.map_new_fd(j, i); + } + wasi_farm_ref.set_park_fds_map(override_fd_map); + + // console.log("fd_map", this.fd_map); + } + + if (this.fd_map[0] === undefined) { + throw new Error("stdin is not found"); + } + if (this.fd_map[1] === undefined) { + throw new Error("stdout is not found"); + } + if (this.fd_map[2] === undefined) { + throw new Error("stderr is not found"); + } + } + + private map_new_fd(fd: number, wasi_ref_n: number): number { + let n = -1; + // 0, 1, 2 are reserved for stdin, stdout, stderr + for (let i = 3; i < this.fd_map.length; i++) { + if (this.fd_map[i] === undefined) { + n = i; + break; + } + } + if (n === -1) { + n = this.fd_map.push(undefined) - 1; + } + this.fd_map[n] = [fd, wasi_ref_n]; + return n; + } + + private map_new_fd_and_notify(fd: number, wasi_ref_n: number): number { + const n = this.map_new_fd(fd, wasi_ref_n); + // console.log("animals: fd", fd, "is mapped to", n); + // console.log("wasi_ref_n", wasi_ref_n); + this.wasi_farm_refs[wasi_ref_n].set_park_fds_map([fd]); + return n; + } + + private map_set_fd_and_notify(fd: number, wasi_ref_n: number, index: number) { + if (this.fd_map[index] !== undefined) { + throw new Error("fd is already mapped"); + } + this.fd_map[index] = [fd, wasi_ref_n]; + this.wasi_farm_refs[wasi_ref_n].set_park_fds_map([fd]); + } + + private check_fds() { + const rm_fds: Array<[number, number]> = []; + for (let i = 0; i < this.id_in_wasi_farm_ref.length; i++) { + const id = this.id_in_wasi_farm_ref[i]; + const removed_fds = (this.wasi_farm_refs[i] as FdCloseSender).get(id); + if (removed_fds) { + for (const fd of removed_fds) { + rm_fds.push([fd, i]); + } + } + } + + if (rm_fds.length > 0) { + for (let i = 0; i < this.fd_map.length; i++) { + const fd_and_wasi_ref_n = this.fd_map[i]; + // biome-ignore lint/suspicious/noDoubleEquals: + if (fd_and_wasi_ref_n == undefined) { + continue; + } + // console.log("fd_and_wasi_ref_n", fd_and_wasi_ref_n); + const [fd, wasi_ref_n] = fd_and_wasi_ref_n; + for (const [rm_fd_fd, rm_fd_wasi_ref_n] of rm_fds) { + if (fd === rm_fd_fd && wasi_ref_n === rm_fd_wasi_ref_n) { + this.fd_map[i] = undefined; + // console.log("fd", i, "is removed"); + break; + } + } + // console.log("fd_and_wasi_ref_n", fd_and_wasi_ref_n); + } + // console.log("rm_fds.length", rm_fds.length); + // console.log("rm_fds", rm_fds); + } + } + + get_share_memory(): WebAssembly.Memory { + return this.thread_spawner.get_share_memory(); + } + + constructor( + wasi_farm_refs: WASIFarmRefObject[] | WASIFarmRefObject, + args: Array, + env: Array, + options: Options & { + can_thread_spawn?: boolean; + thread_spawn_worker_url?: string; + thread_spawn_wasm?: WebAssembly.Module; + extend_imports?: boolean; + hand_override_fd_map?: Array<[number, number]>; + } = {}, + override_fd_maps?: Array, + thread_spawner?: ThreadSpawner, + ) { + debug.enable(options.debug); + + this.extend_imports = options.extend_imports || false; + + let wasi_farm_refs_tmp: WASIFarmRefObject[]; + if (Array.isArray(wasi_farm_refs)) { + wasi_farm_refs_tmp = + wasi_farm_refs as unknown as Array; + } else { + wasi_farm_refs_tmp = [wasi_farm_refs as unknown as WASIFarmRefObject]; + } + + try { + new SharedArrayBuffer(4); + this.can_array_buffer = true; + } catch (_) { + this.can_array_buffer = false; + } + + this.id_in_wasi_farm_ref = []; + this.wasi_farm_refs = []; + for (let i = 0; i < wasi_farm_refs_tmp.length; i++) { + if (this.can_array_buffer) { + this.wasi_farm_refs.push( + WASIFarmRefUseArrayBuffer.init_self( + wasi_farm_refs_tmp[i] as WASIFarmRefUseArrayBufferObject, + ), + ); + } else { + throw new Error("Non SharedArrayBuffer is not supported yet"); + } + this.id_in_wasi_farm_ref.push(this.wasi_farm_refs[i].set_id()); + } + + // console.log("this.wasi_farm_refs", this.wasi_farm_refs); + + if (options.can_thread_spawn) { + this.can_thread_spawn = options.can_thread_spawn; + + if (thread_spawner) { + if (!(thread_spawner instanceof ThreadSpawner)) { + throw new Error("thread_spawner is not ThreadSpawner"); + } + + this.thread_spawner = thread_spawner; + } else { + if (options.thread_spawn_worker_url === undefined) { + throw new Error("thread_spawn_worker_url is not defined"); + } + if (options.thread_spawn_wasm === undefined) { + throw new Error("thread_spawn_wasm is not defined"); + } + + this.thread_spawner = new ThreadSpawner( + options.thread_spawn_worker_url, + wasi_farm_refs_tmp, + undefined, + undefined, + undefined, + options.thread_spawn_wasm, + ); + } + } + + this.mapping_fds(this.wasi_farm_refs, override_fd_maps); + if (options.hand_override_fd_map) { + this.fd_map = options.hand_override_fd_map; + } + + // console.log("this.fd_map", this.fd_map); + + this.args = args; + this.env = env; + const self = this; + this.wasiImport = { + args_sizes_get(argc: number, argv_buf_size: number): number { + self.check_fds(); + const buffer = new DataView(self.inst.exports.memory.buffer); + buffer.setUint32(argc, self.args.length, true); + let buf_size = 0; + for (const arg of self.args) { + buf_size += arg.length + 1; + } + buffer.setUint32(argv_buf_size, buf_size, true); + // debug.log( + // buffer.getUint32(argc, true), + // buffer.getUint32(argv_buf_size, true), + // ); + debug.log("read args_sizes_get: len", self.args.length); + return 0; + }, + args_get(argv: number, argv_buf: number): number { + self.check_fds(); + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const orig_argv_buf = argv_buf; + for (let i = 0; i < self.args.length; i++) { + buffer.setUint32(argv, argv_buf, true); + argv += 4; + const arg = new TextEncoder().encode(self.args[i]); + buffer8.set(arg, argv_buf); + buffer.setUint8(argv_buf + arg.length, 0); + argv_buf += arg.length + 1; + } + // if (debug.enabled) { + debug.log( + "read args_get: args", + new TextDecoder("utf-8").decode( + buffer8.slice(orig_argv_buf, argv_buf), + ), + ); + // } + return 0; + }, + environ_sizes_get(environ_count: number, environ_size: number): number { + self.check_fds(); + const buffer = new DataView(self.inst.exports.memory.buffer); + buffer.setUint32(environ_count, self.env.length, true); + let buf_size = 0; + for (const environ of self.env) { + buf_size += environ.length + 1; + } + buffer.setUint32(environ_size, buf_size, true); + // debug.log( + // buffer.getUint32(environ_count, true), + // buffer.getUint32(environ_size, true), + // ); + debug.log("read environ_sizes_get: len", self.env.length); + return 0; + }, + environ_get(environ: number, environ_buf: number): number { + self.check_fds(); + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const orig_environ_buf = environ_buf; + for (let i = 0; i < self.env.length; i++) { + buffer.setUint32(environ, environ_buf, true); + environ += 4; + const e = new TextEncoder().encode(self.env[i]); + buffer8.set(e, environ_buf); + buffer.setUint8(environ_buf + e.length, 0); + environ_buf += e.length + 1; + } + // if (debug.enabled) { + debug.log( + "read environ_get: environ", + new TextDecoder("utf-8").decode( + buffer8.slice(orig_environ_buf, environ_buf), + ), + ); + // } + return 0; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + clock_res_get(id: number, res_ptr: number): number { + self.check_fds(); + let resolutionValue: bigint; + switch (id) { + case wasi.CLOCKID_MONOTONIC: { + // https://developer.mozilla.org/en-US/docs/Web/API/Performance/now + // > Resolution in isolated contexts: 5 microseconds + resolutionValue = 5_000n; // 5 microseconds + break; + } + case wasi.CLOCKID_REALTIME: { + resolutionValue = 1_000_000n; // 1 millisecond? + break; + } + default: + return wasi.ERRNO_NOSYS; + } + const view = new DataView(self.inst.exports.memory.buffer); + view.setBigUint64(res_ptr, resolutionValue, true); + return wasi.ERRNO_SUCCESS; + }, + clock_time_get(id: number, precision: bigint, time: number): number { + self.check_fds(); + const buffer = new DataView(self.inst.exports.memory.buffer); + if (id === wasi.CLOCKID_REALTIME) { + buffer.setBigUint64( + time, + BigInt(new Date().getTime()) * 1_000_000n, + true, + ); + } else if (id === wasi.CLOCKID_MONOTONIC) { + let monotonic_time: bigint; + try { + monotonic_time = BigInt(Math.round(performance.now() * 1000000)); + } catch (e) { + // performance.now() is only available in browsers. + // TODO use the perf_hooks builtin module for NodeJS + monotonic_time = 0n; + } + buffer.setBigUint64(time, monotonic_time, true); + } else { + // TODO + buffer.setBigUint64(time, 0n, true); + } + return 0; + }, + fd_advise( + fd: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + offset: bigint, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + len: bigint, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + advice: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_advise(mapped_fd); + }, + fd_allocate(fd: number, offset: bigint, len: bigint) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_allocate(mapped_fd, offset, len); + }, + fd_close(fd: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const ret = wasi_farm_ref.fd_close(mapped_fd); + self.check_fds(); + return ret; + }, + fd_datasync(fd: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_datasync(mapped_fd); + }, + fd_fdstat_get(fd: number, fdstat_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const [fdstat, ret] = wasi_farm_ref.fd_fdstat_get(mapped_fd); + if (fdstat) { + fdstat.write_bytes( + new DataView(self.inst.exports.memory.buffer), + fdstat_ptr, + ); + } + return ret; + }, + fd_fdstat_set_flags(fd: number, flags: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_fdstat_set_flags(mapped_fd, flags); + }, + fd_fdstat_set_rights( + fd: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_fdstat_set_rights( + mapped_fd, + fs_rights_base, + fs_rights_inheriting, + ); + }, + fd_filestat_get(fd: number, filestat_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const [filestat, ret] = wasi_farm_ref.fd_filestat_get(mapped_fd); + if (filestat) { + filestat.write_bytes( + new DataView(self.inst.exports.memory.buffer), + filestat_ptr, + ); + } + return ret; + }, + fd_filestat_set_size(fd: number, size: bigint) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_filestat_set_size(mapped_fd, size); + }, + fd_filestat_set_times( + fd: number, + atim: bigint, + mtim: bigint, + fst_flags: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_filestat_set_times( + mapped_fd, + atim, + mtim, + fst_flags, + ); + }, + fd_pread( + fd: number, + iovs_ptr: number, + iovs_len: number, + offset: bigint, + nread_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const iovs_view = new Uint32Array( + buffer.buffer, + iovs_ptr, + iovs_len * 2, + ); + const [nerad_and_read_data, ret] = wasi_farm_ref.fd_pread( + mapped_fd, + iovs_view, + offset, + ); + if (nerad_and_read_data) { + const iovecs = wasi.Iovec.read_bytes_array( + buffer, + iovs_ptr, + iovs_len, + ); + const [nread, read_data] = nerad_and_read_data; + buffer.setUint32(nread_ptr, nread, true); + let nreaded = 0; + for (const iovec of iovecs) { + if (nreaded + iovec.buf_len >= read_data.length) { + buffer8.set(read_data, iovec.buf); + break; + } + buffer8.set( + read_data.slice(nreaded, nreaded + iovec.buf_len), + iovec.buf, + ); + nreaded += iovec.buf_len; + } + } + return ret; + }, + fd_prestat_get(fd: number, prestat_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const [prestat, ret] = wasi_farm_ref.fd_prestat_get(mapped_fd); + if (prestat) { + const [tag, name_len] = prestat; + const buffer = new DataView(self.inst.exports.memory.buffer); + buffer.setUint32(prestat_ptr, tag, true); + buffer.setUint32(prestat_ptr + 4, name_len, true); + } + return ret; + }, + fd_prestat_dir_name(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + // console.log("fd_prestat_dir_name: fd", mapped_fd, "path_len", path_len); + const [path, ret] = wasi_farm_ref.fd_prestat_dir_name( + mapped_fd, + path_len, + ); + if (path) { + // console.log("fd_prestat_dir_name", new TextDecoder().decode(path)); + // console.log("fd_prestat_dir_name", path); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + buffer8.set(path, path_ptr); + } + return ret; + }, + fd_pwrite( + fd: number, + iovs_ptr: number, + iovs_len: number, + offset: bigint, + nwritten_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const iovecs = wasi.Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len); + const data = new Uint8Array( + iovecs.reduce((acc, iovec) => acc + iovec.buf_len, 0), + ); + let nwritten = 0; + for (const iovec of iovecs) { + data.set( + buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len), + nwritten, + ); + nwritten += iovec.buf_len; + } + const [written, ret] = wasi_farm_ref.fd_pwrite(mapped_fd, data, offset); + if (written) { + buffer.setUint32(nwritten_ptr, written, true); + } + return ret; + }, + fd_read( + fd: number, + iovs_ptr: number, + iovs_len: number, + nread_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const iovs_view = new Uint32Array( + buffer.buffer, + iovs_ptr, + iovs_len * 2, + ); + + const [nerad_and_read_data, ret] = wasi_farm_ref.fd_read( + mapped_fd, + iovs_view, + ); + if (nerad_and_read_data) { + const iovecs = wasi.Iovec.read_bytes_array( + buffer, + iovs_ptr, + iovs_len, + ); + const [nread, read_data] = nerad_and_read_data; + + // console.log("fd_read: nread", nread, new TextDecoder().decode(read_data)); + + // fd_read: ref: 14 30 14 + // animals.ts:325 fd_read: nread 14 Hello, world! + + buffer.setUint32(nread_ptr, nread, true); + let nreaded = 0; + for (const iovec of iovecs) { + if (nreaded + iovec.buf_len >= read_data.length) { + buffer8.set(read_data, iovec.buf); + break; + } + buffer8.set( + read_data.slice(nreaded, nreaded + iovec.buf_len), + iovec.buf, + ); + nreaded += iovec.buf_len; + } + } + return ret; + }, + fd_readdir( + fd: number, + buf_ptr: number, + buf_len: number, + cookie: bigint, + buf_used_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const [nerad_and_read_data, ret] = wasi_farm_ref.fd_readdir( + mapped_fd, + buf_len, + cookie, + ); + if (nerad_and_read_data) { + const [read_data, buf_used] = nerad_and_read_data; + buffer.setUint32(buf_used_ptr, buf_used, true); + buffer8.set(read_data, buf_ptr); + } + return ret; + }, + fd_renumber(fd: number, to: number) { + self.check_fds(); + + const [mapped_to, wasi_farm_ref_to] = self.get_fd_and_wasi_ref(to); + + if (mapped_to !== undefined && wasi_farm_ref_to !== undefined) { + const ret = wasi_farm_ref_to.fd_close(mapped_to); + self.check_fds(); + if (ret !== wasi.ERRNO_SUCCESS) { + return ret; + } + } + + if (self.fd_map[to]) { + throw new Error("fd is already mapped"); + } + + self.fd_map[to] = self.fd_map[fd]; + + self.fd_map[fd] = undefined; + + return wasi.ERRNO_SUCCESS; + }, + fd_seek( + fd: number, + offset: bigint, + whence: number, + newoffset_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const [newoffset, ret] = wasi_farm_ref.fd_seek( + mapped_fd, + offset, + whence, + ); + if (newoffset) { + const buffer = new DataView(self.inst.exports.memory.buffer); + + // wasi.ts use BigInt for offset, but API use Uint64 + buffer.setBigUint64(newoffset_ptr, newoffset, true); + } + return ret; + }, + fd_sync(fd: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_sync(mapped_fd); + }, + fd_tell(fd: number, newoffset_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const [newoffset, ret] = wasi_farm_ref.fd_tell(mapped_fd); + if (newoffset) { + const buffer = new DataView(self.inst.exports.memory.buffer); + buffer.setBigUint64(newoffset_ptr, newoffset, true); + } + return ret; + }, + fd_write( + fd: number, + iovs_ptr: number, + iovs_len: number, + nwritten_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + + // console.log("fd_write", fd, iovs_ptr, iovs_len, nwritten_ptr); + + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const iovecs = wasi.Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len); + // console.log("iovecs", iovecs); + const data = new Uint8Array( + iovecs.reduce((acc, iovec) => acc + iovec.buf_len, 0), + ); + // console.log("data", data); + let nwritten = 0; + for (const iovec of iovecs) { + data.set( + buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len), + nwritten, + ); + nwritten += iovec.buf_len; + } + + // console.log("fd_write: ", fd, new TextDecoder().decode(data)); + + const [written, ret] = wasi_farm_ref.fd_write(mapped_fd, data); + + // console.log("fd_write end", fd, ret, written); + + if (written) { + buffer.setUint32(nwritten_ptr, written, true); + } + return ret; + }, + path_create_directory(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_create_directory(mapped_fd, path); + }, + path_filestat_get( + fd: number, + flags: number, + path_ptr: number, + path_len: number, + filestat_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const buffer = new DataView(self.inst.exports.memory.buffer); + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + const [filestat, ret] = wasi_farm_ref.path_filestat_get( + mapped_fd, + flags, + path, + ); + if (filestat) { + filestat.write_bytes(buffer, filestat_ptr); + } + return ret; + }, + path_filestat_set_times( + fd: number, + flags: number, + path_ptr: number, + path_len: number, + atim: bigint, + mtim: bigint, + fst_flags: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_filestat_set_times( + mapped_fd, + flags, + path, + atim, + mtim, + fst_flags, + ); + }, + // TODO! Make it work with different wasi_farm_ref + path_link( + old_fd: number, + old_flags: number, + old_path_ptr: number, + old_path_len: number, + new_fd: number, + new_path_ptr: number, + new_path_len: number, + ) { + self.check_fds(); + const [mapped_old_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(old_fd); + const [mapped_new_fd, wasi_farm_ref_new] = + self.get_fd_and_wasi_ref(new_fd); + if ( + mapped_old_fd === undefined || + wasi_farm_ref === undefined || + mapped_new_fd === undefined || + wasi_farm_ref_new === undefined + ) { + return wasi.ERRNO_BADF; + } + if (wasi_farm_ref !== wasi_farm_ref_new) { + return wasi.ERRNO_BADF; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const old_path = buffer8.slice( + old_path_ptr, + old_path_ptr + old_path_len, + ); + const new_path = buffer8.slice( + new_path_ptr, + new_path_ptr + new_path_len, + ); + return wasi_farm_ref.path_link( + mapped_old_fd, + old_flags, + old_path, + mapped_new_fd, + new_path, + ); + }, + path_open( + fd: number, + dirflags: number, + path_ptr: number, + path_len: number, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + opened_fd_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref_n] = self.get_fd_and_wasi_ref_n(fd); + if (mapped_fd === undefined || wasi_farm_ref_n === undefined) { + return wasi.ERRNO_BADF; + } + const wasi_farm_ref = self.wasi_farm_refs[wasi_farm_ref_n]; + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + const [opened_fd, ret] = wasi_farm_ref.path_open( + mapped_fd, + dirflags, + path, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ); + if (opened_fd) { + if (self.fd_map.includes([opened_fd, wasi_farm_ref_n])) { + throw new Error("opened_fd already exists"); + } + const mapped_opened_fd = self.map_new_fd_and_notify( + opened_fd, + wasi_farm_ref_n, + ); + const buffer = new DataView(self.inst.exports.memory.buffer); + buffer.setUint32(opened_fd_ptr, mapped_opened_fd, true); + } + return ret; + }, + path_readlink( + fd: number, + path_ptr: number, + path_len: number, + buf_ptr: number, + buf_len: number, + buf_used_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + const [buf, ret] = wasi_farm_ref.path_readlink( + mapped_fd, + path, + buf_len, + ); + if (buf) { + const buffer = new DataView(self.inst.exports.memory.buffer); + buffer.setUint32(buf_used_ptr, buf.length, true); + buffer8.set(buf, buf_ptr); + } + return ret; + }, + path_remove_directory(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_remove_directory(mapped_fd, path); + }, + // TODO! Make it work with different wasi_farm_ref + path_rename( + old_fd: number, + old_path_ptr: number, + old_path_len: number, + new_fd: number, + new_path_ptr: number, + new_path_len: number, + ) { + if (old_fd === new_fd) { + return wasi.ERRNO_SUCCESS; + } + console.log("path_rename", old_fd, new_fd); + self.check_fds(); + console.log("path_rename", old_fd, new_fd); + const [mapped_old_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(old_fd); + const [mapped_new_fd, wasi_farm_ref_new] = + self.get_fd_and_wasi_ref(new_fd); + if ( + mapped_old_fd === undefined || + wasi_farm_ref === undefined || + mapped_new_fd === undefined || + wasi_farm_ref_new === undefined + ) { + return wasi.ERRNO_BADF; + } + if (wasi_farm_ref !== wasi_farm_ref_new) { + return wasi.ERRNO_BADF; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const old_path = buffer8.slice( + old_path_ptr, + old_path_ptr + old_path_len, + ); + const new_path = buffer8.slice( + new_path_ptr, + new_path_ptr + new_path_len, + ); + return wasi_farm_ref.path_rename( + mapped_old_fd, + old_path, + mapped_new_fd, + new_path, + ); + }, + path_symlink( + old_path_ptr: number, + old_path_len: number, + fd: number, + new_path_ptr: number, + new_path_len: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const old_path = buffer8.slice( + old_path_ptr, + old_path_ptr + old_path_len, + ); + const new_path = buffer8.slice( + new_path_ptr, + new_path_ptr + new_path_len, + ); + return wasi_farm_ref.path_symlink(old_path, mapped_fd, new_path); + }, + path_unlink_file(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_unlink_file(mapped_fd, path); + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + poll_oneoff(in_, out, nsubscriptions) { + self.check_fds(); + throw "async io not supported"; + }, + proc_exit(exit_code: number) { + self.check_fds(); + throw new WASIProcExit(exit_code); + }, + proc_raise(sig: number) { + self.check_fds(); + throw `raised signal ${sig}`; + }, + sched_yield() { + self.check_fds(); + }, + random_get(buf: number, buf_len: number) { + self.check_fds(); + const buffer8 = new Uint8Array( + self.inst.exports.memory.buffer, + ).subarray(buf, buf + buf_len); + + if ( + "crypto" in globalThis && + !(self.inst.exports.memory.buffer instanceof SharedArrayBuffer) + ) { + for (let i = 0; i < buf_len; i += 65536) { + crypto.getRandomValues(buffer8.subarray(i, i + 65536)); + } + } else { + for (let i = 0; i < buf_len; i++) { + buffer8[i] = (Math.random() * 256) | 0; + } + } + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_recv(fd: number, ri_data, ri_flags) { + self.check_fds(); + throw "sockets not supported"; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_send(fd: number, si_data, si_flags) { + self.check_fds(); + throw "sockets not supported"; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_shutdown(fd: number, how) { + self.check_fds(); + throw "sockets not supported"; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_accept(fd: number, flags) { + self.check_fds(); + throw "sockets not supported"; + }, + }; + + this.wasiThreadImport = { + "thread-spawn": (start_arg: number) => { + self.check_fds(); + if (!self.can_thread_spawn) { + throw new Error("thread_spawn is not allowed"); + } + + const thread_id = self.thread_spawner.thread_spawn( + start_arg, + self.args, + self.env, + self.fd_map, + self.extend_imports, + ); + + return thread_id; + }, + }; + + if (this.extend_imports) { + this.extendImport = { + fetch_open: ( + // i32 + url_ptr: number, + // i32 + url_len: number, + // i32 + method_ptr: number, + // i32 + method_len: number, + // i32 + serialized_headers_ptr: number, + // i32 + serialized_headers_len: number, + // i32 + body_ptr: number, + // i32 + body_len: number, + // fd + ): number => { + console.log("fetch_open", url_ptr, url_len, method_ptr, method_len); + + const buffer8 = new Uint8Array(self.inst.exports.memory.buffer); + + const url_buf = buffer8.slice(url_ptr, url_ptr + url_len); + const method_buf = buffer8.slice(method_ptr, method_ptr + method_len); + const serialized_headers_buf = buffer8.slice( + serialized_headers_ptr, + serialized_headers_ptr + serialized_headers_len, + ); + const body_buf = buffer8.slice(body_ptr, body_ptr + body_len); + + const url = new TextDecoder().decode(url_buf); + const method = new TextDecoder().decode(method_buf); + const serialized_headers_txt = new TextDecoder().decode( + serialized_headers_buf, + ); + const body = new TextDecoder().decode(body_buf); + console.log("fetch_open", url, method, serialized_headers_txt, body); + const serialized_headers = JSON.parse(serialized_headers_txt); + console.log("fetch_open", serialized_headers); + + const request = new XMLHttpRequest(); + // `false` makes the request synchronous + request.open(method, url, false); + request.timeout = 10000; + + for (const [key, value] of Object.entries(serialized_headers)) { + request.setRequestHeader(key as string, value as string); + } + + request.send(body); + + const status = request.status; + + const response_headers_str = request.getAllResponseHeaders(); + + console.log("fetch_open", status, response_headers_str); + + // Vec<(String, String)> + const response_headers: Array<[string, string]> = []; + + for (const line of response_headers_str.split("\r\n")) { + if (line === "") { + continue; + } + const [key, value] = line.split(": "); + response_headers.push([key, value]); + } + + const response_body = request.response; + + let response_body_u8: Uint8Array; + + console.log( + "fetch_open", + status, + response_headers, + response_body, + request.responseType, + ); + + switch (request.responseType) { + case "arraybuffer": + response_body_u8 = new Uint8Array(response_body); + break; + case "text": + response_body_u8 = new TextEncoder().encode( + response_body as string, + ); + break; + default: + throw new Error( + `unsupported response type: ${request.responseType}`, + ); + } + + const response_headers_u8 = new TextEncoder().encode( + JSON.stringify(response_headers), + ); + + // first byte is status le_bytes i32 + // second byte is headers byte length i32 + const ret_bytes: ArrayBuffer = new ArrayBuffer( + 2 + response_headers_u8.length + response_body_u8.length, + ); + + const ret_bytes32 = new DataView(ret_bytes); + ret_bytes32.setInt32(0, status, true); + ret_bytes32.setInt32(4, response_headers_u8.length, true); + + const ret_bytes8 = new Uint8Array(ret_bytes); + ret_bytes8.set(response_headers_u8, 8); + ret_bytes8.set(response_body_u8, 8 + response_headers_u8.length); + + // open fd on first wasi_ref + // lock input fd and call create_fd on it + self.check_fds(); + const [mapped_fd, wasi_farm_ref_n] = self.get_fd_and_wasi_ref_n(0); + if (mapped_fd === undefined || wasi_farm_ref_n === undefined) { + throw new Error("fetch_open: bad fd"); + } + const wasi_farm_ref = self.wasi_farm_refs[wasi_farm_ref_n]; + const [opened_fd, ret] = wasi_farm_ref.open_fd_with_buff( + mapped_fd, + ret_bytes8, + ); + if (ret !== wasi.ERRNO_SUCCESS) { + throw new Error("fetch_open: failed to open fd"); + } + if (opened_fd) { + if (self.fd_map.includes([opened_fd, wasi_farm_ref_n])) { + throw new Error("opened_fd already exists"); + } + const mapped_opened_fd = self.map_new_fd_and_notify( + opened_fd, + wasi_farm_ref_n, + ); + return mapped_opened_fd; + } + throw new Error("fetch_open: failed to open fd"); + }, + }; + } + } +} diff --git a/src/wasi_farm/farm.ts b/src/wasi_farm/farm.ts new file mode 100644 index 0000000..7966339 --- /dev/null +++ b/src/wasi_farm/farm.ts @@ -0,0 +1,111 @@ +import { debug } from "../debug.js"; +import type { Fd } from "../fd.js"; +import type { Options } from "../wasi.js"; +import type { WASIFarmPark } from "./park.js"; +import type { WASIFarmRefObject } from "./ref.js"; +import { WASIFarmParkUseArrayBuffer } from "./shared_array_buffer/index.js"; + +export class WASIFarm { + private fds: Array; + private park: WASIFarmPark; + + private can_array_buffer; + + constructor( + stdin?: Fd, + stdout?: Fd, + stderr?: Fd, + fds: Array = [], + options: Options & { + allocator_size?: number; + } = {}, + ) { + debug.enable(options.debug); + + const new_fds = []; + let stdin_ = undefined; + let stdout_ = undefined; + let stderr_ = undefined; + if (stdin) { + new_fds.push(stdin); + stdin_ = new_fds.length - 1; + } + if (stdout) { + new_fds.push(stdout); + stdout_ = new_fds.length - 1; + } + if (stderr) { + new_fds.push(stderr); + stderr_ = new_fds.length - 1; + } + new_fds.push(...fds); + + const default_allow_fds = []; + for (let i = 0; i < new_fds.length; i++) { + default_allow_fds.push(i); + } + + this.fds = new_fds; + + // WebAssembly.Memory can be used to create a SharedArrayBuffer, but it cannot be transferred by postMessage. + // Uncaught (in promise) DataCloneError: + // Failed to execute 'postMessage' on 'Worker': + // SharedArrayBuffer transfer requires self.crossOriginIsolated. + try { + new SharedArrayBuffer(4); + this.can_array_buffer = true; + } catch (e) { + this.can_array_buffer = false; + console.warn("SharedArrayBuffer is not supported:", e); + + if (!crossOriginIsolated) { + console.warn( + "SharedArrayBuffer is not supported because crossOriginIsolated is not enabled.", + ); + } + } + + if (this.can_array_buffer) { + this.park = new WASIFarmParkUseArrayBuffer( + this.fds_ref(), + stdin_, + stdout_, + stderr_, + default_allow_fds, + options?.allocator_size, + ); + } else { + throw new Error("Non SharedArrayBuffer is not supported yet"); + } + + this.park.listen(); + } + + private fds_ref(): Array { + const fds = new Proxy([] as Array, { + get: (_, prop) => { + // console.log("fds", prop); + + if (prop === "push") { + return (fd: Fd) => { + const len = this.fds.push(fd); + return len; + }; + } + return this.fds[prop]; + }, + + set: (_, prop, value) => { + // console.log("fds", prop, value); + this.fds[prop] = value; + return true; + }, + }); + + return fds; + } + + get_ref(): WASIFarmRefObject { + return this.park.get_ref(); + } +} diff --git a/src/wasi_farm/index.ts b/src/wasi_farm/index.ts new file mode 100644 index 0000000..76b6389 --- /dev/null +++ b/src/wasi_farm/index.ts @@ -0,0 +1,5 @@ +import { WASIFarmAnimal } from "./animals.js"; +import { WASIFarm } from "./farm.js"; +import { WASIFarmRef } from "./ref.js"; +import { thread_spawn_on_worker } from "./shared_array_buffer/index.js"; +export { WASIFarm, WASIFarmRef, WASIFarmAnimal, thread_spawn_on_worker }; diff --git a/src/wasi_farm/park.ts b/src/wasi_farm/park.ts new file mode 100644 index 0000000..31e760a --- /dev/null +++ b/src/wasi_farm/park.ts @@ -0,0 +1,619 @@ +import { debug } from "../debug.js"; +import type { Fd } from "../fd.js"; +import { File, OpenFile } from "../fs_mem.js"; +import * as wasi from "../wasi_defs.js"; +import type { WASIFarmRefObject } from "./ref.js"; + +export abstract class WASIFarmPark { + abstract get_ref(): WASIFarmRefObject; + abstract listen(): void; + abstract notify_set_fd(fd: number): void; + abstract notify_rm_fd(fd: number): void; + abstract can_set_new_fd(fd: number): [boolean, Promise | undefined]; + + protected fds: Array; + protected stdin: number | undefined; + protected stdout: number | undefined; + protected stderr: number | undefined; + protected default_allow_fds: Array; + + constructor( + fds: Array, + stdin: number | undefined, + stdout: number | undefined, + stderr: number | undefined, + default_allow_fds: Array, + ) { + this.fds = fds; + this.stdin = stdin; + this.stdout = stdout; + this.stderr = stderr; + this.default_allow_fds = default_allow_fds; + this.fds_map = new Array(fds.length); + for (let i = 0; i < fds.length; i++) { + this.fds_map[i] = []; + } + // console.log("first fds_map", this.fds_map); + } + + private get_new_fd_lock = new Array<() => Promise>(); + + // fdに対して、現在そのfdにidがアクセス可能かを示す。 + protected fds_map: Array; + + // If the reassigned value is accessed after being closed, + // it will be strange, + // but the programmer should have written it + // so that this does not happen in the first place. + private async get_new_fd(): Promise<[() => Promise, number]> { + const promise = new Promise<[() => Promise, number]>((resolve) => { + const len = this.get_new_fd_lock.push(async () => { + let ret = -1; + for (let i = 0; i < this.fds.length; i++) { + if (this.fds[i] === undefined) { + ret = i; + break; + } + } + if (ret === -1) { + ret = this.fds.length; + // console.log("push_fd", this.fds.length) + this.fds.push(undefined); + this.fds_map.push([]); + // console.log("push_fd", this.fds.length) + } + + const [can, promise] = this.can_set_new_fd(ret); + if (!can) { + await promise; + } + + // If it's assigned, it's resolved. + resolve([ + async () => { + this.get_new_fd_lock.shift(); + const fn = this.get_new_fd_lock[0]; + if (fn !== undefined) { + fn(); + } + // assigned and notify + await this.notify_set_fd(ret); + }, + ret, + ]); + }); + if (len === 1) { + this.get_new_fd_lock[0](); + } + }); + return promise; + } + + protected fd_advise(fd: number): number { + if (this.fds[fd] !== undefined) { + return wasi.ERRNO_SUCCESS; + } + return wasi.ERRNO_BADF; + } + + protected fd_allocate(fd: number, offset: bigint, len: bigint): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_allocate(offset, len); + } + return wasi.ERRNO_BADF; + } + + protected async fd_close(fd: number): Promise { + if (this.fds[fd] !== undefined) { + const ret = this.fds[fd].fd_close(); + this.fds[fd] = undefined; + // console.log("fd_close1", fd); + await this.notify_rm_fd(fd); + // console.log("fd_close2", fd); + return ret; + } + return wasi.ERRNO_BADF; + } + + protected fd_datasync(fd: number): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_sync(); + } + return wasi.ERRNO_BADF; + } + + protected fd_fdstat_get(fd: number): [wasi.Fdstat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, fdstat } = this.fds[fd].fd_fdstat_get(); + if (fdstat != null) { + return [fdstat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_fdstat_set_flags(fd: number, flags: number): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_fdstat_set_flags(flags); + } + return wasi.ERRNO_BADF; + } + + protected fd_fdstat_set_rights( + fd: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_fdstat_set_rights( + fs_rights_base, + fs_rights_inheriting, + ); + } + return wasi.ERRNO_BADF; + } + + protected fd_filestat_get(fd: number): [wasi.Filestat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, filestat } = this.fds[fd].fd_filestat_get(); + if (filestat != null) { + return [filestat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_filestat_set_size(fd: number, size: bigint): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_filestat_set_size(size); + } + return wasi.ERRNO_BADF; + } + + protected fd_filestat_set_times( + fd: number, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_filestat_set_times(atim, mtim, fst_flags); + } + return wasi.ERRNO_BADF; + } + + protected fd_pread( + fd: number, + iovecs: Array, + offset: bigint, + ): [[number, Uint8Array] | undefined, number] { + if (this.fds[fd] !== undefined) { + let nread = 0; + + let buffer8 = new Uint8Array(0); + for (const iovec of iovecs) { + const { ret, data } = this.fds[fd].fd_pread(iovec.buf_len, offset); + if (ret !== wasi.ERRNO_SUCCESS) { + return [[nread, buffer8], ret]; + } + const new_buffer = new Uint8Array(buffer8.byteLength + data.byteLength); + new_buffer.set(buffer8); + new_buffer.set(data, buffer8.byteLength); + buffer8 = new_buffer; + nread += data.byteLength; + if (data.byteLength !== iovec.buf_len) { + break; + } + } + return [[nread, buffer8], wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_prestat_get(fd: number): [wasi.Prestat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, prestat } = this.fds[fd].fd_prestat_get(); + if (prestat != null) { + return [prestat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_prestat_dir_name( + fd: number, + path_len: number, + ): [Uint8Array | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, prestat } = this.fds[fd].fd_prestat_get(); + if (prestat) { + const prestat_dir_name = prestat.inner.pr_name; + + // console.log("fd_prestat_dir_name: park: inner: ", prestat_dir_name); + // console.log("fd_prestat_dir_name: park: inner: ", new TextDecoder().decode(prestat_dir_name)); + + // console.log("fd_prestat_dir_name: park: path_len: ", path_len); + + if (prestat_dir_name.length <= path_len) { + // console.log("fd_prestat_dir_name: park: A"); + return [prestat_dir_name, ret]; + } + + // console.log("fd_prestat_dir_name: park: B"); + return [prestat_dir_name.slice(0, path_len), wasi.ERRNO_NAMETOOLONG]; + } + // console.log("fd_prestat_dir_name: park: C"); + return [undefined, ret]; + } + // console.log("fd_prestat_dir_name: park: D"); + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_pwrite( + fd: number, + write_data: Uint8Array, + offset: bigint, + ): [number | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, nwritten } = this.fds[fd].fd_pwrite(write_data, offset); + return [nwritten, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_read( + fd: number, + iovecs: Array, + ): [[number, Uint8Array] | undefined, number] { + if (this.fds[fd] !== undefined) { + let nread = 0; + + // console.log("fd_read: park: iovecs: ", iovecs); + + // const sum_len = iovecs.reduce((acc, iovec) => acc + iovec.buf_len, 0); + + // console.warn("fd_read: park: sum_len: ", sum_len); + + let buffer8 = new Uint8Array(0); + for (const iovec of iovecs) { + const { ret, data } = this.fds[fd].fd_read(iovec.buf_len); + // console.log("fd_read: park: data: ", data); + if (ret !== wasi.ERRNO_SUCCESS) { + return [[nread, buffer8], ret]; + } + const new_buffer = new Uint8Array(buffer8.byteLength + data.byteLength); + new_buffer.set(buffer8); + new_buffer.set(data, buffer8.byteLength); + buffer8 = new_buffer; + nread += data.byteLength; + if (data.byteLength !== iovec.buf_len) { + break; + } + } + + // console.log("fd_read: park: nread: ", nread); + + return [[nread, buffer8], wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_readdir( + fd: number, + buf_len: number, + cookie: bigint, + ): [[Uint8Array, number] | undefined, number] { + if (this.fds[fd] !== undefined) { + const array = new Uint8Array(buf_len); + + let buf_used = 0; + let offset = 0; + + // eslint-disable-next-line no-constant-condition + while (true) { + const { ret, dirent } = this.fds[fd].fd_readdir_single(cookie); + if (ret !== wasi.ERRNO_SUCCESS) { + return [[array, buf_used], ret]; + } + if (dirent == null) { + break; + } + if (buf_len - buf_used < dirent.head_length()) { + buf_used = buf_len; + break; + } + + const head_bytes = new ArrayBuffer(dirent.head_length()); + dirent.write_head_bytes(new DataView(head_bytes), 0); + array.set( + new Uint8Array(head_bytes).slice( + 0, + Math.min(head_bytes.byteLength, buf_len - buf_used), + ), + offset, + ); + offset += head_bytes.byteLength; + buf_used += head_bytes.byteLength; + + if (buf_len - buf_used < dirent.name_length()) { + buf_used = buf_len; + break; + } + + dirent.write_name_bytes(array, offset, buf_len - buf_used); + offset += dirent.name_length(); + buf_used += dirent.name_length(); + + cookie = dirent.d_next; + } + + return [[array, buf_used], wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + // protected async fd_renumber(fd: number, to: number): Promise { + // if (this.fds[fd] != undefined) { + // const ret = this.fds[to].fd_close(); + // if (ret != wasi.ERRNO_SUCCESS) { + // return ret; + // } + // this.fds[to] = this.fds[fd]; + // this.fds[fd] = undefined; + // await this.notify_rm_fd(fd); + // return wasi.ERRNO_SUCCESS; + // } else { + // return wasi.ERRNO_BADF; + // } + // } + + protected fd_seek( + fd: number, + offset: bigint, + whence: number, + ): [bigint | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, offset: new_offset } = this.fds[fd].fd_seek(offset, whence); + return [new_offset, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_sync(fd: number): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_sync(); + } + return wasi.ERRNO_BADF; + } + + protected fd_tell(fd: number): [bigint | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, offset } = this.fds[fd].fd_tell(); + return [offset, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected async fd_write( + fd: number, + write_data: Uint8Array, + ): Promise<[number | undefined, number]> { + if (this.fds[fd] !== undefined) { + const fd_ret = this.fds[fd].fd_write(write_data); + let ret: number; + let nwritten: number; + if (fd_ret instanceof Promise) { + ({ ret, nwritten } = await fd_ret); + } else { + ({ ret, nwritten } = fd_ret); + } + return [nwritten, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_create_directory(fd: number, path: string): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_create_directory(path); + } + return wasi.ERRNO_BADF; + } + + protected path_filestat_get( + fd: number, + flags: number, + path: string, + ): [wasi.Filestat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, filestat } = this.fds[fd].path_filestat_get(flags, path); + if (filestat != null) { + return [filestat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_filestat_set_times( + fd: number, + flags: number, + path: string, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_filestat_set_times( + flags, + path, + atim, + mtim, + fst_flags, + ); + } + return wasi.ERRNO_BADF; + } + + protected path_link( + old_fd: number, + old_flags: number, + old_path: string, + new_fd: number, + new_path: string, + ): number { + if (this.fds[old_fd] !== undefined && this.fds[new_fd] !== undefined) { + const { ret, inode_obj } = this.fds[old_fd].path_lookup( + old_path, + old_flags, + ); + if (inode_obj == null) { + return ret; + } + return this.fds[new_fd].path_link(new_path, inode_obj, false); + } + return wasi.ERRNO_BADF; + } + + protected async path_open( + fd: number, + dirflags: number, + path: string, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + ): Promise<[number | undefined, number]> { + if (this.fds[fd] !== undefined) { + debug.log("path_open", path); + const { ret, fd_obj } = this.fds[fd].path_open( + dirflags, + path, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ); + console.log("path_open: park: ", path, "fd_obj", fd_obj, "ret", ret); + + // print self dir path + console.log("path_open: self: ", this.fds[fd]); + + // console.log("path_open: park: ", ret, fd_obj); + if (ret !== wasi.ERRNO_SUCCESS) { + return [undefined, ret]; + } + + const [resolve, opened_fd] = await this.get_new_fd(); + + // console.log("path_open: park: ", path, "opened_fd" ,opened_fd); + + this.fds[opened_fd] = fd_obj; + + console.log("path_open: park: ", path, "opened_fd", opened_fd); + + await resolve(); + + // console.log("path_open: park: len: ", len); + + // console.log("path_open: park: ", opened_fd); + + return [opened_fd, wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_readlink( + fd: number, + path: string, + buf_len: number, + ): [Uint8Array | undefined, number] { + if (this.fds[fd] !== undefined) { + debug.log("path_readlink", path); + const { ret, data } = this.fds[fd].path_readlink(path); + if (data != null) { + const data_buf = new TextEncoder().encode(data); + if (data_buf.byteLength > buf_len) { + // wasi.ts use ERRNO_BADF. I think it should be ERRNO_OVERFLOW. + return [data_buf.slice(0, buf_len), wasi.ERRNO_OVERFLOW]; + } + return [data_buf, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_remove_directory(fd: number, path: string): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_remove_directory(path); + } + return wasi.ERRNO_BADF; + } + + protected path_rename( + old_fd: number, + old_path: string, + new_fd: number, + new_path: string, + ): number { + if (this.fds[old_fd] !== undefined && this.fds[new_fd] !== undefined) { + // eslint-disable-next-line prefer-const + let { ret, inode_obj } = this.fds[old_fd].path_unlink(old_path); + if (inode_obj == null) { + return ret; + } + ret = this.fds[new_fd].path_link(new_path, inode_obj, true); + if (ret !== wasi.ERRNO_SUCCESS) { + if ( + this.fds[old_fd].path_link(old_path, inode_obj, true) !== + wasi.ERRNO_SUCCESS + ) { + throw "path_link should always return success when relinking an inode back to the original place"; + } + } + return ret; + } + return wasi.ERRNO_BADF; + } + + protected path_symlink( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + old_path: string, + fd: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + new_path: string, + ): number { + if (this.fds[fd] !== undefined) { + return wasi.ERRNO_NOTSUP; + } + return wasi.ERRNO_BADF; + } + + protected path_unlink_file(fd: number, path: string): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_unlink_file(path); + } + return wasi.ERRNO_BADF; + } + + protected async open_fd_with_buff( + fd: number, + buf: Uint8Array, + ): Promise<[number | undefined, number]> { + if (this.fds[fd] !== undefined) { + const fd_obj = new OpenFile(new File(buf)); + + const [resolve, opened_fd] = await this.get_new_fd(); + + this.fds[opened_fd] = fd_obj; + + await resolve(); + + return [opened_fd, wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } +} diff --git a/src/wasi_farm/polyfill.js b/src/wasi_farm/polyfill.js new file mode 100644 index 0000000..bde01dd --- /dev/null +++ b/src/wasi_farm/polyfill.js @@ -0,0 +1,147 @@ +// https://github.com/tc39/proposal-atomics-wait-async/blob/master/polyfill.js + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: Lars T Hansen, lhansen@mozilla.com + */ + +/* Polyfill for Atomics.waitAsync() for web browsers. + * + * Any kind of agent that is able to create a new Worker can use this polyfill. + * + * Load this file in all agents that will use Atomics.waitAsync. + * + * Agents that don't call Atomics.waitAsync need do nothing special. + * + * Any kind of agent can wake another agent that is sleeping in + * Atomics.waitAsync by just calling Atomics.notify for the location being slept + * on, as normal. + * + * The implementation is not completely faithful to the proposed semantics: in + * the case where an agent first asyncWaits and then waits on the same location: + * when it is woken, the two waits will be woken in order, while in the real + * semantics, the sync wait will be woken first. + * + * In this polyfill Atomics.waitAsync is not very fast. + */ + +/* Implementation: + * + * For every wait we fork off a Worker to perform the wait. Workers are reused + * when possible. The worker communicates with its parent using postMessage. + */ + +(() => { + if (typeof Atomics.waitAsync === "function") return; + + const helperCode = ` + onmessage = function (ev) { + try { + switch (ev.data[0]) { + case 'wait': { + let [_, ia, index, value, timeout] = ev.data; + let result = Atomics.wait(ia, index, value, timeout) + postMessage(['ok', result]); + break; + } + default: { + throw new Error("Bogus message sent to wait helper: " + ev.data.join(',')); + } + } + } catch (e) { + console.log("Exception in wait helper"); + postMessage(['error', 'Exception']); + } + } + `; + + const helpers = []; + + function allocHelper() { + if (helpers.length > 0) return helpers.pop(); + const h = new Worker( + `data:application/javascript,${encodeURIComponent(helperCode)}`, + ); + return h; + } + + function freeHelper(h) { + helpers.push(h); + } + + // Atomics.waitAsync always returns a promise. Throws standard errors + // for parameter validation. The promise is resolved with a string as from + // Atomics.wait, or, in the case something went completely wrong, it is + // rejected with an error string. + + function waitAsync(ia, index_, value_, timeout_) { + if ( + typeof ia !== "object" || + !(ia instanceof Int32Array) || + !(ia.buffer instanceof SharedArrayBuffer) + ) + throw new TypeError("Expected shared memory"); + + // These conversions only approximate the desired semantics but are + // close enough for the polyfill. + + const index = index_ | 0; + const value = value_ | 0; + const timeout = + timeout_ === undefined ? Number.POSITIVE_INFINITY : +timeout_; + + // Range checking for the index. + + ia[index]; + + // Optimization, avoid the helper thread in this common case. + + if (Atomics.load(ia, index) !== value) return Promise.resolve("not-equal"); + + // General case, we must wait. + + return new Promise((resolve, reject) => { + const h = allocHelper(); + h.onmessage = (ev) => { + // Free the helper early so that it can be reused if the resolution + // needs a helper. + freeHelper(h); + switch (ev.data[0]) { + case "ok": + resolve(ev.data[1]); + break; + case "error": + // Note, rejection is not in the spec, it is an artifact of the polyfill. + // The helper already printed an error to the console. + reject(ev.data[1]); + break; + } + }; + + // It's possible to do better here if the ia is already known to the + // helper. In that case we can communicate the other data through + // shared memory and wake the agent. And it is possible to make ia + // known to the helper by waking it with a special value so that it + // checks its messages, and then posting the ia to the helper. Some + // caching / decay scheme is useful no doubt, to improve performance + // and avoid leaks. + // + // In the event we wake the helper directly, we can micro-wait here + // for a quick result. We'll need to restructure some code to make + // that work out properly, and some synchronization is necessary for + // the helper to know that we've picked up the result and no + // postMessage is necessary. + + h.postMessage(["wait", ia, index, value, timeout]); + }); + } + + Object.defineProperty(Atomics, "waitAsync", { + value: waitAsync, + configurable: true, + enumerable: false, + writable: true, + }); +})(); diff --git a/src/wasi_farm/ref.ts b/src/wasi_farm/ref.ts new file mode 100644 index 0000000..4f0f773 --- /dev/null +++ b/src/wasi_farm/ref.ts @@ -0,0 +1,190 @@ +import type * as wasi from "../wasi_defs.js"; +import type { FdCloseSender } from "./sender.js"; + +export type WASIFarmRefObject = { + stdin: number | undefined; + stdout: number | undefined; + stderr: number | undefined; + fd_close_receiver: FdCloseSender; + default_fds: Array; +}; + +export abstract class WASIFarmRef { + abstract get_fds_len(): number; + // please implement this method + // abstract init_self(sl: WASIFarmRef): WASIFarmRef; + + protected stdin: number | undefined; + protected stdout: number | undefined; + protected stderr: number | undefined; + + protected id: number; + + fd_close_receiver: FdCloseSender; + + default_fds: Array = []; + + async send(targets: Array, fd: number): Promise { + await this.fd_close_receiver.send(targets, fd); + } + + get(id: number): Array | undefined { + return this.fd_close_receiver.get(id); + } + + abstract set_park_fds_map(fds: Array); + + abstract set_id(): number; + + constructor( + stdin: number | undefined, + stdout: number | undefined, + stderr: number | undefined, + fd_close_receiver: FdCloseSender, + default_fds?: Array, + ) { + this.stdin = stdin; + this.stdout = stdout; + this.stderr = stderr; + this.fd_close_receiver = fd_close_receiver; + if (default_fds !== undefined) { + this.default_fds = default_fds; + } + } + + get_stdin(): number | undefined { + return this.stdin; + } + + get_stdout(): number | undefined { + return this.stdout; + } + + get_stderr(): number | undefined { + return this.stderr; + } + + abstract fd_advise(fd: number | undefined): number; + abstract fd_allocate( + fd: number | undefined, + offset: bigint, + len: bigint, + ): number; + abstract fd_close(fd: number | undefined): number; + abstract fd_datasync(fd: number | undefined): number; + abstract fd_fdstat_get( + fd: number | undefined, + ): [wasi.Fdstat | undefined, number]; + abstract fd_fdstat_set_flags(fd: number | undefined, flags: number): number; + abstract fd_fdstat_set_rights( + fd: number | undefined, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ): number; + abstract fd_filestat_get( + fd: number | undefined, + ): [wasi.Filestat | undefined, number]; + abstract fd_filestat_set_size(fd: number | undefined, size: bigint): number; + abstract fd_filestat_set_times( + fd: number | undefined, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number; + abstract fd_pread( + fd: number | undefined, + iovs: Uint32Array, + offset: bigint, + ): [[number, Uint8Array] | undefined, number]; + abstract fd_prestat_get( + fd: number | undefined, + ): [[number, number] | undefined, number]; + abstract fd_prestat_dir_name( + fd: number | undefined, + path_len: number, + ): [Uint8Array | undefined, number]; + abstract fd_pwrite( + fd: number | undefined, + iovs: Uint8Array, + offset: bigint, + ): [number | undefined, number]; + abstract fd_read( + fd: number | undefined, + iovs: Uint32Array, + ): [[number, Uint8Array] | undefined, number]; + abstract fd_readdir( + fd: number | undefined, + limit_buf_len: number, + cookie: bigint, + ): [[Uint8Array, number] | undefined, number]; + // abstract fd_renumber(fd: number | undefined, to: number): number; + abstract fd_seek( + fd: number | undefined, + offset: bigint, + whence: number, + ): [bigint | undefined, number]; + abstract fd_sync(fd: number | undefined): number; + abstract fd_tell(fd: number | undefined): [bigint, number]; + abstract fd_write( + fd: number | undefined, + iovs: Uint8Array, + ): [number | undefined, number]; + abstract path_create_directory( + fd: number | undefined, + path: Uint8Array, + ): number; + abstract path_filestat_get( + fd: number | undefined, + flags: number, + path: Uint8Array, + ): [wasi.Filestat | undefined, number]; + abstract path_filestat_set_times( + fd: number | undefined, + flags: number, + path: Uint8Array, + st_atim: bigint, + st_mtim: bigint, + fst_flags: number, + ): number; + abstract path_link( + old_fd: number | undefined, + old_flags: number, + old_path: Uint8Array, + new_fd: number | undefined, + new_path: Uint8Array, + ): number; + abstract path_open( + fd: number | undefined, + dirflags: number, + path: Uint8Array, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + ): [number, number]; + abstract path_readlink( + fd: number | undefined, + path: Uint8Array, + buf_len: number, + ): [Uint8Array | undefined, number]; + abstract path_remove_directory( + fd: number | undefined, + path: Uint8Array, + ): number; + abstract path_rename( + old_fd: number | undefined, + old_path: Uint8Array, + new_fd: number | undefined, + new_path: Uint8Array, + ): number; + abstract path_symlink( + old_path: Uint8Array, + fd: number | undefined, + new_path: Uint8Array, + ): number; + abstract path_unlink_file(fd: number | undefined, path: Uint8Array): number; + abstract open_fd_with_buff( + fd: number | undefined, + buf: Uint8Array, + ): [number, number]; +} diff --git a/src/wasi_farm/sender.ts b/src/wasi_farm/sender.ts new file mode 100644 index 0000000..ed9d30a --- /dev/null +++ b/src/wasi_farm/sender.ts @@ -0,0 +1,4 @@ +export interface FdCloseSender { + send(targets: Array, fd: number): Promise; + get(id: number): Array | undefined; +} diff --git a/src/wasi_farm/shared_array_buffer/allocator.ts b/src/wasi_farm/shared_array_buffer/allocator.ts new file mode 100644 index 0000000..8cd3edf --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/allocator.ts @@ -0,0 +1,215 @@ +// @ts-ignore +// import { debug } from "../../debug.js"; +// import "../polyfill.js"; + +export type AllocatorUseArrayBufferObject = { + share_arrays_memory: SharedArrayBuffer; +}; + +export class AllocatorUseArrayBuffer { + // Pass a !Sized type + // The first 4 bytes are for a lock value: i32 + // The next 4 bytes are for the current number of arrays: m: i32 + // The next 4 bytes are for the length of the occupied space in share_arrays_memory: n: i32 + // Once it is no longer busy, it should become empty immediately, so reset only when it is empty. + // Even if it becomes too long, it should be fine due to the browser's virtualization. + // Using an algorithm even simpler than First-Fit + // SharedArrayBuffer.grow is supported by all major browsers except Android WebView, + // which does not support SharedArrayBuffer in the first place, + // but es2024 and the type system does not support it, + // so the size is fixed from the beginning + + // share_arrays_memory: SharedArrayBuffer = new SharedArrayBuffer(12, { + // // 10MB + // maxByteLength: 10 * 1024 * 1024, + // }); + + // Even if 100MB is allocated, due to browser virtualization, + // the memory should not actually be used until it is needed. + share_arrays_memory: SharedArrayBuffer; + + // When adding data, use Atomics.wait to wait until the first 4 bytes become 0. + // After that, use Atomics.compareExchange to set the first 4 bytes to 1. + // Then, use Atomics.add to increment the next 4 bytes by 1. + // If the return value is 0, proceed to *1. + // If the return value is 1, use Atomics.wait to wait until the first 4 bytes become 0. + // *1: Increment the second by 1 using Atomics.add. If the return value is 0, reset it. + // Add the data. Extend if there is not enough space. + // To release, just decrement by 1 using Atomics.sub. + + // Since postMessage makes the class an object, + // it must be able to receive and assign a SharedArrayBuffer. + constructor( + share_arrays_memory: SharedArrayBuffer = new SharedArrayBuffer( + 10 * 1024 * 1024, + ), + ) { + this.share_arrays_memory = share_arrays_memory; + const view = new Int32Array(this.share_arrays_memory); + Atomics.store(view, 0, 0); + Atomics.store(view, 1, 0); + Atomics.store(view, 2, 12); + } + + // Since postMessage converts classes to objects, + // it must be able to convert objects to classes. + static init_self(sl: AllocatorUseArrayBufferObject): AllocatorUseArrayBuffer { + return new AllocatorUseArrayBuffer(sl.share_arrays_memory); + } + + // Writes without blocking threads when acquiring locks + async async_write( + data: Uint8Array | Uint32Array, + memory: SharedArrayBuffer, + // ptr, len + // Pass I32Array ret_ptr + ret_ptr: number, + ): Promise<[number, number]> { + const view = new Int32Array(this.share_arrays_memory); + // eslint-disable-next-line no-constant-condition + while (true) { + let lock: "not-equal" | "timed-out" | "ok"; + const { value } = Atomics.waitAsync(view, 0, 1); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + + const ret = this.write_inner(data, memory, ret_ptr); + + // release lock + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + + return ret; + } + } + + // Blocking threads for writing when acquiring locks + block_write( + data: Uint8Array | Uint32Array, + memory: SharedArrayBuffer, + // ptr, len + ret_ptr: number, + ): [number, number] { + // eslint-disable-next-line no-constant-condition + while (true) { + const view = new Int32Array(this.share_arrays_memory); + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + + const ret = this.write_inner(data, memory, ret_ptr); + + // release lock + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + + return ret; + } + } + + // Function to write after acquiring a lock + write_inner( + data: Uint8Array | Uint32Array, + memory: SharedArrayBuffer, + // ptr, len + ret_ptr: number, + ): [number, number] { + // console.log("data", data); + + const view = new Int32Array(this.share_arrays_memory); + const view8 = new Uint8Array(this.share_arrays_memory); + + // Indicates more users using memory + const old_num = Atomics.add(view, 1, 1); + let share_arrays_memory_kept: number; + if (old_num === 0) { + // Reset because there were no users. + // debug.log("reset allocator"); + share_arrays_memory_kept = Atomics.store(view, 2, 12); + } else { + share_arrays_memory_kept = Atomics.load(view, 2); + } + // console.log("num", Atomics.load(view, 1)); + + const memory_len = this.share_arrays_memory.byteLength; + const len = data.byteLength; + const new_memory_len = share_arrays_memory_kept + len; + if (memory_len < new_memory_len) { + // extend memory + // support from es2024 + // this.share_arrays_memory.grow(new_memory_len); + throw new Error( + "size is bigger than memory. \nTODO! fix memory limit. support big size another way.", + ); + } + + let data8: Uint8Array; + if (data instanceof Uint8Array) { + data8 = data; + } else if (data instanceof Uint32Array) { + // data to uint8 + const tmp = new ArrayBuffer(data.byteLength); + new Uint32Array(tmp).set(data); + data8 = new Uint8Array(tmp); + } + + view8.set(new Uint8Array(data8), share_arrays_memory_kept); + Atomics.store(view, 2, new_memory_len); + + const memory_view = new Int32Array(memory); + Atomics.store(memory_view, ret_ptr, share_arrays_memory_kept); + Atomics.store(memory_view, ret_ptr + 1, len); + + // console.log("allocator: allocate", share_arrays_memory_kept, len); + + return [share_arrays_memory_kept, len]; + } + + // free allocated memory + free( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + pointer: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + len: number, + ) { + Atomics.sub(new Int32Array(this.share_arrays_memory), 1, 1); + + // console.log("allocator: free", pointer, len); + } + + // get memory from pointer and length + get_memory(ptr: number, len: number): ArrayBuffer { + const data = new ArrayBuffer(len); + const view = new Uint8Array(data); + view.set(new Uint8Array(this.share_arrays_memory).slice(ptr, ptr + len)); + return data; + } + + // Write again to the memory before releasing + // Not used because the situation for using it does not exist. + use_defined_memory(ptr: number, len: number, data: ArrayBufferLike) { + const memory = new Uint8Array(this.share_arrays_memory); + memory.set(new Uint8Array(data).slice(0, len), ptr); + } + + get_object(): AllocatorUseArrayBufferObject { + return { + share_arrays_memory: this.share_arrays_memory, + }; + } +} diff --git a/src/wasi_farm/shared_array_buffer/fd_close_sender.ts b/src/wasi_farm/shared_array_buffer/fd_close_sender.ts new file mode 100644 index 0000000..a137e77 --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/fd_close_sender.ts @@ -0,0 +1,62 @@ +import type { FdCloseSender } from "../sender.js"; +import { + ToRefSenderUseArrayBuffer, + type ToRefSenderUseArrayBufferObject, +} from "./sender.js"; + +export type FdCloseSenderUseArrayBufferObject = { + max_share_arrays_memory?: number; + share_arrays_memory?: SharedArrayBuffer; +} & ToRefSenderUseArrayBufferObject; + +// Object to tell other processes, +// such as child processes, +// that the file descriptor has been closed +export class FdCloseSenderUseArrayBuffer + extends ToRefSenderUseArrayBuffer + implements FdCloseSender +{ + // Should be able to change the size of memory as it accumulates more and more on memory + constructor( + max_share_arrays_memory?: number, + share_arrays_memory?: SharedArrayBuffer, + ) { + super(4, max_share_arrays_memory, share_arrays_memory); + } + + // Send the closed file descriptor to the target process + async send(targets: Array, fd: number): Promise { + if (targets === undefined || targets.length === 0) { + throw new Error("targets is empty"); + } + // console.log("fd_close_sender send", targets, fd); + + await this.async_send(targets, new Uint32Array([fd])); + } + + // Get the closed file descriptor from the target process + get(id: number): Array | undefined { + const data = this.get_data(id); + if (data === undefined) { + return undefined; + } + + // console.log("fd_close_sender get", data); + + const array = []; + for (const i of data) { + array.push(i[0]); + } + + return array; + } + + // Initialize the class from object + static init_self(sl: FdCloseSenderUseArrayBufferObject): FdCloseSender { + const sel = ToRefSenderUseArrayBuffer.init_self_inner(sl); + return new FdCloseSenderUseArrayBuffer( + sel.max_share_arrays_memory, + sel.share_arrays_memory, + ); + } +} diff --git a/src/wasi_farm/shared_array_buffer/index.ts b/src/wasi_farm/shared_array_buffer/index.ts new file mode 100644 index 0000000..0e2d34a --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/index.ts @@ -0,0 +1,13 @@ +import { WASIFarmParkUseArrayBuffer } from "./park.js"; +import { WASIFarmRefUseArrayBuffer } from "./ref.js"; +import type { WASIFarmRefUseArrayBufferObject } from "./ref.js"; +import { ThreadSpawner } from "./thread_spawn.js"; +import { thread_spawn_on_worker } from "./thread_spawn.js"; + +export { + WASIFarmRefUseArrayBuffer, + type WASIFarmRefUseArrayBufferObject, + WASIFarmParkUseArrayBuffer, + ThreadSpawner, + thread_spawn_on_worker, +}; diff --git a/src/wasi_farm/shared_array_buffer/park.ts b/src/wasi_farm/shared_array_buffer/park.ts new file mode 100644 index 0000000..0096397 --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/park.ts @@ -0,0 +1,1016 @@ +import type { Fd } from "../../fd.js"; +import * as wasi from "../../wasi_defs.js"; +import { WASIFarmPark } from "../park.js"; +import type { FdCloseSender } from "../sender.js"; +import { AllocatorUseArrayBuffer } from "./allocator.js"; +import { FdCloseSenderUseArrayBuffer } from "./fd_close_sender.js"; +import type { WASIFarmRefUseArrayBufferObject } from "./ref.js"; +import { get_func_name_from_number } from "./util.js"; + +export const fd_func_sig_u32_size: number = 18; +export const fd_func_sig_bytes: number = fd_func_sig_u32_size * 4; + +export class WASIFarmParkUseArrayBuffer extends WASIFarmPark { + private allocator: AllocatorUseArrayBuffer; + + // args and env do not change, so copying them is fine. + // Functions that do not depend on fds are skipped. + // Since it is wasm32, the pointer is u32. + // errno is u8. + // https://github.com/WebAssembly/WASI/blob/4feaf733e946c375b610cc5d39ea2e1a68046e62/legacy/preview1/docs.md + // The first item is the function signature, and the second (if it exists) represents data that must be communicated in Park. If they are the same, it is not written. + // From here, direct access to fd begins. + // fd_advise: (fd: u32, offset: u64, len: u64, advice: u8) => errno; + // (fd: u32) => errno; + // fd_allocate: (fd: u32, offset: u64, len: u64) => errno; + // fd_close: (fd: u32) => errno; + // fd_datasync: (fd: u32) => errno; + // fd_fdstat_get: (fd: u32, fdstat_ptr: pointer) => errno; + // (fd: u32) => [wasi.Fdstat(u32 * 6)], errno]; + // fd_fdstat_set_flags: (fd: u32, flags: u16) => errno; + // fd_fdstat_set_rights: (fd: u32, fs_rights_base: u64, fs_rights_inheriting: u64) => errno; + // fd_filestat_get: (fd: u32, filestat_ptr: pointer) => errno; + // (fd: u32) => [wasi.Filestat(u32 * 16)], errno]; + // fd_filestat_set_size: (fd: u32, size: u64) => errno; + // fd_filestat_set_times: (fd: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + // fd_pread: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, data_ptr, errno]; + // fd_prestat_get: (fd: u32, prestat_ptr: pointer) => errno; + // (fd: u32) => [wasi.Prestat(u32 * 2)], errno]; + // fd_prestat_dir_name: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + // (fd: u32, path_len: u32) => [path_ptr: pointer, path_len: u32, errno]; + // fd_pwrite: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, write_data: pointer, write_data_len: u32, offset: u64) => [u32, errno]; + // fd_read: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, data_ptr, errno]; + // fd_readdir: (fd: u32, buf_ptr: pointer, buf_len: u32, cookie: u64) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, buf_len: u32, cookie: u64) => [buf_ptr: pointer, buf_len: u32, buf_used: u32, errno]; + // fd_renumber: (fd: u32, to: u32) => errno; + // fd_seek: (fd: u32, offset: i64, whence: u8) => [u64, errno]; + // fd_sync: (fd: u32) => errno; + // fd_tell: (fd: u32) => [u64, errno]; + // fd_write: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, write_data: pointer, write_data_len: u32) => [u32, errno]; + // path_create_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + // path_filestat_get: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32) => [wasi.Filestat(u32 * 16), errno]; + // path_filestat_set_times: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + // path_link: (old_fd: u32, old_flags: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + // path_open: (fd: u32, dirflags: u32, path_ptr: pointer, path_len: u32, oflags: u32, fs_rights_base: u64, fs_rights_inheriting: u64, fdflags: u16) => [u32, errno]; + // note: fdsにpushするが、既存のfdに影響しないので、競合しない。 + // path_readlink: (fd: u32, path_ptr: pointer, path_len: u32, buf_ptr: pointer, buf_len: u32) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, path_ptr: pointer, path_len: u32, buf_len: u32) => [buf_len: u32, data_ptr: pointer, data_len: u32, errno]; + // path_remove_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + // path_rename: (old_fd: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + // path_symlink: (old_path_ptr: pointer, old_path_len: u32, fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + // path_unlink_file: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + + // Lock when you want to use fd + // Array<[lock, call_func]> + private lock_fds: SharedArrayBuffer; + + // 1 bytes: fds.length + // 1 bytes: wasi_farm_ref num(id) + // Actually, as long as it is working properly, fds.length is not used + private fds_len_and_num: SharedArrayBuffer; + + // listen promise keep + private listen_fds: Array> = []; + + // The largest size is u32 * 18 + 1 + // Alignment is troublesome, so make it u32 * 18 + 4 + // In other words, one size is 76 bytes + private fd_func_sig: SharedArrayBuffer; + + // listen base handle keep + private listen_base_handle: Promise; + + // listen base lock and call etc + private base_func_util: SharedArrayBuffer; + + // tell other processes that the file descriptor has been closed + private fd_close_receiver: FdCloseSender; + + // this is not send by postMessage, + // so it is not necessary to keep shared_array_buffer + // this class is not used by user, + // to avoid mistakes, all constructors are now required to be passed in. + constructor( + fds: Array, + // stdin fd number + stdin: number | undefined, + // stdout fd number + stdout: number | undefined, + // stderr fd number + stderr: number | undefined, + // wasi_farm_ref default allow fds + default_allow_fds: Array, + allocator_size?: number, + ) { + super(fds, stdin, stdout, stderr, default_allow_fds); + + if (allocator_size === undefined) { + this.allocator = new AllocatorUseArrayBuffer(); + } else { + this.allocator = new AllocatorUseArrayBuffer( + new SharedArrayBuffer(allocator_size), + ); + } + const max_fds_len = 128; + this.lock_fds = new SharedArrayBuffer(4 * max_fds_len * 3); + this.fd_func_sig = new SharedArrayBuffer( + fd_func_sig_u32_size * 4 * max_fds_len, + ); + this.fds_len_and_num = new SharedArrayBuffer(8); + + const view = new Int32Array(this.fds_len_and_num); + Atomics.store(view, 0, fds.length); + Atomics.store(view, 1, 0); + + this.fd_close_receiver = new FdCloseSenderUseArrayBuffer(); + this.base_func_util = new SharedArrayBuffer(24); + } + + /// Send this return by postMessage. + get_ref(): WASIFarmRefUseArrayBufferObject { + return { + allocator: this.allocator, + lock_fds: this.lock_fds, + fds_len_and_num: this.fds_len_and_num, + fd_func_sig: this.fd_func_sig, + base_func_util: this.base_func_util, + fd_close_receiver: this.fd_close_receiver, + stdin: this.stdin, + stdout: this.stdout, + stderr: this.stderr, + default_fds: this.default_allow_fds, + }; + } + + // abstract methods implementation + // from fd set ex) path_open + // received and listen the fd + // and set fds.length + async notify_set_fd(fd: number) { + if (this.fds[fd] === undefined) { + throw new Error("fd is not defined"); + } + if (fd >= 128) { + throw new Error("fd is too big. expand is not supported yet"); + } + if (this.listen_fds[fd] !== undefined) { + if (this.listen_fds[fd] instanceof Promise) { + console.warn("fd is already set yet"); + await this.listen_fds[fd]; + } + } + this.listen_fds[fd] = this.listen_fd(fd); + + const view = new Int32Array(this.fds_len_and_num); + Atomics.store(view, 0, this.fds.length); + // const len = Atomics.store(view, 0, this.fds.length); + // console.log("notify_set_fd: len: ", len); + } + + // abstract methods implementation + // called by fd close ex) fd_close + async notify_rm_fd(fd: number) { + (async () => { + await this.listen_fds[fd]; + this.listen_fds[fd] = undefined; + })(); + + // console.log("notify_rm_fd", fd); + // console.log("fds", this.fds); + // console.log("fds_map", this.fds_map); + + // console.log("notify_rm_fd: fds_map", this.fds_map); + // console.log("notify_rm_fd: fd", fd); + + // console.log("notify_rm_fd: fds_map[fd]", [...this.fds_map[fd]]); + + await this.fd_close_receiver.send(this.fds_map[fd], fd); + + this.fds_map[fd] = []; + } + + // abstract methods implementation + // wait to close old listener + can_set_new_fd(fd: number): [boolean, Promise | undefined] { + if (this.listen_fds[fd] instanceof Promise) { + return [false, this.listen_fds[fd]]; + } + return [true, undefined]; + } + + // listen all fds and base + // Must be called before was_ref_id is instantiated + listen() { + this.listen_fds = []; + for (let n = 0; n < this.fds.length; n++) { + this.listen_fds.push(this.listen_fd(n)); + } + this.listen_base_handle = this.listen_base(); + } + + // listen base + // ex) set_fds_map + // if close fd and send to other process, + // it need targets wasi_farm_ref id + // so, set fds_map + async listen_base() { + const lock_view = new Int32Array(this.base_func_util); + Atomics.store(lock_view, 0, 0); + Atomics.store(lock_view, 1, 0); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + let lock: "not-equal" | "timed-out" | "ok"; + + const { value } = Atomics.waitAsync(lock_view, 1, 0); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out"); + } + + const func_number = Atomics.load(lock_view, 2); + + switch (func_number) { + // set_fds_map: (fds_ptr: u32, fds_len: u32); + case 0: { + // console.log("set_fds_map"); + const ptr = Atomics.load(lock_view, 3); + const len = Atomics.load(lock_view, 4); + // console.log("set_fds_map", ptr, len); + const data = new Uint32Array(this.allocator.get_memory(ptr, len)); + this.allocator.free(ptr, len); + const wasi_farm_ref_id = Atomics.load(lock_view, 5); + + // console.log("listen_base set_fds_map", data, "from", wasi_farm_ref_id); + + // console.log("listen_base fds_map", this.fds_map); + + for (let i = 0; i < len / 4; i++) { + const fd = data[i]; + if (this.fds_map[fd] === undefined) { + this.fds_map[fd] = []; + console.error("listen_base fd is not defined"); + } + this.fds_map[fd].push(wasi_farm_ref_id); + // console.log("this.fds_map", this.fds_map); + // console.log("this.fds_map[fd]", this.fds_map[fd]); + // console.log("this.fds_map[1]", this.fds_map[1]); + // console.log("fd", fd, "wasi_farm_ref_id", wasi_farm_ref_id); + } + + // console.log("listen_base fds_map", this.fds_map); + + // sleep 1000ms + await new Promise((resolve) => setTimeout(resolve, 1000)); + + break; + } + } + + const old_call_lock = Atomics.exchange(lock_view, 1, 0); + if (old_call_lock !== 1) { + throw new Error("Lock is already set"); + } + const num = Atomics.notify(lock_view, 1, 1); + if (num !== 1) { + if (num === 0) { + console.warn("notify failed, waiter is late"); + continue; + } + throw new Error(`notify failed: ${num}`); + } + } catch (e) { + console.error("error", e); + Atomics.store(lock_view, 1, 0); + Atomics.notify(lock_view, 1, 1); + } + } + } + + // listen fd + async listen_fd(fd_n: number) { + const lock_view = new Int32Array(this.lock_fds, fd_n * 12); + const bytes_offset = fd_n * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_i32 = new Int32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Int32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + const errno_offset = fd_func_sig_u32_size - 1; + Atomics.store(lock_view, 0, 0); + Atomics.store(lock_view, 1, 0); + Atomics.store(func_sig_view_i32, errno_offset, -1); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + let lock: "not-equal" | "timed-out" | "ok"; + + const { value } = Atomics.waitAsync(lock_view, 1, 0); + if (value instanceof Promise) { + // console.log("listen", fd_n, 1); + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out"); + } + + const func_lock = Atomics.load(lock_view, 1); + + if (func_lock !== 1) { + throw new Error(`func_lock is already set: ${func_lock}`); + } + + // console.log("func_lock", func_lock); + + // console.log("called", fd_n, 1); + + const set_error = (errno: number) => { + // console.log("set_error", errno, "pointer", errno_offset); + Atomics.store(func_sig_view_i32, errno_offset, errno); + }; + + const func_number = Atomics.load(func_sig_view_u32, 0); + + // console.log("called: func: ", get_func_name_from_number(func_number), "fd: ", fd_n); + + switch (func_number) { + // fd_advise: (fd: u32) => errno; + case 7: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = this.fd_advise(fd); + + set_error(error); + break; + } + // fd_allocate: (fd: u32, offset: u64, len: u64) => errno; + case 8: { + const fd = Atomics.load(func_sig_view_u32, 1); + const offset = Atomics.load(func_sig_view_u64, 1); + const len = Atomics.load(func_sig_view_u64, 2); + + const error = this.fd_allocate(fd, offset, len); + + set_error(error); + break; + } + // fd_close: (fd: u32) => errno; + case 9: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = await this.fd_close(fd); + + // console.log("fd_close", fd, error); + + set_error(error); + break; + } + // fd_datasync: (fd: u32) => errno; + case 10: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = this.fd_datasync(fd); + + set_error(error); + break; + } + // fd_fdstat_get: (fd: u32) => [wasi.Fdstat(u32 * 6)], errno]; + case 11: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [fdstat, ret] = this.fd_fdstat_get(fd); + + if (fdstat) { + Atomics.store(func_sig_view_u8, 0, fdstat.fs_filetype); + Atomics.store(func_sig_view_u16, 2, fdstat.fs_flags); + Atomics.store(func_sig_view_u64, 1, fdstat.fs_rights_base); + Atomics.store(func_sig_view_u64, 2, fdstat.fs_rights_inherited); + } + set_error(ret); + break; + } + // fd_fdstat_set_flags: (fd: u32, flags: u16) => errno; + case 12: { + const fd = Atomics.load(func_sig_view_u32, 1); + const flags = Atomics.load(func_sig_view_u16, 4); + + const error = this.fd_fdstat_set_flags(fd, flags); + + set_error(error); + break; + } + // fd_fdstat_set_rights: (fd: u32, fs_rights_base: u64, fs_rights_inheriting: u64) => errno; + case 13: { + const fd = Atomics.load(func_sig_view_u32, 1); + const fs_rights_base = Atomics.load(func_sig_view_u64, 1); + const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 2); + + const error = this.fd_fdstat_set_rights( + fd, + fs_rights_base, + fs_rights_inheriting, + ); + + set_error(error); + break; + } + // fd_filestat_get: (fd: u32) => [wasi.Filestat(u32 * 16)], errno]; + case 14: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [filestat, ret] = this.fd_filestat_get(fd); + + if (filestat) { + Atomics.store(func_sig_view_u64, 0, filestat.dev); + Atomics.store(func_sig_view_u64, 1, filestat.ino); + Atomics.store(func_sig_view_u8, 16, filestat.filetype); + Atomics.store(func_sig_view_u64, 3, filestat.nlink); + Atomics.store(func_sig_view_u64, 4, filestat.size); + Atomics.store(func_sig_view_u64, 5, filestat.atim); + Atomics.store(func_sig_view_u64, 6, filestat.mtim); + Atomics.store(func_sig_view_u64, 7, filestat.ctim); + } + + set_error(ret); + break; + } + // fd_filestat_set_size: (fd: u32, size: u64) => errno; + case 15: { + const fd = Atomics.load(func_sig_view_u32, 1); + const size = Atomics.load(func_sig_view_u64, 1); + + const error = this.fd_filestat_set_size(fd, size); + + set_error(error); + break; + } + // fd_filestat_set_times: (fd: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + case 16: { + const fd = Atomics.load(func_sig_view_u32, 1); + const atim = Atomics.load(func_sig_view_u64, 1); + const mtim = Atomics.load(func_sig_view_u64, 2); + const fst_flags = Atomics.load(func_sig_view_u16, 12); + + const error = this.fd_filestat_set_times(fd, atim, mtim, fst_flags); + + set_error(error); + break; + } + // fd_pread: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, data_ptr, errno]; + case 17: { + const fd = Atomics.load(func_sig_view_u32, 1); + const iovs_ptr = Atomics.load(func_sig_view_u32, 2); + const iovs_ptr_len = Atomics.load(func_sig_view_u32, 3); + const offset = Atomics.load(func_sig_view_u64, 2); + const data = new Uint32Array( + this.allocator.get_memory(iovs_ptr, iovs_ptr_len), + ); + this.allocator.free(iovs_ptr, iovs_ptr_len); + + const iovecs = new Array(); + for (let i = 0; i < iovs_ptr_len; i += 8) { + const iovec = new wasi.Iovec(); + iovec.buf = data[i * 2]; + iovec.buf_len = data[i * 2 + 1]; + iovecs.push(iovec); + } + + const [[nread, buffer8], error] = this.fd_pread(fd, iovecs, offset); + + if (nread !== undefined) { + Atomics.store(func_sig_view_u32, 0, nread); + } + if (buffer8) { + await this.allocator.async_write( + buffer8, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + } + set_error(error); + break; + } + // fd_prestat_get: (fd: u32) => [wasi.Prestat(u32 * 2)], errno]; + case 18: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [prestat, ret] = this.fd_prestat_get(fd); + + // console.log("fd_prestat_get", prestat, ret); + + if (prestat) { + Atomics.store(func_sig_view_u32, 0, prestat.tag); + Atomics.store( + func_sig_view_u32, + 1, + prestat.inner.pr_name.byteLength, + ); + } + set_error(ret); + break; + } + // fd_prestat_dir_name: (fd: u32, path_len: u32) => [path_ptr: pointer, path_len: u32, errno]; + case 19: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_len = Atomics.load(func_sig_view_u32, 2); + + const [prestat_dir_name, ret] = this.fd_prestat_dir_name( + fd, + path_len, + ); + + // console.log("fd_prestat_dir_name: park: ", prestat_dir_name); + + if ( + prestat_dir_name && + (ret === wasi.ERRNO_SUCCESS || ret === wasi.ERRNO_NAMETOOLONG) + ) { + await this.allocator.async_write( + prestat_dir_name, + this.fd_func_sig, + fd * fd_func_sig_u32_size, + ); + } + set_error(ret); + break; + } + // fd_pwrite: (fd: u32, write_data: pointer, write_data_len: u32, offset: u64) => [u32, errno]; + case 20: { + const fd = Atomics.load(func_sig_view_u32, 1); + const write_data_ptr = Atomics.load(func_sig_view_u32, 2); + const write_data_len = Atomics.load(func_sig_view_u32, 3); + const offset = Atomics.load(func_sig_view_u64, 2); + + const data = new Uint8Array( + this.allocator.get_memory(write_data_ptr, write_data_len), + ); + this.allocator.free(write_data_ptr, write_data_len); + + const [nwritten, error] = this.fd_pwrite(fd, data, offset); + + if (nwritten !== undefined) { + Atomics.store(func_sig_view_u32, 0, nwritten); + } + set_error(error); + break; + } + // fd_read: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, data_ptr, errno]; + case 21: { + const fd = Atomics.load(func_sig_view_u32, 1); + const iovs_ptr = Atomics.load(func_sig_view_u32, 2); + const iovs_ptr_len = Atomics.load(func_sig_view_u32, 3); + // console.log("fd_read: park: iovs: Uint8Array", this.allocator.get_memory(iovs_ptr, iovs_ptr_len)); + // console.log("ptr_len", iovs_ptr_len); + const iovs = new Uint32Array( + this.allocator.get_memory(iovs_ptr, iovs_ptr_len), + ); + this.allocator.free(iovs_ptr, iovs_ptr_len); + + // console.log("fd_read: park: iovs", iovs); + + const iovecs = new Array(); + for (let i = 0; i < iovs_ptr_len; i += 8) { + const iovec = new wasi.Iovec(); + iovec.buf = iovs[i * 2]; + iovec.buf_len = iovs[i * 2 + 1]; + iovecs.push(iovec); + } + + // console.log("fd_read: park: iovecs", iovecs); + + const [[nread, buffer8], error] = this.fd_read(fd, iovecs); + + // console.log("fd_read: park: buffer8", new TextDecoder().decode(buffer8)); + + if (nread !== undefined) { + Atomics.store(func_sig_view_u32, 0, nread); + } + if (buffer8) { + await this.allocator.async_write( + buffer8, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + } + set_error(error); + break; + } + // fd_readdir: (fd: u32, buf_len: u32, cookie: u64) => [buf_ptr: pointer, buf_len: u32, buf_used: u32, errno]; + case 22: { + const fd = Atomics.load(func_sig_view_u32, 1); + const buf_len = Atomics.load(func_sig_view_u32, 2); + const cookie = Atomics.load(func_sig_view_u64, 2); + + const [[array, buf_used], error] = this.fd_readdir( + fd, + buf_len, + cookie, + ); + + if (array) { + await this.allocator.async_write( + array, + this.fd_func_sig, + fd * fd_func_sig_u32_size, + ); + } + if (buf_used !== undefined) { + Atomics.store(func_sig_view_u32, 2, buf_used); + } + set_error(error); + break; + } + // fd_seek: (fd: u32, offset: i64, whence: u8) => [u64, errno]; + case 24: { + const fd = Atomics.load(func_sig_view_u32, 1); + const offset = Atomics.load(func_sig_view_u64, 1); + const whence = Atomics.load(func_sig_view_u8, 16); + + const [new_offset, error] = this.fd_seek(fd, offset, whence); + + if (new_offset !== undefined) { + Atomics.store(func_sig_view_u64, 0, new_offset); + } + set_error(error); + break; + } + // fd_sync: (fd: u32) => errno; + case 25: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = this.fd_sync(fd); + + set_error(error); + break; + } + // fd_tell: (fd: u32) => [u64, errno]; + case 26: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [offset, error] = this.fd_tell(fd); + + if (offset !== undefined) { + Atomics.store(func_sig_view_u64, 0, offset); + } + set_error(error); + break; + } + // fd_write: (fd: u32, write_data: pointer, write_data_len: u32) => [u32, errno]; + case 27: { + const fd = Atomics.load(func_sig_view_u32, 1); + const write_data_ptr = Atomics.load(func_sig_view_u32, 2); + const write_data_len = Atomics.load(func_sig_view_u32, 3); + + const data = new Uint8Array( + this.allocator.get_memory(write_data_ptr, write_data_len), + ); + this.allocator.free(write_data_ptr, write_data_len); + + // console.log("allocator", this.allocator); + + console.log( + "fd_write: park: write_data", + "fd: ", fd, + new TextDecoder().decode(data), + ); + + const [nwritten, error] = await this.fd_write(fd, data); + + // console.log("fd_write: park: error", error); + + if (nwritten !== undefined) { + Atomics.store(func_sig_view_u32, 0, nwritten); + } + set_error(error); + break; + } + // path_create_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + case 28: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const error = this.path_create_directory(fd, path_str); + + set_error(error); + break; + } + // path_filestat_get: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32) => [wasi.Filestat(u32 * 16), errno]; + case 29: { + const fd = Atomics.load(func_sig_view_u32, 1); + const flags = Atomics.load(func_sig_view_u32, 2); + const path_ptr = Atomics.load(func_sig_view_u32, 3); + const path_len = Atomics.load(func_sig_view_u32, 4); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const [filestat, ret] = this.path_filestat_get(fd, flags, path_str); + + if (filestat) { + Atomics.store(func_sig_view_u64, 0, filestat.dev); + Atomics.store(func_sig_view_u64, 1, filestat.ino); + Atomics.store(func_sig_view_u8, 16, filestat.filetype); + Atomics.store(func_sig_view_u64, 3, filestat.nlink); + Atomics.store(func_sig_view_u64, 4, filestat.size); + Atomics.store(func_sig_view_u64, 5, filestat.atim); + Atomics.store(func_sig_view_u64, 6, filestat.mtim); + Atomics.store(func_sig_view_u64, 7, filestat.ctim); + } + set_error(ret); + break; + } + // path_filestat_set_times: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + case 30: { + const fd = Atomics.load(func_sig_view_u32, 1); + const flags = Atomics.load(func_sig_view_u32, 2); + const path_ptr = Atomics.load(func_sig_view_u32, 3); + const path_len = Atomics.load(func_sig_view_u32, 4); + const atim = Atomics.load(func_sig_view_u64, 3); + const mtim = Atomics.load(func_sig_view_u64, 4); + const fst_flags = Atomics.load(func_sig_view_u16, 12); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const error = this.path_filestat_set_times( + fd, + flags, + path_str, + atim, + mtim, + fst_flags, + ); + + set_error(error); + break; + } + // path_link: (old_fd: u32, old_flags: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + case 31: { + const old_fd = Atomics.load(func_sig_view_u32, 1); + const old_flags = Atomics.load(func_sig_view_u32, 2); + const old_path_ptr = Atomics.load(func_sig_view_u32, 3); + const old_path_len = Atomics.load(func_sig_view_u32, 4); + const new_fd = Atomics.load(func_sig_view_u32, 5); + const new_path_ptr = Atomics.load(func_sig_view_u32, 6); + const new_path_len = Atomics.load(func_sig_view_u32, 7); + + const old_path = new Uint8Array( + this.allocator.get_memory(old_path_ptr, old_path_len), + ); + const old_path_str = new TextDecoder().decode(old_path); + this.allocator.free(old_path_ptr, old_path_len); + const new_path = new Uint8Array( + this.allocator.get_memory(new_path_ptr, new_path_len), + ); + const new_path_str = new TextDecoder().decode(new_path); + this.allocator.free(new_path_ptr, new_path_len); + + const error = this.path_link( + old_fd, + old_flags, + old_path_str, + new_fd, + new_path_str, + ); + + set_error(error); + break; + } + // path_open: (fd: u32, dirflags: u32, path_ptr: pointer, path_len: u32, oflags: u32, fs_rights_base: u64, fs_rights_inheriting: u64, fdflags: u16) => [u32, errno]; + case 32: { + const fd = Atomics.load(func_sig_view_u32, 1); + const dirflags = Atomics.load(func_sig_view_u32, 2); + const path_ptr = Atomics.load(func_sig_view_u32, 3); + const path_len = Atomics.load(func_sig_view_u32, 4); + const oflags = Atomics.load(func_sig_view_u32, 5); + const fs_rights_base = Atomics.load(func_sig_view_u64, 3); + const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 4); + const fd_flags = Atomics.load(func_sig_view_u16, 20); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const [opened_fd, error] = await this.path_open( + fd, + dirflags, + path_str, + oflags, + fs_rights_base, + fs_rights_inheriting, + fd_flags, + ); + + // console.log("path_open: opend_fd", opened_fd, error); + + if (opened_fd !== undefined) { + Atomics.store(func_sig_view_u32, 0, opened_fd); + } + set_error(error); + break; + } + // path_readlink: (fd: u32, path_ptr: pointer, path_len: u32, buf_len: u32) => [buf_len: u32, data_ptr: pointer, data_len: u32, errno]; + case 33: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + const buf_len = Atomics.load(func_sig_view_u32, 4); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const [buf, error] = this.path_readlink(fd, path_str, buf_len); + + if (buf) { + await this.allocator.async_write( + buf, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + Atomics.store(func_sig_view_u32, 0, buf.byteLength); + } + set_error(error); + break; + } + // path_remove_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + case 34: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const error = this.path_remove_directory(fd, path_str); + + set_error(error); + break; + } + // path_rename: (old_fd: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + case 35: { + const fd = Atomics.load(func_sig_view_u32, 1); + const old_path_ptr = Atomics.load(func_sig_view_u32, 2); + const old_path_len = Atomics.load(func_sig_view_u32, 3); + const new_fd = Atomics.load(func_sig_view_u32, 4); + const new_path_ptr = Atomics.load(func_sig_view_u32, 5); + const new_path_len = Atomics.load(func_sig_view_u32, 6); + + const old_path = new Uint8Array( + this.allocator.get_memory(old_path_ptr, old_path_len), + ); + const old_path_str = new TextDecoder().decode(old_path); + this.allocator.free(old_path_ptr, old_path_len); + const new_path = new Uint8Array( + this.allocator.get_memory(new_path_ptr, new_path_len), + ); + const new_path_str = new TextDecoder().decode(new_path); + this.allocator.free(new_path_ptr, new_path_len); + + const error = this.path_rename( + fd, + old_path_str, + new_fd, + new_path_str, + ); + + set_error(error); + break; + } + // path_symlink: (old_path_ptr: pointer, old_path_len: u32, fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + case 36: { + const old_path_ptr = Atomics.load(func_sig_view_u32, 1); + const old_path_len = Atomics.load(func_sig_view_u32, 2); + const fd = Atomics.load(func_sig_view_u32, 3); + const new_path_ptr = Atomics.load(func_sig_view_u32, 4); + const new_path_len = Atomics.load(func_sig_view_u32, 5); + + const old_path = new Uint8Array( + this.allocator.get_memory(old_path_ptr, old_path_len), + ); + const old_path_str = new TextDecoder().decode(old_path); + this.allocator.free(old_path_ptr, old_path_len); + const new_path = new Uint8Array( + this.allocator.get_memory(new_path_ptr, new_path_len), + ); + const new_path_str = new TextDecoder().decode(new_path); + this.allocator.free(new_path_ptr, new_path_len); + + set_error(this.path_symlink(old_path_str, fd, new_path_str)); + break; + } + // path_unlink_file: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + case 37: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + set_error(this.path_unlink_file(fd, path_str)); + break; + } + // open_fd_with_buff: (fd: u32, buf: pointer, buf_len: u32) => [u32, errno]; + case 38: { + const fd = Atomics.load(func_sig_view_u32, 1); + const buf_ptr = Atomics.load(func_sig_view_u32, 2); + const buf_len = Atomics.load(func_sig_view_u32, 3); + + const buf = new Uint8Array( + this.allocator.get_memory(buf_ptr, buf_len), + ); + this.allocator.free(buf_ptr, buf_len); + + const [opened_fd, error] = await this.open_fd_with_buff(fd, buf); + + if (opened_fd !== undefined) { + Atomics.store(func_sig_view_u32, 0, opened_fd); + } + set_error(error); + break; + } + default: { + throw new Error(`Unknown function number: ${func_number}`); + } + } + + const old_call_lock = Atomics.exchange(lock_view, 1, 0); + if (old_call_lock !== 1) { + throw new Error( + `Call is already set: ${old_call_lock}\nfunc: ${get_func_name_from_number(func_number)}\nfd: ${fd_n}`, + ); + } + + // console.log("called end: func: ", get_func_name_from_number(func_number), "fd: ", fd_n); + + const n = Atomics.notify(lock_view, 1); + if (n !== 1) { + if (n === 0) { + console.warn("notify number is 0. ref is late?"); + } else { + console.warn(`notify number is not 1: ${n}`); + } + } + + if (this.fds[fd_n] === undefined) { + break; + } + } catch (e) { + console.error(e); + + // sleep 1000ms + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const lock_view = new Int32Array(this.lock_fds); + Atomics.exchange(lock_view, 1, 0); + const func_sig_view = new Int32Array(this.fd_func_sig); + Atomics.exchange(func_sig_view, 16, -1); + } + } + } +} diff --git a/src/wasi_farm/shared_array_buffer/ref.ts b/src/wasi_farm/shared_array_buffer/ref.ts new file mode 100644 index 0000000..d0dfffb --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/ref.ts @@ -0,0 +1,1437 @@ +import * as wasi from "../../wasi_defs.js"; +import { WASIFarmRef, type WASIFarmRefObject } from "../ref.js"; +import { + AllocatorUseArrayBuffer, + type AllocatorUseArrayBufferObject, +} from "./allocator.js"; +import { + FdCloseSenderUseArrayBuffer, + type FdCloseSenderUseArrayBufferObject, +} from "./fd_close_sender.js"; +import { fd_func_sig_bytes, fd_func_sig_u32_size } from "./park.js"; + +export type WASIFarmRefUseArrayBufferObject = { + allocator: AllocatorUseArrayBuffer; + lock_fds: SharedArrayBuffer; + fds_len_and_num: SharedArrayBuffer; + fd_func_sig: SharedArrayBuffer; + base_func_util: SharedArrayBuffer; +} & WASIFarmRefObject; + +// Transmittable objects to communicate with Park +export class WASIFarmRefUseArrayBuffer extends WASIFarmRef { + // For more information on member variables, see . See /park.ts + allocator: AllocatorUseArrayBuffer; + lock_fds: SharedArrayBuffer; + // byte 1: fds_len + // byte 2: all wasi_farm_ref num + fds_len_and_num: SharedArrayBuffer; + fd_func_sig: SharedArrayBuffer; + base_func_util: SharedArrayBuffer; + + constructor( + allocator: AllocatorUseArrayBufferObject, + lock_fds: SharedArrayBuffer, + fds_len_and_num: SharedArrayBuffer, + fd_func_sig: SharedArrayBuffer, + base_func_util: SharedArrayBuffer, + fd_close_receiver: FdCloseSenderUseArrayBufferObject, + stdin: number | undefined, + stdout: number | undefined, + stderr: number | undefined, + default_fds: Array, + ) { + super( + stdin, + stdout, + stderr, + FdCloseSenderUseArrayBuffer.init_self(fd_close_receiver), + default_fds, + ); + this.allocator = AllocatorUseArrayBuffer.init_self(allocator); + this.lock_fds = lock_fds; + this.fd_func_sig = fd_func_sig; + this.base_func_util = base_func_util; + this.fds_len_and_num = fds_len_and_num; + + // console.log("fds_len_and_num", this.fds_len_and_num); + + // const view = new Int32Array(this.fds_len_and_num); + // Atomics.store(view, 0, 0); + } + + get_fds_len(): number { + const view = new Int32Array(this.fds_len_and_num); + return Atomics.load(view, 0); + } + + static init_self(sl: WASIFarmRefUseArrayBufferObject): WASIFarmRef { + return new WASIFarmRefUseArrayBuffer( + sl.allocator, + sl.lock_fds, + sl.fds_len_and_num, + sl.fd_func_sig, + sl.base_func_util, + sl.fd_close_receiver as unknown as FdCloseSenderUseArrayBufferObject, + sl.stdin, + sl.stdout, + sl.stderr, + sl.default_fds, + ); + } + + // allocate a new id on wasi_farm_ref and return it + set_id(): number { + const view = new Int32Array(this.fds_len_and_num); + const id = Atomics.add(view, 1, 1); + this.id = id; + return id; + } + + // lock base_func + private lock_base_func(): void { + const view = new Int32Array(this.base_func_util); + // eslint-disable-next-line no-constant-condition + while (true) { + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + // call base_func + private call_base_func(): void { + const view = new Int32Array(this.base_func_util); + const old = Atomics.exchange(view, 1, 1); + if (old !== 0) { + console.error("what happened?"); + } + Atomics.notify(view, 1, 1); + } + + // wait base_func + private wait_base_func(): void { + const view = new Int32Array(this.base_func_util); + const lock = Atomics.wait(view, 1, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + } + + // release base_func + private release_base_func(): void { + const view = new Int32Array(this.base_func_util); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + // set park_fds_map + set_park_fds_map(fds: Array): void { + this.lock_base_func(); + const view = new Int32Array(this.base_func_util); + Atomics.store(view, 2, 0); + const fds_array = new Uint32Array(fds); + // console.log("fds_array", fds_array); + this.allocator.block_write(fds_array, this.base_func_util, 3); + Atomics.store(view, 5, this.id); + this.call_base_func(); + this.wait_base_func(); + this.release_base_func(); + } + + private lock_fd(fd: number) { + // console.log("lock_fd start", fd); + const view = new Int32Array(this.lock_fds, fd * 12); + // eslint-disable-next-line no-constant-condition + while (true) { + const now_value = Atomics.load(view, 0); + if (now_value !== 0) { + const value = Atomics.wait(view, 0, now_value); + if (value === "timed-out") { + console.error("lock_fd timed-out"); + continue; + } + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old === 0) { + // console.log("lock_fd success", fd); + return; + } + } + } + + private release_fd(fd: number) { + // console.log("release_fd", fd); + const view = new Int32Array(this.lock_fds, fd * 12); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + private lock_double_fd(fd1: number, fd2: number) { + // console.log("lock_double_fd", fd1, fd2); + const view = new Int32Array(this.lock_fds); + // eslint-disable-next-line no-constant-condition + while (true) { + const now_value1 = Atomics.load(view, fd1 * 3); + const value = Atomics.wait(view, fd1 * 3, now_value1); + if (value === "timed-out") { + console.error("lock_double_fd timed-out"); + continue; + } + const old1 = Atomics.exchange(view, fd1 * 3, 2); + if (old1 === 0) { + const now_value2 = Atomics.load(view, fd2 * 3); + if (now_value2 === 2) { + // It's nearly deadlocked. + if (fd1 < fd2) { + // release fd1 + Atomics.store(view, fd1 * 3, 0); + Atomics.notify(view, fd1 * 3, 1); + continue; + } + } + const value = Atomics.wait(view, fd2 * 3, now_value2); + if (value === "timed-out") { + console.error("lock_double_fd timed-out"); + continue; + } + const old2 = Atomics.exchange(view, fd2 * 3, 2); + if (old2 === 0) { + return; + } + Atomics.store(view, fd1 * 3, 0); + Atomics.notify(view, fd1 * 3, 1); + } + } + } + + private release_double_fd(fd1: number, fd2: number) { + // console.log("release_double_fd", fd1, fd2); + const view = new Int32Array(this.lock_fds); + Atomics.store(view, fd1 * 3, 0); + Atomics.notify(view, fd1 * 3, 1); + Atomics.store(view, fd2 * 3, 0); + Atomics.notify(view, fd2 * 3, 1); + } + + private invoke_fd_func(fd: number): boolean { + if (fd === undefined) { + return false; + } + // console.log("invoke_fd_func", fd); + const view = new Int32Array(this.lock_fds, fd * 12 + 4); + const old = Atomics.exchange(view, 0, 1); + if (old === 1) { + console.error(`invoke_fd_func already invoked\nfd: ${fd}`); + return; + } + const n = Atomics.notify(view, 0); + if (n !== 1) { + if (n === 0) { + const len = this.get_fds_len(); + if (len <= fd) { + const lock = Atomics.exchange(view, 0, 0); + if (lock !== 1) { + console.error("what happened?"); + } + Atomics.notify(view, 0, 1); + console.error("what happened?: len", len, "fd", fd); + return true; + } + console.warn("invoke_func_loop is late"); + return true; + } + console.error("invoke_fd_func notify failed:", n); + return false; + } + return true; + } + + private wait_fd_func(fd: number) { + // console.log("wait_fd_func", fd); + const view = new Int32Array(this.lock_fds, fd * 12 + 4); + const value = Atomics.wait(view, 0, 1); + if (value === "timed-out") { + console.error("wait call park_fd_func timed-out"); + } + } + + private call_fd_func(fd: number): boolean { + if (!this.invoke_fd_func(fd)) { + return false; + } + // console.log("call_fd_func", fd); + this.wait_fd_func(fd); + // console.log("wait_fd_func", fd); + // console.log("call_fd_func released", fd); + return true; + } + + private get_error(fd: number): number { + const func_sig_view_i32 = new Int32Array( + this.fd_func_sig, + fd * fd_func_sig_bytes, + ); + const errno_offset = fd_func_sig_u32_size - 1; + // console.log("get_error: offset", errno_offset); + return Atomics.load(func_sig_view_i32, errno_offset); + } + + fd_advise(fd: number): number { + this.lock_fd(fd); + + const func_sig_view_u32 = new Uint32Array( + this.fd_func_sig, + fd * fd_func_sig_bytes, + ); + + Atomics.store(func_sig_view_u32, 0, 7); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_allocate(fd: number, offset: bigint, len: bigint): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 8); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, offset); + Atomics.store(func_sig_view_u64, 2, len); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_close(fd: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 9); + Atomics.store(func_sig_view_u32, 1, fd); + + // console.log("fd_close: ref", fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + // console.log("fd_close: ref2", fd); + + const error = this.get_error(fd); + + this.release_fd(fd); + + // console.log("fd_close: ref3", fd); + + return error; + } + + fd_datasync(fd: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 10); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_fdstat_get(fd: number): [wasi.Fdstat | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 11); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const fs_filetype = Atomics.load(func_sig_view_u8, 0); + const fs_flags = Atomics.load(func_sig_view_u16, 2); + const fs_rights_base = Atomics.load(func_sig_view_u64, 1); + const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 2); + + this.release_fd(fd); + + const fd_stat = new wasi.Fdstat(fs_filetype, fs_flags); + fd_stat.fs_rights_base = fs_rights_base; + fd_stat.fs_rights_inherited = fs_rights_inheriting; + + return [fd_stat, error]; + } + + fd_fdstat_set_flags(fd: number, flags: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 12); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u16, 4, flags); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_fdstat_set_rights( + fd: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 13); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, fs_rights_base); + Atomics.store(func_sig_view_u64, 2, fs_rights_inheriting); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_filestat_get(fd: number): [wasi.Filestat | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 14); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const fs_dev = Atomics.load(func_sig_view_u64, 0); + const fs_ino = Atomics.load(func_sig_view_u64, 1); + const fs_filetype = Atomics.load(func_sig_view_u8, 16); + const fs_nlink = Atomics.load(func_sig_view_u64, 3); + const fs_size = Atomics.load(func_sig_view_u64, 4); + const fs_atim = Atomics.load(func_sig_view_u64, 5); + const fs_mtim = Atomics.load(func_sig_view_u64, 6); + const fs_ctim = Atomics.load(func_sig_view_u64, 7); + + this.release_fd(fd); + + const file_stat = new wasi.Filestat(fs_filetype, fs_size); + file_stat.dev = fs_dev; + file_stat.ino = fs_ino; + file_stat.nlink = fs_nlink; + file_stat.atim = fs_atim; + file_stat.mtim = fs_mtim; + file_stat.ctim = fs_ctim; + + return [file_stat, error]; + } + + fd_filestat_set_size(fd: number, size: bigint): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 15); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, size); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_filestat_set_times( + fd: number, + st_atim: bigint, + st_mtim: bigint, + fst_flags: number, + ): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 16); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, st_atim); + Atomics.store(func_sig_view_u64, 2, st_mtim); + Atomics.store(func_sig_view_u16, 12, fst_flags); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_pread( + fd: number, + iovs: Uint32Array, + offset: bigint, + ): [[number, Uint8Array] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 17); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + iovs, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u64, 2, offset); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const nread = Atomics.load(func_sig_view_u32, 0); + const buf_ptr = Atomics.load(func_sig_view_u32, 1); + const buf_len = Atomics.load(func_sig_view_u32, 2); + this.release_fd(fd); + + if (error === wasi.ERRNO_BADF) { + this.allocator.free(buf_ptr, buf_len); + return [undefined, error]; + } + + const buf = new Uint8Array(this.allocator.get_memory(buf_ptr, buf_len)); + + if (nread !== buf_len) { + console.error("pread nread !== buf_len"); + } + + this.allocator.free(buf_ptr, buf_len); + + return [[nread, buf], error]; + } + + fd_prestat_get(fd: number): [[number, number] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 18); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const pr_tag = Atomics.load(func_sig_view_u32, 0); + const pr_name_len = Atomics.load(func_sig_view_u32, 1); + + this.release_fd(fd); + + return [[pr_tag, pr_name_len], error]; + } + + fd_prestat_dir_name( + fd: number, + path_len: number, + ): [Uint8Array | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 19); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, path_len); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const ret_path_ptr = Atomics.load(func_sig_view_u32, 0); + const ret_path_len = Atomics.load(func_sig_view_u32, 1); + + this.release_fd(fd); + if (error !== wasi.ERRNO_SUCCESS && error !== wasi.ERRNO_NAMETOOLONG) { + this.allocator.free(ret_path_ptr, ret_path_len); + return [undefined, error]; + } + + const ret_path = new Uint8Array( + this.allocator.get_memory(ret_path_ptr, ret_path_len), + ); + this.allocator.free(ret_path_ptr, ret_path_len); + + return [ret_path, error]; + } + + fd_pwrite( + fd: number, + write_data: Uint8Array, + offset: bigint, + ): [number | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 20); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + write_data, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u64, 2, offset); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const nwritten = Atomics.load(func_sig_view_u32, 0); + + this.release_fd(fd); + + return [nwritten, error]; + } + + fd_read( + fd: number, + iovs: Uint32Array, + ): [[number, Uint8Array] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 21); + Atomics.store(func_sig_view_u32, 1, fd); + + // console.log("fd_read: ref: iovs", iovs); + // console.log("iovs.buffer", iovs.buffer.slice(0, iovs.byteLength)); + + const [ptr, len] = this.allocator.block_write( + iovs, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + // console.log("fd_read: ref: iovs", iovs); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const nread = Atomics.load(func_sig_view_u32, 0); + const buf_ptr = Atomics.load(func_sig_view_u32, 1); + const buf_len = Atomics.load(func_sig_view_u32, 2); + this.release_fd(fd); + + // console.log("fd_read: ref: ", nread, buf_ptr, buf_len); + + if (error === wasi.ERRNO_BADF) { + this.allocator.free(buf_ptr, buf_len); + return [undefined, error]; + } + + // fd_read: ref: 14 30 14 + // animals.ts:325 fd_read: nread 14 Hello, world! + // fd_read: ref: 21 52 32 + // ref.ts:655 fd_read: ref: 21 + const buf = new Uint8Array(this.allocator.get_memory(buf_ptr, buf_len)); + // console.log("fd_read: ref: ", nread, new TextDecoder().decode(buf)); + + // console.log("fd_read: nread", nread, new TextDecoder().decode(buf)); + + if (nread !== buf_len) { + console.error("read nread !== buf_len"); + } + + this.allocator.free(buf_ptr, buf_len); + + return [[nread, buf], error]; + } + + fd_readdir( + fd: number, + limit_buf_len: number, + cookie: bigint, + ): [[Uint8Array, number] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 22); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, limit_buf_len); + Atomics.store(func_sig_view_u64, 2, cookie); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const buf_ptr = Atomics.load(func_sig_view_u32, 0); + const buf_len = Atomics.load(func_sig_view_u32, 1); + const buf_used = Atomics.load(func_sig_view_u32, 2); + this.release_fd(fd); + + if (error === wasi.ERRNO_BADF) { + this.allocator.free(buf_ptr, buf_len); + return [undefined, error]; + } + + const buf = new Uint8Array(this.allocator.get_memory(buf_ptr, buf_len)); + + this.allocator.free(buf_ptr, buf_len); + + return [[buf, buf_used], error]; + } + + // fd_renumber( + // fd: number, + // to: number, + // ): number { + // this.lock_double_fd(fd, to); + + // const bytes_offset = fd * fd_func_sig_bytes; + // const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + // // fd + // Atomics.store(func_sig_view_u32, 0, 23); + // Atomics.store(func_sig_view_u32, 1, fd); + // Atomics.store(func_sig_view_u32, 2, to); + + // if (!this.call_fd_func(fd)) { + // this.release_fd(fd); + // return wasi.ERRNO_BADF; + // } + + // const error = this.get_error(fd); + + // this.release_double_fd(fd, to); + + // return error; + // } + + fd_seek( + fd: number, + offset: bigint, + whence: number, + ): [bigint | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 24); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, offset); + Atomics.store(func_sig_view_u8, 16, whence); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const new_offset = Atomics.load(func_sig_view_u64, 0); + + this.release_fd(fd); + + return [new_offset, error]; + } + + fd_sync(fd: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 25); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_tell(fd: number): [bigint | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 26); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const offset = Atomics.load(func_sig_view_u64, 0); + + this.release_fd(fd); + + return [offset, error]; + } + + fd_write(fd: number, write_data: Uint8Array): [number | undefined, number] { + this.lock_fd(fd); + + // console.log("fd_write: ref: write_data", new TextDecoder().decode(write_data)); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 27); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + write_data, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + // console.log("fd_write: ref: error", "wasi.ERRNO_BADF"); + + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + // console.log("fd_write: ref: error", this.get_error(fd)); + + // console.log("fd_write: ref: error", error); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const nwritten = Atomics.load(func_sig_view_u32, 0); + + this.release_fd(fd); + + return [nwritten, error]; + } + + path_create_directory(fd: number, path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 28); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_filestat_get( + fd: number, + flags: number, + path: Uint8Array, + ): [wasi.Filestat | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 29); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, flags); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 3, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const fs_dev = Atomics.load(func_sig_view_u64, 0); + const fs_ino = Atomics.load(func_sig_view_u64, 1); + const fs_filetype = Atomics.load(func_sig_view_u8, 16); + const fs_nlink = Atomics.load(func_sig_view_u64, 3); + const fs_size = Atomics.load(func_sig_view_u64, 4); + const fs_atim = Atomics.load(func_sig_view_u64, 5); + const fs_mtim = Atomics.load(func_sig_view_u64, 6); + const fs_ctim = Atomics.load(func_sig_view_u64, 7); + + this.release_fd(fd); + + const file_stat = new wasi.Filestat(fs_filetype, fs_size); + file_stat.dev = fs_dev; + file_stat.ino = fs_ino; + file_stat.nlink = fs_nlink; + file_stat.atim = fs_atim; + file_stat.mtim = fs_mtim; + file_stat.ctim = fs_ctim; + + return [file_stat, error]; + } + + path_filestat_set_times( + fd: number, + flags: number, + path: Uint8Array, + st_atim: bigint, + st_mtim: bigint, + fst_flags: number, + ): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 30); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, flags); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 3, + ); + Atomics.store(func_sig_view_u64, 3, st_atim); + Atomics.store(func_sig_view_u64, 4, st_mtim); + Atomics.store(func_sig_view_u16, 12, fst_flags); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_link( + old_fd: number, + old_flags: number, + old_path: Uint8Array, + new_fd: number, + new_path: Uint8Array, + ): number { + this.lock_double_fd(old_fd, new_fd); + + const bytes_offset = old_fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 31); + Atomics.store(func_sig_view_u32, 1, old_fd); + Atomics.store(func_sig_view_u32, 2, old_flags); + const [ptr1, len1] = this.allocator.block_write( + old_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 3, + ); + Atomics.store(func_sig_view_u32, 5, new_fd); + const [ptr2, len2] = this.allocator.block_write( + new_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 6, + ); + + if (!this.call_fd_func(old_fd)) { + this.allocator.free(ptr1, len1); + this.allocator.free(ptr2, len2); + this.release_double_fd(old_fd, new_fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(old_fd); + + this.release_double_fd(old_fd, new_fd); + + return error; + } + + path_open( + fd: number, + dirflags: number, + path: Uint8Array, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + ): [number | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 32); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, dirflags); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 3, + ); + Atomics.store(func_sig_view_u32, 5, oflags); + Atomics.store(func_sig_view_u64, 3, fs_rights_base); + Atomics.store(func_sig_view_u64, 4, fs_rights_inheriting); + Atomics.store(func_sig_view_u16, 20, fs_flags); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_SUCCESS) { + const new_fd = Atomics.load(func_sig_view_u32, 0); + this.release_fd(fd); + return [new_fd, error]; + } + + this.release_fd(fd); + + return [undefined, error]; + } + + path_readlink( + fd: number, + path: Uint8Array, + buf_len: number, + ): [Uint8Array | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 33); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u32, 4, buf_len); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const nread = Atomics.load(func_sig_view_u32, 0); + const ret_path_ptr = Atomics.load(func_sig_view_u32, 1); + const ret_path_len = Atomics.load(func_sig_view_u32, 2); + + this.release_fd(fd); + if (error !== wasi.ERRNO_SUCCESS && error !== wasi.ERRNO_NAMETOOLONG) { + this.allocator.free(ret_path_ptr, ret_path_len); + return [undefined, error]; + } + + const ret_path = new Uint8Array( + this.allocator.get_memory(ret_path_ptr, ret_path_len), + ); + const ret_path_slice = ret_path.slice(0, nread); + + return [ret_path_slice, error]; + } + + path_remove_directory(fd: number, path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 34); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_rename( + old_fd: number, + old_path: Uint8Array, + new_fd: number, + new_path: Uint8Array, + ): number { + this.lock_double_fd(old_fd, new_fd); + + const bytes_offset = old_fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 35); + Atomics.store(func_sig_view_u32, 1, old_fd); + const [ptr1, len1] = this.allocator.block_write( + old_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u32, 4, new_fd); + const [ptr2, len2] = this.allocator.block_write( + new_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 5, + ); + + if (!this.call_fd_func(old_fd)) { + this.allocator.free(ptr1, len1); + this.allocator.free(ptr2, len2); + this.release_double_fd(old_fd, new_fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(old_fd); + + this.release_double_fd(old_fd, new_fd); + + return error; + } + + path_symlink(old_path: Uint8Array, fd: number, new_path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 36); + const [ptr1, len1] = this.allocator.block_write( + old_path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + Atomics.store(func_sig_view_u32, 3, fd); + const [ptr2, len2] = this.allocator.block_write( + new_path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 4, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr1, len1); + this.allocator.free(ptr2, len2); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_unlink_file(fd: number, path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 37); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + open_fd_with_buff(fd: number, buf: Uint8Array): [number, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 38); + Atomics.store(func_sig_view_u32, 1, fd); + + const [ptr, len] = this.allocator.block_write( + buf, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_SUCCESS) { + const new_fd = Atomics.load(func_sig_view_u32, 0); + this.release_fd(fd); + return [new_fd, error]; + } + + this.release_fd(fd); + + return [undefined, error]; + } +} diff --git a/src/wasi_farm/shared_array_buffer/sender.ts b/src/wasi_farm/shared_array_buffer/sender.ts new file mode 100644 index 0000000..252e36b --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/sender.ts @@ -0,0 +1,221 @@ +export type ToRefSenderUseArrayBufferObject = { + data_size: number; + share_arrays_memory?: SharedArrayBuffer; +}; + +// To ref sender abstract class +export abstract class ToRefSenderUseArrayBuffer { + // The structure is similar to an allocator, but the mechanism is different + + // Example of fd management + // This needs to be handled + // 1. Start of path_open + // 2. Removed by fd_close + // 2.1 Sent by ToRefSender + // 3. Reassigned by path_open + // < Closed by ToRefSender + // 3.1 The person who opened it can use it + // < Closed by ToRefSender — this alone will cause a bug + // Structurally, this shouldn't happen in the farm + + // In the end, when receiving from this function, it should be done on the first call of each function + + // The first 4 bytes are for lock value: i32 + // The next 4 bytes are the current number of data: m: i32 + // The next 4 bytes are the length of the area used by share_arrays_memory: n: i32 + // Data header + // 4 bytes: remaining target count + // 4 bytes: target count (n) + // n * 4 bytes: target allocation numbers + // Data + // data_size bytes: data + private share_arrays_memory: SharedArrayBuffer; + + // The size of the data + private data_size: number; + + constructor( + // data is Uint32Array + // and data_size is data.length + data_size: number, + max_share_arrays_memory: number = 100 * 1024, + share_arrays_memory?: SharedArrayBuffer, + ) { + this.data_size = data_size; + if (share_arrays_memory) { + this.share_arrays_memory = share_arrays_memory; + } else { + this.share_arrays_memory = new SharedArrayBuffer(max_share_arrays_memory); + } + const view = new Int32Array(this.share_arrays_memory); + Atomics.store(view, 0, 0); + Atomics.store(view, 1, 0); + Atomics.store(view, 2, 12); + } + + protected static init_self_inner(sl: ToRefSenderUseArrayBufferObject): { + data_size: number; + max_share_arrays_memory: number; + share_arrays_memory: SharedArrayBuffer; + } { + return { + data_size: sl.data_size, + max_share_arrays_memory: sl.share_arrays_memory.byteLength, + share_arrays_memory: sl.share_arrays_memory, + }; + } + + private async async_lock(): Promise { + const view = new Int32Array(this.share_arrays_memory); + // eslint-disable-next-line no-constant-condition + while (true) { + let lock: "not-equal" | "timed-out" | "ok"; + const { value } = Atomics.waitAsync(view, 0, 1); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private block_lock(): void { + // eslint-disable-next-line no-constant-condition + while (true) { + const view = new Int32Array(this.share_arrays_memory); + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private release_lock(): void { + const view = new Int32Array(this.share_arrays_memory); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + protected async async_send( + targets: Array, + data: Uint32Array, + ): Promise { + await this.async_lock(); + + const view = new Int32Array(this.share_arrays_memory); + const used_len = Atomics.load(view, 2); + const data_len = data.byteLength; + if (data_len !== this.data_size) { + throw new Error(`invalid data size: ${data_len} !== ${this.data_size}`); + } + const new_used_len = used_len + data_len + 8 + targets.length * 4; + if (new_used_len > this.share_arrays_memory.byteLength) { + throw new Error("over memory"); + } + + Atomics.store(view, 2, new_used_len); + + const header = new Int32Array(this.share_arrays_memory, used_len); + header[0] = targets.length; + header[1] = targets.length; + header.set(targets, 2); + + const data_view = new Uint32Array( + this.share_arrays_memory, + used_len + 8 + targets.length * 4, + ); + data_view.set(data); + + // console.log("async_send send", targets, data); + + Atomics.add(view, 1, 1); + + this.release_lock(); + } + + protected get_data(id: number): Array | undefined { + const view = new Int32Array(this.share_arrays_memory); + const data_num_tmp = Atomics.load(view, 1); + if (data_num_tmp === 0) { + return undefined; + } + + this.block_lock(); + + const data_num = Atomics.load(view, 1); + + const return_data: Array = []; + + let offset = 12; + // console.log("data_num", data_num); + for (let i = 0; i < data_num; i++) { + // console.log("this.share_arrays_memory", this.share_arrays_memory); + const header = new Int32Array(this.share_arrays_memory, offset); + const target_num = header[1]; + const targets = new Int32Array( + this.share_arrays_memory, + offset + 8, + target_num, + ); + const data_len = this.data_size; + if (targets.includes(id)) { + const data = new Uint32Array( + this.share_arrays_memory, + offset + 8 + target_num * 4, + data_len / 4, + ); + + // なぜかわからないが、上では正常に動作せず、以下のようにすると動作する + // return_data.push(new Uint32Array(data)); + return_data.push(new Uint32Array([...data])); + + const target_index = targets.indexOf(id); + Atomics.store(targets, target_index, -1); + const old_left_targets_num = Atomics.sub(header, 0, 1); + if (old_left_targets_num === 1) { + // rm data + Atomics.sub(view, 1, 1); + const used_len = Atomics.load(view, 2); + const new_used_len = used_len - data_len - 8 - target_num * 4; + Atomics.store(view, 2, new_used_len); + const next_data_offset = offset + data_len + 8 + target_num * 4; + const next_tail = new Int32Array( + this.share_arrays_memory, + next_data_offset, + ); + const now_tail = new Int32Array(this.share_arrays_memory, offset); + now_tail.set(next_tail); + // console.log("new_used_len", new_used_len); + } else { + offset += data_len + 8 + target_num * 4; + } + } else { + offset += data_len + 8 + target_num * 4; + } + // console.log("offset", offset); + } + + if (offset !== Atomics.load(view, 2)) { + throw new Error(`invalid offset: ${offset} !== ${Atomics.load(view, 2)}`); + } + + this.release_lock(); + + // console.log("get_data get: return_data", return_data); + + return return_data; + } +} diff --git a/src/wasi_farm/shared_array_buffer/thread_spawn.ts b/src/wasi_farm/shared_array_buffer/thread_spawn.ts new file mode 100644 index 0000000..fcaa2cd --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/thread_spawn.ts @@ -0,0 +1,468 @@ +// (export "wasi_thread_start" (func $61879)) +// (func $61879 (param $0 i32) (param $1 i32) +// (local $2 i32) +// (local $3 i32) +// (local $4 i32) +// (local $5 i32) +// (local $6 i32) +// (local $7 i32) +// (global.set $global$0 +// (i32.load +// (local.get $1) +// ) +// ) + +// (import "wasi" "thread-spawn" (func $fimport$27 (param i32) (result i32))) + +import { strace } from "../../strace.js"; +import { WASIFarmAnimal } from "../animals.js"; +import type { WASIFarmRefObject } from "../ref.js"; +import type { WorkerBackgroundRefObject } from "./worker_background/index.js"; +import { + WorkerBackgroundRef, + worker_background_worker_url, +} from "./worker_background/index.js"; +import { WorkerBackgroundRefObjectConstructor } from "./worker_background/worker_export.js"; + +type ThreadSpawnerObject = { + share_memory: WebAssembly.Memory; + wasi_farm_refs_object: Array; + worker_url: string; + worker_background_ref_object: WorkerBackgroundRefObject; + // inst_default_buffer_kept: WebAssembly.Memory; +}; + +export class ThreadSpawner { + private share_memory: WebAssembly.Memory; + private wasi_farm_refs_object: Array; + private worker_url: string; + private worker_background_ref: WorkerBackgroundRef; + private worker_background_ref_object: WorkerBackgroundRefObject; + // inst_default_buffer_kept: WebAssembly.Memory; + + // hold the worker to prevent GC. + private worker_background_worker?: Worker; + private worker_background_worker_promise?: Promise; + + // https://github.com/rustwasm/wasm-pack/issues/479 + + constructor( + worker_url: string, + wasi_farm_refs_object: Array, + share_memory?: WebAssembly.Memory, + // 16MB for the time being. + // https://users.rust-lang.org/t/what-is-the-size-limit-of-threads-stack-in-rust/11867/3 + MIN_STACK = 16777216, + worker_background_ref_object?: WorkerBackgroundRefObject, + thread_spawn_wasm?: WebAssembly.Module, + // inst_default_buffer_kept?: WebAssembly.Memory, + ) { + this.worker_url = worker_url; + this.wasi_farm_refs_object = wasi_farm_refs_object; + + const min_initial_size = 1048576 / 65536; // Rust's default stack size is 1MB. + const initial_size = MIN_STACK / 65536; + if (initial_size < min_initial_size) { + throw new Error( + `The stack size must be at least ${min_initial_size} bytes.`, + ); + } + const max_memory = 1073741824 / 65536; // Rust's default maximum memory size is 1GB. + + // this.inst_default_buffer_kept = + // inst_default_buffer_kept || + // new WebAssembly.Memory({ + // initial: 1, + // maximum: max_memory, + // shared: true, + // }); + + this.share_memory = + share_memory || + // WebAssembly.Memory's 1 page is 65536 bytes. + new WebAssembly.Memory({ + initial: initial_size, + maximum: max_memory, + shared: true, + }); + + if (worker_background_ref_object === undefined) { + const worker_background_worker_url__ = worker_background_worker_url(); + this.worker_background_worker = new Worker( + worker_background_worker_url__, + { type: "module" }, + ); + URL.revokeObjectURL(worker_background_worker_url__); + const { promise, resolve } = Promise.withResolvers(); + this.worker_background_worker_promise = promise; + this.worker_background_worker.onmessage = () => { + this.worker_background_worker_promise = undefined; + resolve(); + }; + this.worker_background_ref_object = + WorkerBackgroundRefObjectConstructor(); + this.worker_background_ref = WorkerBackgroundRef.init_self( + this.worker_background_ref_object, + ); + this.worker_background_worker.postMessage({ + override_object: { + sl_object: this.get_object(), + thread_spawn_wasm, + }, + worker_background_ref_object: this.worker_background_ref_object, + }); + } else { + this.worker_background_ref = WorkerBackgroundRef.init_self( + worker_background_ref_object, + ); + } + } + + // This cannot blocking. + async wait_worker_background_worker(): Promise { + if (this.worker_background_worker_promise) { + const promise = this.worker_background_worker_promise; + + await promise; + + return; + } + return; + } + + check_worker_background_worker(): void { + if (this.worker_background_worker_promise) { + throw new Error("worker_background_worker is not ready."); + } + } + + thread_spawn( + start_arg: number, + args: Array, + env: Array, + fd_map: Array<[number, number]>, + extend_imports: boolean, + ): number { + const worker = this.worker_background_ref.new_worker( + this.worker_url, + { type: "module" }, + { + this_is_thread_spawn: true, + start_arg, + args, + env, + fd_map, + extend_imports, + }, + ); + + const thread_id = worker.get_id(); + + return thread_id; + } + + async async_start_on_thread( + args: Array, + env: Array, + extend_imports: boolean, + ): Promise { + if (!self.Worker.toString().includes("[native code]")) { + if (self.Worker.toString().includes("function")) { + console.warn("SubWorker(new Worker on Worker) is polyfilled maybe."); + } else { + throw new Error("SubWorker(new Worker on Worker) is not supported."); + } + } + + await this.worker_background_ref.async_start_on_thread( + this.worker_url, + { type: "module" }, + { + this_is_thread_spawn: true, + this_is_start: true, + args, + env, + extend_imports, + }, + ); + } + + block_start_on_thread( + args: Array, + env: Array, + extend_imports: boolean, + ): void { + if (!self.Worker.toString().includes("[native code]")) { + if (self.Worker.toString().includes("function")) { + console.warn("SubWorker(new Worker on Worker) is polyfilled maybe."); + } else { + throw new Error("SubWorker(new Worker on Worker) is not supported."); + } + } + + this.worker_background_ref.block_start_on_thread( + this.worker_url, + { type: "module" }, + { + this_is_thread_spawn: true, + this_is_start: true, + args, + env, + extend_imports, + }, + ); + } + + static init_self(sl: ThreadSpawnerObject): ThreadSpawner { + const thread_spawner = new ThreadSpawner( + sl.worker_url, + sl.wasi_farm_refs_object, + sl.share_memory, + undefined, + sl.worker_background_ref_object, + // undefined, + // sl.inst_default_buffer_kept, + ); + return thread_spawner; + } + + static init_self_with_worker_background_ref( + sl: ThreadSpawnerObject, + worker_background_ref_object: WorkerBackgroundRefObject, + ): ThreadSpawner { + const thread_spawner = new ThreadSpawner( + sl.worker_url, + sl.wasi_farm_refs_object, + sl.share_memory, + undefined, + worker_background_ref_object, + // undefined, + // sl.inst_default_buffer_kept, + ); + return thread_spawner; + } + + get_share_memory(): WebAssembly.Memory { + return this.share_memory; + } + + get_object(): ThreadSpawnerObject { + return { + share_memory: this.share_memory, + wasi_farm_refs_object: this.wasi_farm_refs_object, + worker_url: this.worker_url, + worker_background_ref_object: this.worker_background_ref_object, + // inst_default_buffer_kept: this.inst_default_buffer_kept, + }; + } + + done_notify(code: number): void { + this.worker_background_ref.done_notify(code); + } + + async async_wait_done_or_error(): Promise { + if (this.worker_background_worker === undefined) { + throw new Error("worker_background_worker is undefined."); + } + + return await this.worker_background_ref.async_wait_done_or_error(); + } + + block_wait_done_or_error(): number { + if (this.worker_background_worker === undefined) { + throw new Error("worker_background_worker is undefined."); + } + + return this.worker_background_ref.block_wait_done_or_error(); + } +} + +// send fd_map is not implemented yet. +// issue: the fd passed to the child process is different from the parent process. +export const thread_spawn_on_worker = async (msg: { + this_is_thread_spawn: boolean; + worker_id?: number; + start_arg: number; + worker_background_ref: WorkerBackgroundRefObject; + sl_object: ThreadSpawnerObject; + thread_spawn_wasm: WebAssembly.Module; + args: Array; + env: Array; + fd_map: [number, number][]; + extend_imports: boolean; + this_is_start?: boolean; +}): Promise => { + console.log("thread_spawn_on_worker", msg); + + if (msg.this_is_thread_spawn) { + if (msg.this_is_start) { + const thread_spawner = ThreadSpawner.init_self_with_worker_background_ref( + msg.sl_object, + msg.worker_background_ref, + ); + + const wasi = new WASIFarmAnimal( + msg.sl_object.wasi_farm_refs_object, + msg.args, + msg.env, + { + can_thread_spawn: true, + thread_spawn_worker_url: msg.sl_object.worker_url, + extend_imports: msg.extend_imports, + }, + undefined, + thread_spawner, + ); + + const imports: { + env: { + memory: WebAssembly.Memory; + }; + wasi: { + "thread-spawn": (start_arg: number) => number; + }; + wasi_snapshot_preview1: { + [key: string]: (...args: Array) => unknown; + }; + extend_imports?: { + [key: string]: (...args: Array) => unknown; + }; + } = { + env: { + memory: wasi.get_share_memory(), + }, + wasi: wasi.wasiThreadImport, + wasi_snapshot_preview1: wasi.wasiImport, + }; + + if (msg.extend_imports) { + imports.extend_imports = wasi.extendImport; + } + + const inst = await WebAssembly.instantiate( + msg.thread_spawn_wasm, + imports, + ); + + wasi.start( + inst as unknown as { + exports: { + memory: WebAssembly.Memory; + _start: () => unknown; + }; + }, + ); + + globalThis.postMessage({ + msg: "done", + }); + + return wasi; + } + + const { + worker_id: thread_id, + start_arg, + args, + env, + sl_object, + thread_spawn_wasm, + } = msg; + + console.log(`thread_spawn worker ${thread_id} start`); + + const thread_spawner = ThreadSpawner.init_self_with_worker_background_ref( + sl_object, + msg.worker_background_ref, + ); + + const override_fd_map: Array = new Array( + sl_object.wasi_farm_refs_object.length, + ); + + // Possibly null (undefined) + for (const fd_and_wasi_ref_n of msg.fd_map) { + // biome-ignore lint/suspicious/noDoubleEquals: + if (fd_and_wasi_ref_n == undefined) { + continue; + } + const [fd, wasi_ref_n] = fd_and_wasi_ref_n; + if (override_fd_map[wasi_ref_n] === undefined) { + override_fd_map[wasi_ref_n] = []; + } + override_fd_map[wasi_ref_n].push(fd); + } + + const wasi = new WASIFarmAnimal( + sl_object.wasi_farm_refs_object, + args, + env, + { + can_thread_spawn: true, + thread_spawn_worker_url: sl_object.worker_url, + extend_imports: msg.extend_imports, + hand_override_fd_map: msg.fd_map, + }, + override_fd_map, + thread_spawner, + ); + + const imports: { + env: { + memory: WebAssembly.Memory; + }; + wasi: { + "thread-spawn": (start_arg: number) => number; + }; + wasi_snapshot_preview1: { + [key: string]: (...args: Array) => unknown; + }; + extend_imports?: { + [key: string]: (...args: Array) => unknown; + }; + } = { + env: { + memory: wasi.get_share_memory(), + }, + wasi: wasi.wasiThreadImport, + // wasi_snapshot_preview1: wasi.wasiImport, + wasi_snapshot_preview1: strace(wasi.wasiImport, []), + }; + + if (msg.extend_imports) { + imports.extend_imports = wasi.extendImport; + } + + const inst = await WebAssembly.instantiate(thread_spawn_wasm, imports); + + globalThis.postMessage({ + msg: "ready", + }); + + try { + wasi.wasi_thread_start( + inst as unknown as { + exports: { + memory: WebAssembly.Memory; + wasi_thread_start: (thread_id: number, start_arg: number) => void; + }; + }, + thread_id, + start_arg, + ); + } catch (e) { + globalThis.postMessage({ + msg: "error", + error: e, + }); + + return wasi; + } + + globalThis.postMessage({ + msg: "done", + }); + + return wasi; + } +}; diff --git a/src/wasi_farm/shared_array_buffer/util.ts b/src/wasi_farm/shared_array_buffer/util.ts new file mode 100644 index 0000000..4ed230f --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/util.ts @@ -0,0 +1,68 @@ +export const get_func_name_from_number = (num: number): string => { + switch (num) { + case 7: + return "fd_advise"; + case 8: + return "fd_allocate"; + case 9: + return "fd_close"; + case 10: + return "fd_datasync"; + case 11: + return "fd_fdstat_get"; + case 12: + return "fd_fdstat_set_flags"; + case 13: + return "fd_fdstat_set_rights"; + case 14: + return "fd_filestat_get"; + case 15: + return "fd_filestat_set_size"; + case 16: + return "fd_filestat_set_times"; + case 17: + return "fd_pread"; + case 18: + return "fd_prestat_get"; + case 19: + return "fd_prestat_dir_name"; + case 20: + return "fd_pwrite"; + case 21: + return "fd_read"; + case 22: + return "fd_readdir"; + case 23: + return "fd_renumber"; + case 24: + return "fd_seek"; + case 25: + return "fd_sync"; + case 26: + return "fd_tell"; + case 27: + return "fd_write"; + case 28: + return "path_create_directory"; + case 29: + return "path_filestat_get"; + case 30: + return "path_filestat_set_times"; + case 31: + return "path_link"; + case 32: + return "path_open"; + case 33: + return "path_readlink"; + case 34: + return "path_remove_directory"; + case 35: + return "path_rename"; + case 36: + return "path_symlink"; + case 37: + return "path_unlink_file"; + default: + return "unknown"; + } +}; diff --git a/src/wasi_farm/shared_array_buffer/worker_background/index.ts b/src/wasi_farm/shared_array_buffer/worker_background/index.ts new file mode 100644 index 0000000..eb3e67c --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/index.ts @@ -0,0 +1,10 @@ +import type { WorkerBackgroundRefObject } from "./worker_export.js"; +import { WorkerBackgroundRef, WorkerRef } from "./worker_background_ref.js"; +import { url as worker_background_worker_url } from "./worker_blob.js"; + +export { + WorkerBackgroundRef, + WorkerRef, + type WorkerBackgroundRefObject, + worker_background_worker_url, +}; diff --git a/src/wasi_farm/shared_array_buffer/worker_background/minify.js b/src/wasi_farm/shared_array_buffer/worker_background/minify.js new file mode 100644 index 0000000..eecb571 --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/minify.js @@ -0,0 +1,46 @@ +import swc from "@swc/core"; + +import { readFileSync, writeFileSync } from "node:fs"; + +const old_code = readFileSync( + "./dist/workers/worker_background_worker.js", + "utf8", +); + +const { code } = await swc.minify(old_code, { + compress: { + reduce_funcs: true, + arguments: true, + booleans_as_integers: true, + hoist_funs: false, + keep_classnames: false, + unsafe: true, + }, + mangle: true, +}); + +writeFileSync( + "./dist/workers/worker_background_worker_minify.js", + code, + "utf8", +); + +// \n -> \\n + +const wrapper_code = `export const url = () => { + const code = + '${code.replace(/\\/g, "\\\\")}'; + + const blob = new Blob([code], { type: "application/javascript" }); + + const url = URL.createObjectURL(blob); + + return url; +}; +`; + +writeFileSync( + "./src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts", + wrapper_code, + "utf8", +); diff --git a/src/wasi_farm/shared_array_buffer/worker_background/spack.config.cjs b/src/wasi_farm/shared_array_buffer/worker_background/spack.config.cjs new file mode 100644 index 0000000..2b498de --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/spack.config.cjs @@ -0,0 +1,15 @@ +// https://swc.rs/docs/configuration/bundling + +const { config } = require("@swc/core/spack"); + +console.log(__dirname); + +module.exports = config({ + entry: { + web: `${__dirname}/worker.ts`, + }, + output: { + path: `${__dirname}/../../../../dist/workers/`, + name: "worker_background_worker.js", + }, +}); diff --git a/src/wasi_farm/shared_array_buffer/worker_background/worker.ts b/src/wasi_farm/shared_array_buffer/worker_background/worker.ts new file mode 100644 index 0000000..9db336e --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/worker.ts @@ -0,0 +1,290 @@ +// If you create a worker and try to increase the number of threads, +// you will have to use Atomics.wait because they need to be synchronized. +// However, this is essentially impossible because Atomics.wait blocks the threads. +// Therefore, a dedicated worker that creates a subworker (worker in worker) is prepared. +// The request is made using BroadcastChannel. + +import * as Serializer from "../../../serialize_error.js"; +import { AllocatorUseArrayBuffer } from "../allocator.js"; +import type { WorkerBackgroundRefObject } from "./worker_export.js"; + +// Note that postMessage, etc. +// cannot be used in a blocking environment such as during wasm execution. +// (at least as far as I have tried) + +class WorkerBackground { + private override_object: T; + private allocator: AllocatorUseArrayBuffer; + private lock: SharedArrayBuffer; + private signature_input: SharedArrayBuffer; + + private workers: Array = [undefined]; + + private start_worker?: Worker; + + private listen_holder: Promise; + + constructor( + override_object: T, + lock?: SharedArrayBuffer, + allocator?: AllocatorUseArrayBuffer, + signature_input?: SharedArrayBuffer, + ) { + this.override_object = override_object; + this.lock = lock ?? new SharedArrayBuffer(20); + this.allocator = + allocator ?? + new AllocatorUseArrayBuffer(new SharedArrayBuffer(10 * 1024)); + this.signature_input = signature_input ?? new SharedArrayBuffer(24); + this.listen_holder = this.listen(); + } + + static init_self( + override_object: T, + worker_background_ref_object: WorkerBackgroundRefObject, + ): WorkerBackground { + return new WorkerBackground( + override_object, + worker_background_ref_object.lock, + AllocatorUseArrayBuffer.init_self(worker_background_ref_object.allocator), + worker_background_ref_object.signature_input, + ); + } + + assign_worker_id(): number { + for (let i = 1; i < this.workers.length; i++) { + if (this.workers[i] === undefined) { + return i; + } + } + this.workers.push(undefined); + return this.workers.length - 1; + } + + ref(): WorkerBackgroundRefObject { + return { + allocator: this.allocator.get_object(), + lock: this.lock, + signature_input: this.signature_input, + }; + } + + async listen(): Promise { + const lock_view = new Int32Array(this.lock); + Atomics.store(lock_view, 0, 0); + Atomics.store(lock_view, 1, 0); + + const signature_input_view = new Int32Array(this.signature_input); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + let lock: "not-equal" | "timed-out" | "ok"; + + const { value } = Atomics.waitAsync(lock_view, 1, 0); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out"); + } + + const locked_value = Atomics.load(lock_view, 1); + if (locked_value !== 1) { + throw new Error("locked"); + } + + const signature_input = Atomics.load(signature_input_view, 0); + switch (signature_input) { + // create new worker + case 1: { + const url_ptr = Atomics.load(signature_input_view, 1); + const url_len = Atomics.load(signature_input_view, 2); + const url_buff = this.allocator.get_memory(url_ptr, url_len); + this.allocator.free(url_ptr, url_len); + const url = new TextDecoder().decode(url_buff); + const is_module = Atomics.load(signature_input_view, 3) === 1; + const worker = new Worker(url, { + type: is_module ? "module" : "classic", + }); + const json_ptr = Atomics.load(signature_input_view, 4); + const json_len = Atomics.load(signature_input_view, 5); + const json_buff = this.allocator.get_memory(json_ptr, json_len); + this.allocator.free(json_ptr, json_len); + const json = new TextDecoder().decode(json_buff); + const obj = JSON.parse(json); + + const worker_id = this.assign_worker_id(); + + console.log(`new worker ${worker_id}`); + + this.workers[worker_id] = worker; + + const { promise, resolve } = Promise.withResolvers(); + + worker.onmessage = async (e) => { + const { msg } = e.data; + + if (msg === "ready") { + resolve(); + } + + if (msg === "done") { + this.workers[worker_id].terminate(); + this.workers[worker_id] = undefined; + + console.log(`worker ${worker_id} done so terminate`); + } + + if (msg === "error") { + this.workers[worker_id].terminate(); + this.workers[worker_id] = undefined; + + let n = 0; + for (const worker of this.workers) { + if (worker !== undefined) { + worker.terminate(); + console.warn( + `wasi throw error but child process exists, terminate ${n}`, + ); + } + n++; + } + if (this.start_worker !== undefined) { + this.start_worker.terminate(); + console.warn( + "wasi throw error but wasi exists, terminate wasi", + ); + } + + this.workers = [undefined]; + this.start_worker = undefined; + + const error = e.data.error; + + const notify_view = new Int32Array(this.lock, 8); + + const serialized_error = Serializer.serialize(error); + + const [ptr, len] = await this.allocator.async_write( + new TextEncoder().encode(JSON.stringify(serialized_error)), + this.lock, + 3, + ); + + // notify error = code 1 + const old = Atomics.compareExchange(notify_view, 0, 0, 1); + + if (old !== 0) { + console.error("what happened?"); + + this.allocator.free(ptr, len); + + return; + } + + const num = Atomics.notify(notify_view, 0); + + if (num === 0) { + console.error(error); + + this.allocator.free(ptr, len); + + Atomics.store(notify_view, 0, 0); + } + } + }; + + worker.postMessage({ + ...this.override_object, + ...obj, + worker_id, + worker_background_ref: this.ref(), + }); + + await promise; + + Atomics.store(signature_input_view, 0, worker_id); + + break; + } + // create start + case 2: { + const url_ptr = Atomics.load(signature_input_view, 1); + const url_len = Atomics.load(signature_input_view, 2); + const url_buff = this.allocator.get_memory(url_ptr, url_len); + this.allocator.free(url_ptr, url_len); + const url = new TextDecoder().decode(url_buff); + const is_module = Atomics.load(signature_input_view, 3) === 1; + this.start_worker = new Worker(url, { + type: is_module ? "module" : "classic", + }); + const json_ptr = Atomics.load(signature_input_view, 4); + const json_len = Atomics.load(signature_input_view, 5); + const json_buff = this.allocator.get_memory(json_ptr, json_len); + this.allocator.free(json_ptr, json_len); + const json = new TextDecoder().decode(json_buff); + const obj = JSON.parse(json); + + this.start_worker.onmessage = async (e) => { + const { msg } = e.data; + + if (msg === "done") { + let n = 0; + for (const worker of this.workers) { + if (worker !== undefined) { + worker.terminate(); + console.warn(`wasi done but worker exists, terminate ${n}`); + } + n++; + } + + this.start_worker.terminate(); + this.start_worker = undefined; + + console.log("start worker done so terminate"); + } + }; + + this.start_worker.postMessage({ + ...this.override_object, + ...obj, + worker_background_ref: this.ref(), + }); + + break; + } + } + + const old_call_lock = Atomics.exchange(lock_view, 1, 0); + if (old_call_lock !== 1) { + throw new Error("Lock is already set"); + } + const num = Atomics.notify(lock_view, 1, 1); + if (num !== 1) { + if (num === 0) { + console.warn("notify failed, waiter is late"); + continue; + } + throw new Error(`notify failed: ${num}`); + } + } catch (e) { + console.error(e); + } + } + } +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let worker_background: WorkerBackground; + +globalThis.onmessage = (e: MessageEvent) => { + const { override_object, worker_background_ref_object } = e.data; + worker_background = WorkerBackground.init_self( + override_object, + worker_background_ref_object, + ); + postMessage("ready"); +}; diff --git a/src/wasi_farm/shared_array_buffer/worker_background/worker_background_ref.ts b/src/wasi_farm/shared_array_buffer/worker_background/worker_background_ref.ts new file mode 100644 index 0000000..4d570df --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/worker_background_ref.ts @@ -0,0 +1,311 @@ +import * as Serializer from "../../../serialize_error.js"; +import { AllocatorUseArrayBuffer } from "../allocator.js"; +import type { WorkerBackgroundRefObject } from "./worker_export.js"; + +export class WorkerBackgroundRef { + private allocator: AllocatorUseArrayBuffer; + private lock: SharedArrayBuffer; + private signature_input: SharedArrayBuffer; + + constructor( + allocator: AllocatorUseArrayBuffer, + lock: SharedArrayBuffer, + signature_input: SharedArrayBuffer, + ) { + this.allocator = allocator; + this.lock = lock; + this.signature_input = signature_input; + } + + private block_lock_base_func(): void { + const view = new Int32Array(this.lock); + // eslint-disable-next-line no-constant-condition + while (true) { + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private async async_lock_base_func(): Promise { + const view = new Int32Array(this.lock); + // eslint-disable-next-line no-constant-condition + while (true) { + let value: "timed-out" | "not-equal" | "ok"; + const { value: _value } = Atomics.waitAsync(view, 0, 1); + if (_value instanceof Promise) { + value = await _value; + } else { + value = _value; + } + if (value === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private call_base_func(): void { + const view = new Int32Array(this.lock); + const old = Atomics.exchange(view, 1, 1); + if (old !== 0) { + console.error("what happened?"); + } + Atomics.notify(view, 1, 1); + } + + // wait base_func + private block_wait_base_func(): void { + const view = new Int32Array(this.lock); + const lock = Atomics.wait(view, 1, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + } + + private async async_wait_base_func(): Promise { + const view = new Int32Array(this.lock); + let value: "timed-out" | "not-equal" | "ok"; + const { value: _value } = Atomics.waitAsync(view, 1, 1); + if (_value instanceof Promise) { + value = await _value; + } else { + value = _value; + } + if (value === "timed-out") { + throw new Error("timed-out lock"); + } + } + + // release base_func + private release_base_func(): void { + const view = new Int32Array(this.lock); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + new_worker( + url: string, + options?: WorkerOptions, + post_obj?: unknown, + ): WorkerRef { + this.block_lock_base_func(); + const view = new Int32Array(this.signature_input); + Atomics.store(view, 0, 1); + const url_buffer = new TextEncoder().encode(url); + this.allocator.block_write(url_buffer, this.signature_input, 1); + Atomics.store(view, 3, options.type === "module" ? 1 : 0); + const obj_json = JSON.stringify(post_obj); + const obj_buffer = new TextEncoder().encode(obj_json); + this.allocator.block_write(obj_buffer, this.signature_input, 4); + this.call_base_func(); + this.block_wait_base_func(); + + const id = Atomics.load(view, 0); + + this.release_base_func(); + + return new WorkerRef(id); + } + + async async_start_on_thread( + url: string, + options: WorkerOptions | undefined, + post_obj: unknown, + ) { + await this.async_lock_base_func(); + const view = new Int32Array(this.signature_input); + Atomics.store(view, 0, 2); + const url_buffer = new TextEncoder().encode(url); + await this.allocator.async_write(url_buffer, this.signature_input, 1); + Atomics.store(view, 3, options?.type === "module" ? 1 : 0); + const obj_json = JSON.stringify(post_obj); + const obj_buffer = new TextEncoder().encode(obj_json); + await this.allocator.async_write(obj_buffer, this.signature_input, 4); + this.call_base_func(); + await this.async_wait_base_func(); + + this.release_base_func(); + } + + block_start_on_thread( + url: string, + options: WorkerOptions | undefined, + post_obj: unknown, + ) { + this.block_lock_base_func(); + const view = new Int32Array(this.signature_input); + Atomics.store(view, 0, 2); + const url_buffer = new TextEncoder().encode(url); + this.allocator.block_write(url_buffer, this.signature_input, 1); + Atomics.store(view, 3, options?.type === "module" ? 1 : 0); + const obj_json = JSON.stringify(post_obj); + const obj_buffer = new TextEncoder().encode(obj_json); + this.allocator.block_write(obj_buffer, this.signature_input, 4); + this.call_base_func(); + this.block_wait_base_func(); + + this.release_base_func(); + } + + static init_self(sl: WorkerBackgroundRefObject): WorkerBackgroundRef { + return new WorkerBackgroundRef( + AllocatorUseArrayBuffer.init_self(sl.allocator), + sl.lock, + sl.signature_input, + ); + } + + done_notify(code: number): void { + const notify_view = new Int32Array(this.lock, 8); + + // notify done = code 2 + const old = Atomics.compareExchange(notify_view, 0, 0, 2); + + if (old !== 0) { + console.error("what happened?"); + + return; + } + + Atomics.store(notify_view, 1, code); + + const num = Atomics.notify(notify_view, 0); + + if (num === 0) { + Atomics.store(notify_view, 0, 0); + } + } + + async async_wait_done_or_error(): Promise { + const notify_view = new Int32Array(this.lock, 8); + + Atomics.store(notify_view, 0, 0); + + let value: "timed-out" | "not-equal" | "ok"; + const { value: _value } = Atomics.waitAsync(notify_view, 0, 0); + if (_value instanceof Promise) { + value = await _value; + } else { + value = _value; + } + + if (value === "timed-out") { + throw new Error("timed-out"); + } + + if (value === "not-equal") { + throw new Error("not-equal"); + } + + const code = Atomics.load(notify_view, 0); + + if (code === 2) { + const old = Atomics.compareExchange(notify_view, 0, 2, 0); + + const code = Atomics.load(notify_view, 1); + + if (old !== 2) { + console.error("what happened?"); + } + + return code; + } + + if (code !== 1) { + throw new Error("unknown code"); + } + + // get error + const ptr = Atomics.load(notify_view, 1); + const size = Atomics.load(notify_view, 2); + const error_buffer = this.allocator.get_memory(ptr, size); + const error_txt = new TextDecoder().decode(error_buffer); + const error_serialized = JSON.parse( + error_txt, + ) as Serializer.SerializedError; + const error = Serializer.deserialize(error_serialized); + + const old = Atomics.compareExchange(notify_view, 0, 1, 0); + + if (old !== 1) { + console.error("what happened?"); + } + + throw error; + } + + block_wait_done_or_error(): number { + const notify_view = new Int32Array(this.lock, 8); + + Atomics.store(notify_view, 0, 0); + + const value = Atomics.wait(notify_view, 0, 0); + + if (value === "timed-out") { + throw new Error("timed-out"); + } + + if (value === "not-equal") { + throw new Error("not-equal"); + } + + const code = Atomics.load(notify_view, 0); + + if (code === 2) { + const old = Atomics.compareExchange(notify_view, 0, 2, 0); + + const code = Atomics.load(notify_view, 1); + + if (old !== 2) { + console.error("what happened?"); + } + + return code; + } + + if (code !== 1) { + throw new Error("unknown code"); + } + + // get error + const ptr = Atomics.load(notify_view, 1); + const size = Atomics.load(notify_view, 2); + const error_buffer = this.allocator.get_memory(ptr, size); + const error_txt = new TextDecoder().decode(error_buffer); + const error_serialized = JSON.parse( + error_txt, + ) as Serializer.SerializedError; + const error = Serializer.deserialize(error_serialized); + + const old = Atomics.compareExchange(notify_view, 0, 1, 0); + + if (old !== 1) { + console.error("what happened?"); + } + + throw error; + } +} + +export class WorkerRef { + private id: number; + + constructor(id: number) { + this.id = id; + } + + get_id(): number { + return this.id; + } +} diff --git a/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts b/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts new file mode 100644 index 0000000..8a1adf3 --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts @@ -0,0 +1,10 @@ +export const url = () => { + const code = + 'let worker_background;function _define_property(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}let serialize=e=>({message:e.message,name:e.name,stack:e.stack,cause:e.cause});class AllocatorUseArrayBuffer{static init_self(e){return new AllocatorUseArrayBuffer(e.share_arrays_memory)}async async_write(e,r,t){let o=new Int32Array(this.share_arrays_memory);for(;;){let{value:s}=Atomics.waitAsync(o,0,1);if("timed-out"===(s instanceof Promise?await s:s))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let i=this.write_inner(e,r,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),i}}block_write(e,r,t){for(;;){let o=new Int32Array(this.share_arrays_memory);if("timed-out"===Atomics.wait(o,0,1))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let s=this.write_inner(e,r,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),s}}write_inner(e,r,t){let o,s;let i=new Int32Array(this.share_arrays_memory),a=new Uint8Array(this.share_arrays_memory);o=0===Atomics.add(i,1,1)?Atomics.store(i,2,12):Atomics.load(i,2);let n=this.share_arrays_memory.byteLength,c=e.byteLength,l=o+c;if(n{let{msg:r}=e.data;if("ready"===r&&y(),"done"===r&&(this.workers[w].terminate(),this.workers[w]=void 0,console.log(`worker ${w} done so terminate`)),"error"===r){this.workers[w].terminate(),this.workers[w]=void 0;let r=0;for(let e of this.workers)void 0!==e&&(e.terminate(),console.warn("wasi throw error but child process exists, terminate "+r)),r++;void 0!==this.start_worker&&(this.start_worker.terminate(),console.warn("wasi throw error but wasi exists, terminate wasi")),this.workers=[void 0],this.start_worker=void 0;let t=e.data.error,o=new Int32Array(this.lock,8),s=serialize(t),[i,a]=await this.allocator.async_write(new TextEncoder().encode(JSON.stringify(s)),this.lock,3),n=Atomics.compareExchange(o,0,0,1);if(0!==n){console.error("what happened?"),this.allocator.free(i,a);return}let c=Atomics.notify(o,0);0===c&&(console.error(t),this.allocator.free(i,a),Atomics.store(o,0,0))}},a.postMessage({...this.override_object,...m,worker_id:w,worker_background_ref:this.ref()}),await d,Atomics.store(r,0,w);break}case 2:{let e=Atomics.load(r,1),t=Atomics.load(r,2),o=this.allocator.get_memory(e,t);this.allocator.free(e,t);let s=new TextDecoder().decode(o),i=1===Atomics.load(r,3);this.start_worker=new Worker(s,{type:i?"module":"classic"});let a=Atomics.load(r,4),n=Atomics.load(r,5),c=this.allocator.get_memory(a,n);this.allocator.free(a,n);let l=new TextDecoder().decode(c),h=JSON.parse(l);this.start_worker.onmessage=async e=>{let{msg:r}=e.data;if("done"===r){let e=0;for(let r of this.workers)void 0!==r&&(r.terminate(),console.warn("wasi done but worker exists, terminate "+e)),e++;this.start_worker.terminate(),this.start_worker=void 0,console.log("start worker done so terminate")}},this.start_worker.postMessage({...this.override_object,...h,worker_background_ref:this.ref()})}}let i=Atomics.exchange(e,1,0);if(1!==i)throw Error("Lock is already set");let a=Atomics.notify(e,1,1);if(1!==a){if(0===a){console.warn("notify failed, waiter is late");continue}throw Error("notify failed: "+a)}}catch(e){console.error(e)}}constructor(e,r,t,o){_define_property(this,"override_object",void 0),_define_property(this,"allocator",void 0),_define_property(this,"lock",void 0),_define_property(this,"signature_input",void 0),_define_property(this,"workers",[void 0]),_define_property(this,"start_worker",void 0),_define_property(this,"listen_holder",void 0),this.override_object=e,this.lock=r??new SharedArrayBuffer(20),this.allocator=t??new AllocatorUseArrayBuffer(new SharedArrayBuffer(10240)),this.signature_input=o??new SharedArrayBuffer(24),this.listen_holder=this.listen()}};globalThis.onmessage=e=>{let{override_object:r,worker_background_ref_object:t}=e.data;worker_background=WorkerBackground.init_self(r,t),postMessage("ready")};'; + + const blob = new Blob([code], { type: "application/javascript" }); + + const url = URL.createObjectURL(blob); + + return url; +}; diff --git a/src/wasi_farm/shared_array_buffer/worker_background/worker_export.ts b/src/wasi_farm/shared_array_buffer/worker_background/worker_export.ts new file mode 100644 index 0000000..9f831a8 --- /dev/null +++ b/src/wasi_farm/shared_array_buffer/worker_background/worker_export.ts @@ -0,0 +1,18 @@ +import type { AllocatorUseArrayBufferObject } from "../allocator.js"; + +export type WorkerBackgroundRefObject = { + allocator: AllocatorUseArrayBufferObject; + lock: SharedArrayBuffer; + signature_input: SharedArrayBuffer; +}; + +export const WorkerBackgroundRefObjectConstructor = + (): WorkerBackgroundRefObject => { + return { + allocator: { + share_arrays_memory: new SharedArrayBuffer(10 * 1024), + }, + lock: new SharedArrayBuffer(20), + signature_input: new SharedArrayBuffer(24), + }; + }; diff --git a/test/run-testsuite.sh b/test/run-testsuite.sh old mode 100755 new mode 100644