From 5109a407c417bcb0445b13a62fef47ceeb44dfac Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 12:14:21 +0100 Subject: [PATCH 01/56] WASIX Preparation This commit extracts changes to core libraries made in the WASIX branch. It is not reasonable to extract the partial commit history, so this is just a batch commit. The history will make a bit more sense again if we decide to merge the WASIX branch with full commit history. --- .github/workflows/lint.yaml | 2 +- Cargo.lock | 246 +++++++------- lib/api/Cargo.toml | 3 +- lib/api/src/into_bytes.rs | 50 +++ lib/api/src/js/export.rs | 80 ++++- lib/api/src/js/exports.rs | 3 + lib/api/src/js/externals/function.rs | 62 +++- lib/api/src/js/externals/memory.rs | 64 +++- lib/api/src/js/externals/memory_view.rs | 42 ++- lib/api/src/js/externals/mod.rs | 2 +- lib/api/src/js/imports.rs | 56 ++-- lib/api/src/js/instance.rs | 34 +- lib/api/src/js/mem_access.rs | 18 + lib/api/src/js/mod.rs | 4 +- lib/api/src/js/module.rs | 313 ++++++++++++------ lib/api/src/js/native.rs | 36 +- lib/api/src/js/store.rs | 63 ++++ lib/api/src/js/trap.rs | 29 ++ lib/api/src/js/types.rs | 6 + lib/api/src/js/value.rs | 10 +- lib/api/src/js/wasm_bindgen_polyfill.rs | 2 + lib/api/src/lib.rs | 3 + lib/api/src/sys/exports.rs | 3 + lib/api/src/sys/externals/function.rs | 146 ++++++-- lib/api/src/sys/externals/global.rs | 1 + lib/api/src/sys/externals/memory.rs | 32 ++ lib/api/src/sys/externals/memory_view.rs | 31 ++ lib/api/src/sys/externals/table.rs | 5 +- lib/api/src/sys/imports.rs | 62 ++++ lib/api/src/sys/instance.rs | 42 ++- lib/api/src/sys/mem_access.rs | 18 + lib/api/src/sys/mod.rs | 2 +- lib/api/src/sys/module.rs | 57 +--- lib/api/src/sys/native.rs | 121 ++++++- lib/api/src/sys/native_type.rs | 4 +- lib/api/src/sys/ptr.rs | 19 +- lib/api/src/sys/store.rs | 70 +++- lib/api/src/sys/tunables.rs | 10 + lib/api/src/sys/value.rs | 8 +- lib/api/tests/reference_types.rs | 9 +- lib/c-api/Cargo.toml | 2 +- .../wasmer-capi-examples-runner/src/lib.rs | 7 +- lib/c-api/src/wasm_c_api/wasi/mod.rs | 17 +- .../tests/wasmer-c-api-test-runner/src/lib.rs | 4 +- lib/cache/src/filesystem.rs | 8 +- lib/cli-compiler/src/commands/compile.rs | 1 + lib/compiler-cranelift/Cargo.toml | 1 + lib/compiler-cranelift/build.rs | 2 +- lib/compiler-cranelift/src/compiler.rs | 4 + .../src/translator/func_translator.rs | 4 +- lib/compiler-llvm/src/compiler.rs | 4 + lib/compiler-singlepass/src/compiler.rs | 4 + .../src/artifact_builders/artifact_builder.rs | 15 +- .../src/artifact_builders/trampoline.rs | 4 +- lib/compiler/src/compiler.rs | 3 + lib/compiler/src/engine/artifact.rs | 6 +- lib/compiler/src/engine/code_memory.rs | 2 +- lib/compiler/src/engine/inner.rs | 22 +- lib/compiler/src/engine/trap/error.rs | 4 +- lib/compiler/src/engine/tunables.rs | 89 +++++ lib/compiler/src/traits.rs | 2 +- lib/emscripten/src/lib.rs | 25 +- lib/emscripten/src/memory.rs | 2 + lib/types/src/lib.rs | 5 +- lib/types/src/memory.rs | 5 +- lib/types/src/serialize.rs | 5 +- lib/types/src/store.rs | 72 ++++ lib/types/src/trapcode.rs | 12 + lib/vm/Cargo.toml | 3 + lib/vm/src/export.rs | 5 + lib/vm/src/extern_ref.rs | 5 +- lib/vm/src/function_env.rs | 4 + lib/vm/src/global.rs | 39 ++- lib/vm/src/instance/allocator.rs | 3 +- lib/vm/src/memory.rs | 97 +++++- lib/vm/src/mmap.rs | 205 +++++++++++- lib/vm/src/store.rs | 25 +- lib/vm/src/table.rs | 13 + rust-toolchain | 2 +- tests/compilers/traps.rs | 2 +- tests/compilers/typed_functions.rs | 6 +- 81 files changed, 2008 insertions(+), 500 deletions(-) create mode 100644 lib/api/src/into_bytes.rs create mode 100644 lib/types/src/store.rs diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 65ef0043af1..dc5fee64890 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -23,7 +23,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 components: rustfmt, clippy - name: Install LLVM (Linux) run: | diff --git a/Cargo.lock b/Cargo.lock index 068b0c26887..dfd8d3ad9a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -137,7 +137,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.5.4", + "miniz_oxide", "object 0.29.0", "rustc-demangle", ] @@ -171,9 +171,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake3" -version = "1.3.3" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" dependencies = [ "arrayref", "arrayvec", @@ -254,9 +254,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "bytesize" @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.77" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" dependencies = [ "jobserver", ] @@ -493,9 +493,9 @@ checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation-sys" @@ -670,22 +670,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", ] @@ -740,9 +740,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.83" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" dependencies = [ "cc", "cxxbridge-flags", @@ -752,9 +752,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.83" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" dependencies = [ "cc", "codespan-reporting", @@ -767,15 +767,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.83" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" +checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" [[package]] name = "cxxbridge-macro" -version = "1.0.83" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" dependencies = [ "proc-macro2", "quote", @@ -829,9 +829,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a16495aeb28047bb1185fca837baf755e7d71ed3aeed7f8504654ffa927208" +checksum = "4903dff04948f22033ca30232ab8eca2c3fc4c913a8b6a34ee5199699814817f" dependencies = [ "proc-macro2", "quote", @@ -1080,7 +1080,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" dependencies = [ - "memoffset 0.6.5", + "memoffset", "rustc_version 0.3.3", ] @@ -1098,12 +1098,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] @@ -1533,9 +1533,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" dependencies = [ "http", "hyper", @@ -1603,9 +1603,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -1690,9 +1690,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" [[package]] name = "isatty" @@ -1768,9 +1768,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.138" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libfuzzer-sys" @@ -1914,15 +1914,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.16" @@ -1980,15 +1971,6 @@ dependencies = [ "adler", ] -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "mio" version = "0.8.5" @@ -2018,27 +2000,27 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nix" -version = "0.24.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", - "memoffset 0.6.5", + "memoffset", ] [[package]] name = "nix" -version = "0.25.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ "autocfg", "bitflags", "cfg-if 1.0.0", "libc", - "memoffset 0.6.5", + "memoffset", "pin-utils", ] @@ -2128,9 +2110,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "orbclient" -version = "0.3.42" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba683f1641c11041c59d5d93689187abcab3c1349dc6d9d70c550c9f9360802f" +checksum = "b13aaa572be9c80bf827187cd4a188746bf2edb95368362d8ff3c785ff38c3ff" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2144,9 +2126,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" [[package]] name = "output_vt100" @@ -2202,9 +2184,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" +checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" dependencies = [ "thiserror", "ucd-trie", @@ -2264,9 +2246,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "2.1.4" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54fc5dc63ed3bbf19494623db4f3af16842c0d975818e469022d09e53f0aa05" +checksum = "ed6bd09a7f7e68f3f0bf710fb7ab9c4615a488b58b5f653382a687701e458c92" dependencies = [ "difflib", "float-cmp", @@ -2500,19 +2482,21 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ + "autocfg", + "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -2666,7 +2650,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.22.6", + "webpki-roots 0.22.5", "winreg", ] @@ -2924,9 +2908,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.150" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] @@ -2963,9 +2947,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.150" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", @@ -2974,9 +2958,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ "itoa 1.0.4", "ryu", @@ -3213,9 +3197,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.105" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -3460,9 +3444,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.23.0" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ "autocfg", "bytes", @@ -3472,7 +3456,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2", - "windows-sys 0.42.0", + "winapi", ] [[package]] @@ -3582,9 +3566,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "trybuild" -version = "1.0.72" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db29f438342820400f2d9acfec0d363e987a38b2950bdb50a7069ed17b2148ee" +checksum = "ea496675d71016e9bc76aa42d87f16aefd95447cc5818e671e12b2d7e269075d" dependencies = [ "glob", "once_cell", @@ -3597,9 +3581,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "typetag" @@ -3919,9 +3903,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.20.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +checksum = "9424cdab516a16d4ea03c8f4a01b14e7b2d04a129dcc2bcdde5bcc5f68f06c41" dependencies = [ "leb128", ] @@ -3945,6 +3929,7 @@ dependencies = [ "anyhow", "bytes", "cfg-if 1.0.0", + "derivative", "hashbrown 0.11.2", "indexmap", "js-sys", @@ -4012,7 +3997,7 @@ dependencies = [ "wasmer-types", "wasmer-vfs", "wasmer-wasi", - "webc", + "webc 0.4.1", ] [[package]] @@ -4100,7 +4085,7 @@ dependencies = [ "wasmer-wasi", "wasmer-wasi-experimental-io-devices", "wasmer-wast", - "webc", + "webc 3.0.1", ] [[package]] @@ -4327,7 +4312,7 @@ dependencies = [ "toml", "url", "wapm-toml", - "webc", + "webc 3.0.1", "whoami", ] @@ -4338,7 +4323,7 @@ dependencies = [ "enum-iterator", "enumset", "indexmap", - "memoffset 0.6.5", + "memoffset", "more-asserts", "rkyv", "serde", @@ -4366,7 +4351,7 @@ dependencies = [ "thiserror", "tracing", "typetag", - "webc", + "webc 3.0.1", ] [[package]] @@ -4377,17 +4362,19 @@ dependencies = [ "cc", "cfg-if 1.0.0", "corosensei", + "derivative", "enum-iterator", "indexmap", "lazy_static", "libc", "mach", - "memoffset 0.6.5", + "memoffset", "more-asserts", "region", "scopeguard", "serde", "thiserror", + "tracing", "wasmer-types", "winapi", ] @@ -4429,7 +4416,7 @@ dependencies = [ "wasmer-vnet", "wasmer-wasi-local-networking", "wasmer-wasi-types", - "webc", + "webc 3.0.1", "winapi", ] @@ -4438,7 +4425,7 @@ name = "wasmer-wasi-experimental-io-devices" version = "3.1.0" dependencies = [ "minifb", - "nix 0.25.1", + "nix 0.25.0", "ref_thread_local", "serde", "tracing", @@ -4594,22 +4581,21 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wasmparser" -version = "0.95.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" +checksum = "cdac7e1d98d70913ae3b4923dd7419c8ea7bdfd4c44a240a0ba305d929b7f191" dependencies = [ "indexmap", - "url", ] [[package]] name = "wasmprinter" -version = "0.2.44" +version = "0.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae24500f9cc27a4b2b338e66693ff53c08b17cf920bdc81e402a09fe7a204eea" +checksum = "9c093ddb9e6526cc59d93032b9be7a8d014cc997e8a9372f394c9624f820d209" dependencies = [ "anyhow", - "wasmparser 0.95.0", + "wasmparser 0.94.0", ] [[package]] @@ -4632,23 +4618,23 @@ dependencies = [ [[package]] name = "wast" -version = "50.0.0" +version = "49.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" +checksum = "05ef81fcd60d244cafffeafac3d17615fdb2fddda6aca18f34a8ae233353587c" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.20.0", + "wasm-encoder 0.19.1", ] [[package]] name = "wat" -version = "1.0.52" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +checksum = "4c347c4460ffb311e95aafccd8c29e4888f241b9e4b3bb0e0ccbd998de2c8c0d" dependencies = [ - "wast 50.0.0", + "wast 49.0.0", ] [[package]] @@ -4660,7 +4646,7 @@ dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.24.3", + "nix 0.24.2", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -4673,7 +4659,7 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ - "nix 0.24.3", + "nix 0.24.2", "once_cell", "smallvec", "wayland-sys", @@ -4685,7 +4671,7 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ - "nix 0.24.3", + "nix 0.24.2", "wayland-client", "xcursor", ] @@ -4734,6 +4720,28 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webc" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cbb86b1e6e0b9bcd03b5e530987bb18fbef8dcac56c3f238039be085ce1b40d" +dependencies = [ + "anyhow", + "base64", + "indexmap", + "leb128", + "lexical-sort", + "memchr", + "path-clean", + "rand 0.8.5", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "url", + "walkdir", +] + [[package]] name = "webc" version = "3.0.1" @@ -4788,9 +4796,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki 0.22.0", ] @@ -4948,9 +4956,9 @@ dependencies = [ [[package]] name = "x11-dl" -version = "2.20.1" +version = "2.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1536d6965a5d4e573c7ef73a2c15ebcd0b2de3347bdf526c34c297c00ac40f0" +checksum = "0c83627bc137605acc00bb399c7b908ef460b621fc37c953db2b09f88c449ea6" dependencies = [ "lazy_static", "libc", diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 764b54afd94..c6b700301cd 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -26,6 +26,7 @@ indexmap = { version = "1.6" } cfg-if = "1.0" thiserror = "1.0" more-asserts = "0.2" +derivative = { version = "^2" } bytes = "1" # - Optional shared dependencies. wat = { version = "1.0", optional = true } @@ -117,7 +118,7 @@ jit = ["engine"] js = ["wasm-bindgen", "js-sys"] js-default = ["js", "std", "wasm-types-polyfill"] -wasm-types-polyfill = ["js", "wasmparser"] +wasm-types-polyfill = ["js", "wasmparser", "js-serializable-module" ] js-serializable-module = [] diff --git a/lib/api/src/into_bytes.rs b/lib/api/src/into_bytes.rs new file mode 100644 index 00000000000..2016036819d --- /dev/null +++ b/lib/api/src/into_bytes.rs @@ -0,0 +1,50 @@ +use bytes::Bytes; +use std::borrow::Cow; + +/// Convert binary data into [`bytes::Bytes`]. +pub trait IntoBytes { + /// Convert binary data into [`bytes::Bytes`]. + fn into_bytes(self) -> Bytes; +} + +impl IntoBytes for Bytes { + fn into_bytes(self) -> Bytes { + self + } +} + +impl IntoBytes for Vec { + fn into_bytes(self) -> Bytes { + Bytes::from(self) + } +} + +impl IntoBytes for &Vec { + fn into_bytes(self) -> Bytes { + Bytes::from(self.clone()) + } +} + +impl IntoBytes for &[u8] { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} + +impl IntoBytes for &[u8; N] { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} + +impl IntoBytes for &str { + fn into_bytes(self) -> Bytes { + Bytes::from(self.as_bytes().to_vec()) + } +} + +impl IntoBytes for Cow<'_, [u8]> { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} diff --git a/lib/api/src/js/export.rs b/lib/api/src/js/export.rs index a8cb689ed02..1cf18286a83 100644 --- a/lib/api/src/js/export.rs +++ b/lib/api/src/js/export.rs @@ -1,12 +1,18 @@ use crate::js::error::WasmError; use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle}; use crate::js::wasm_bindgen_polyfill::Global; +use crate::MemoryView; use js_sys::Function; use js_sys::WebAssembly::{Memory, Table}; use serde::{Deserialize, Serialize}; use std::fmt; +#[cfg(feature = "tracing")] +use tracing::trace; use wasm_bindgen::{JsCast, JsValue}; -use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType, WASM_PAGE_SIZE}; +use wasmer_types::{ + ExternType, FunctionType, GlobalType, MemoryError, MemoryType, Pages, StoreSnapshot, TableType, + WASM_PAGE_SIZE, +}; /// Represents linear memory that is managed by the javascript runtime #[derive(Clone, Debug, PartialEq)] @@ -25,7 +31,8 @@ struct DummyBuffer { } impl VMMemory { - pub(crate) fn new(memory: Memory, ty: MemoryType) -> Self { + /// Creates a new memory directly from a WebAssembly javascript object + pub fn new(memory: Memory, ty: MemoryType) -> Self { Self { memory, ty } } @@ -45,6 +52,52 @@ impl VMMemory { pub(crate) fn try_clone(&self) -> Option { Some(self.clone()) } + + /// Copies this memory to a new memory + pub fn fork(&self) -> Result { + let new_memory = crate::Memory::new_internal(self.ty.clone())?; + + #[cfg(feature = "tracing")] + trace!("memory copy started"); + + let src = MemoryView::new_raw(&self.memory); + let amount = src.data_size() as usize; + let mut dst = MemoryView::new_raw(&new_memory); + let dst_size = dst.data_size() as usize; + + if amount > dst_size { + let delta = amount - dst_size; + let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + + let our_js_memory: &crate::js::externals::memory::JSMemory = + JsCast::unchecked_from_js_ref(&new_memory); + our_js_memory.grow(pages as u32).map_err(|err| { + if err.is_instance_of::() { + let cur_pages = dst_size; + MemoryError::CouldNotGrow { + current: Pages(cur_pages as u32), + attempted_delta: Pages(pages as u32), + } + } else { + MemoryError::Generic(err.as_string().unwrap()) + } + })?; + + dst = MemoryView::new_raw(&new_memory); + } + + src.copy_to_memory(amount as u64, &dst).map_err(|err| { + wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err)) + })?; + + #[cfg(feature = "tracing")] + trace!("memory copy finished (size={})", dst.size().bytes().0); + + Ok(Self { + memory: new_memory, + ty: self.ty.clone(), + }) + } } #[derive(Clone, Debug, PartialEq)] @@ -57,6 +110,29 @@ impl VMGlobal { pub(crate) fn new(global: Global, ty: GlobalType) -> Self { Self { global, ty } } + + /// Saves the global value into the snapshot + pub fn save_snapshot(&self, index: usize, snapshot: &mut StoreSnapshot) { + if let Some(val) = self.global.as_f64() { + let entry = snapshot.globals.entry(index as u32).or_default(); + *entry = val as u128; + } + } + + /// Restores the global value from the snapshot + pub fn restore_snapshot(&mut self, index: usize, snapshot: &StoreSnapshot) { + let index = index as u32; + if let Some(entry) = snapshot.globals.get(&index) { + if let Some(existing) = self.global.as_f64() { + let existing = existing as u128; + if existing == *entry { + return; + } + } + let value = JsValue::from_f64(*entry as _); + self.global.set_value(&value); + } + } } unsafe impl Send for VMGlobal {} diff --git a/lib/api/src/js/exports.rs b/lib/api/src/js/exports.rs index 024ab8b9696..339095bafa0 100644 --- a/lib/api/src/js/exports.rs +++ b/lib/api/src/js/exports.rs @@ -53,6 +53,9 @@ pub enum ExportError { /// This error arises when an export is missing #[error("Missing export {0}")] Missing(String), + /// This error arises when an export is missing + #[error("Serialization failed {0}")] + SerializationFailed(String), } /// Exports is a special kind of map that allows easily unwrapping diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 770c462ae60..92f1388dd61 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -61,6 +61,12 @@ pub struct Function { pub(crate) handle: StoreHandle, } +impl Into for StoreHandle { + fn into(self) -> Function { + Function { handle: self } + } +} + impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// @@ -393,6 +399,13 @@ impl Function { store: &mut impl AsStoreMut, params: &[Value], ) -> Result, RuntimeError> { + #[allow(unused_unsafe)] + let params: Vec<_> = unsafe { + params + .iter() + .map(|a| a.as_raw_value(&store.as_store_ref())) + .collect() + }; let arr = js_sys::Array::new_with_length(params.len() as u32); // let raw_env = env.as_raw() as *mut u8; @@ -402,11 +415,34 @@ impl Function { let js_value = param.as_jsvalue(&store.as_store_ref()); arr.set(i as u32, js_value); } - let result = js_sys::Reflect::apply( - &self.handle.get(store.as_store_ref().objects()).function, - &wasm_bindgen::JsValue::NULL, - &arr, - )?; + + let result = { + let mut r; + loop { + r = js_sys::Reflect::apply( + &self.handle.get(store.as_store_ref().objects()).function, + &wasm_bindgen::JsValue::NULL, + &arr, + ); + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { + continue; + } + Ok(wasmer_types::OnCalledAction::Finish) => { + break; + } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { + return Err(RuntimeError::user(trap)) + } + Err(trap) => return Err(RuntimeError::user(trap)), + } + } + break; + } + r? + }; let result_types = self.handle.get(store.as_store_ref().objects()).ty.results(); match result_types.len() { @@ -1134,16 +1170,18 @@ mod inner { T: Send + 'static, Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, { - // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; - let func: &Func = &*(&() as *const () as *const Func); let mut store = StoreMut::from_raw(store_ptr as *mut _); let mut store2 = StoreMut::from_raw(store_ptr as *mut _); - let result = panic::catch_unwind(AssertUnwindSafe(|| { - let handle: StoreHandle = StoreHandle::from_internal(store2.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); - let env: FunctionEnvMut = FunctionEnv::from_handle(handle).into_mut(&mut store2); - func(env, $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() - })); + let result = { + // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; + let func: &Func = &*(&() as *const () as *const Func); + panic::catch_unwind(AssertUnwindSafe(|| { + let handle: StoreHandle = StoreHandle::from_internal(store2.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); + let env: FunctionEnvMut = FunctionEnv::from_handle(handle).into_mut(&mut store2); + func(env, $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() + })) + }; match result { Ok(Ok(result)) => return result.into_c_struct(&mut store), diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index b57639b4897..5678e5c3e7b 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -11,7 +11,7 @@ use tracing::warn; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use wasmer_types::Pages; +use wasmer_types::{Pages, WASM_PAGE_SIZE}; use super::MemoryView; @@ -86,19 +86,26 @@ impl Memory { /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); /// ``` pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + let vm_memory = VMMemory::new(Self::new_internal(ty.clone())?, ty); + Ok(Self::from_vm_export(store, vm_memory)) + } + + pub(crate) fn new_internal(ty: MemoryType) -> Result { let descriptor = js_sys::Object::new(); - js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); - if let Some(max) = ty.maximum { - js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into()).unwrap(); + #[allow(unused_unsafe)] + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); + if let Some(max) = ty.maximum { + js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into()).unwrap(); + } + js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into()).unwrap(); } - js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into()).unwrap(); let js_memory = js_sys::WebAssembly::Memory::new(&descriptor) .map_err(|_e| MemoryError::Generic("Error while creating the memory".to_owned()))?; - let vm_memory = VMMemory::new(js_memory, ty); - let handle = StoreHandle::new(store.objects_mut(), vm_memory); - Ok(Self::from_vm_extern(store, handle.internal_handle())) + Ok(js_memory) } /// Creates a new host `Memory` from provided JavaScript memory. @@ -200,6 +207,41 @@ impl Memory { Ok(Pages(new_pages)) } + /// Copies the memory to a new store and returns a memory reference to it + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + // Create the new memory using the parameters of the existing memory + let view = self.view(store); + let ty = self.ty(store); + let amount = view.data_size() as usize; + + let new_memory = Self::new(new_store, ty)?; + let mut new_view = new_memory.view(&new_store); + let new_view_size = new_view.data_size() as usize; + if amount > new_view_size { + let delta = amount - new_view_size; + let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + new_memory.grow(new_store, Pages(pages as u32))?; + new_view = new_memory.view(&new_store); + } + + // Copy the bytes + view.copy_to_memory(amount as u64, &new_view) + .map_err(|err| MemoryError::Generic(err.to_string()))?; + + // Return the new memory + Ok(new_memory) + } + + pub(crate) fn from_vm_export(store: &mut impl AsStoreMut, vm_memory: VMMemory) -> Self { + Self { + handle: StoreHandle::new(store.objects_mut(), vm_memory), + } + } + pub(crate) fn from_vm_extern( store: &mut impl AsStoreMut, internal: InternalStoreHandle, @@ -221,6 +263,12 @@ impl Memory { pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } + + /// Copies this memory to a new memory + pub fn fork(&mut self, store: &impl AsStoreRef) -> Result { + let mem = self.handle.get(store.as_store_ref().objects()); + mem.fork() + } } impl std::cmp::PartialEq for Memory { diff --git a/lib/api/src/js/externals/memory_view.rs b/lib/api/src/js/externals/memory_view.rs index a219572207e..d4eb10e44f7 100644 --- a/lib/api/src/js/externals/memory_view.rs +++ b/lib/api/src/js/externals/memory_view.rs @@ -27,11 +27,12 @@ pub struct MemoryView<'a> { impl<'a> MemoryView<'a> { pub(crate) fn new(memory: &Memory, store: &impl AsStoreRef) -> Self { - let buffer = memory - .handle - .get(store.as_store_ref().objects()) - .memory - .buffer(); + let memory = memory.handle.get(store.as_store_ref().objects()); + Self::new_raw(&memory.memory) + } + + pub(crate) fn new_raw(memory: &js_sys::WebAssembly::Memory) -> Self { + let buffer = memory.buffer(); let size = js_sys::Reflect::get(&buffer, &"byteLength".into()) .unwrap() @@ -250,4 +251,35 @@ impl<'a> MemoryView<'a> { view.set_index(offset, val); Ok(()) } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < self.data_size() { + let remaining = self.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } } diff --git a/lib/api/src/js/externals/mod.rs b/lib/api/src/js/externals/mod.rs index 60ed02f1879..259a8e13c9e 100644 --- a/lib/api/src/js/externals/mod.rs +++ b/lib/api/src/js/externals/mod.rs @@ -30,7 +30,7 @@ use js_sys::Function as JsFunction; use js_sys::WebAssembly::{Memory as JsMemory, Table as JsTable}; use std::fmt; use wasm_bindgen::{JsCast, JsValue}; -use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType}; +use wasmer_types::ExternType; /// The value of an export passed from one instance to another. pub enum VMExtern { diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs index fb82e9885c1..80235e2cc43 100644 --- a/lib/api/src/js/imports.rs +++ b/lib/api/src/js/imports.rs @@ -1,8 +1,7 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. -use crate::js::error::{InstantiationError, LinkError, WasmError}; -use crate::js::export::Export; +use crate::js::error::{LinkError, WasmError}; use crate::js::exports::Exports; use crate::js::module::Module; use crate::js::store::{AsStoreMut, AsStoreRef}; @@ -172,11 +171,17 @@ impl Imports { for (ns, exports) in namespaces.into_iter() { let import_namespace = js_sys::Object::new(); for (name, ext) in exports { - js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(store)) - .expect("Error while setting into the js namespace object"); + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(store)) + .expect("Error while setting into the js namespace object"); + } + } + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) + .expect("Error while setting into the js imports object"); } - js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) - .expect("Error while setting into the js imports object"); } imports } @@ -233,29 +238,40 @@ impl Imports { } impl AsJs for Imports { + #[allow(unused_unsafe)] fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { let imports_object = js_sys::Object::new(); for (namespace, name, extern_) in self.iter() { - let val = js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap(); + let val = unsafe { js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap() }; if !val.is_undefined() { // If the namespace is already set - js_sys::Reflect::set( - &val, - &name.into(), - &extern_.as_jsvalue(&store.as_store_ref()), - ) - .unwrap(); + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &val, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) + .unwrap(); + } } else { // If the namespace doesn't exist let import_namespace = js_sys::Object::new(); - js_sys::Reflect::set( - &import_namespace, - &name.into(), - &extern_.as_jsvalue(&store.as_store_ref()), - ) - .unwrap(); - js_sys::Reflect::set(&imports_object, &namespace.into(), &import_namespace.into()) + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &import_namespace, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) .unwrap(); + js_sys::Reflect::set( + &imports_object, + &namespace.into(), + &import_namespace.into(), + ) + .unwrap(); + } } } imports_object.into() diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index 5e1ddbf2e45..ff327e38d34 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -1,5 +1,4 @@ use crate::js::error::InstantiationError; -use crate::js::export::Export; use crate::js::exports::Exports; use crate::js::externals::Extern; use crate::js::imports::Imports; @@ -63,11 +62,11 @@ impl Instance { module: &Module, imports: &Imports, ) -> Result { - let instance: WebAssembly::Instance = module + let (instance, externs) = module .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; - let self_instance = Self::from_module_and_instance(store, module, instance)?; + let self_instance = Self::from_module_and_instance(store, module, externs, instance)?; //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; Ok(self_instance) } @@ -106,24 +105,43 @@ impl Instance { pub fn from_module_and_instance( mut store: &mut impl AsStoreMut, module: &Module, - instance: WebAssembly::Instance, + externs: Vec, + handle: StoreHandle, ) -> Result { use crate::js::externals::VMExtern; + + let instance = handle.get(store.objects_mut()); let instance_exports = instance.exports(); - let exports = module + + let mut exports = module .exports() .map(|export_type| { let name = export_type.name(); let extern_type = export_type.ty().clone(); - let js_export = js_sys::Reflect::get(&instance_exports, &name.into()) - .map_err(|_e| InstantiationError::NotInExports(name.to_string()))?; + #[allow(unused_unsafe)] + let js_export = unsafe { + js_sys::Reflect::get(&instance_exports, &name.into()) + .map_err(|_e| InstantiationError::NotInExports(name.to_string()))? + }; let export: VMExtern = VMExtern::from_js_value(js_export, &mut store, extern_type)?.into(); let extern_ = Extern::from_vm_extern(&mut store, export); Ok((name.to_string(), extern_)) }) .collect::>()?; - let handle = StoreHandle::new(store.as_store_mut().objects_mut(), instance); + + // If the memory is imported then also export it for backwards compatibility reasons + // (many will assume the memory is always exported) - later we can remove this + if exports.get_memory("memory").is_err() { + if let Some(memory) = externs + .iter() + .filter(|a| a.ty(store).memory().is_some()) + .next() + { + exports.insert("memory", memory.clone()); + } + } + Ok(Self { _handle: handle, module: module.clone(), diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index ac0a543436f..5a9142e48e4 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -328,6 +328,24 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } Ok(vec) } + + /// Reads this `WasmSlice` into a `BytesMut` + #[inline] + pub fn read_to_bytes(self) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut ret = bytes::BytesMut::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + ret.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + ret.set_len(len); + } + Ok(ret) + } } impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 13cc7a2504b..1404b6fff10 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -78,8 +78,8 @@ pub mod vm { pub use wasmer_types::is_wasm; pub use wasmer_types::{ - Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES, - WASM_MIN_PAGES, WASM_PAGE_SIZE, + Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, StoreSnapshot, + ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; #[cfg(feature = "wat")] diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 36c7f355361..6d003cabaa4 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -3,19 +3,23 @@ use crate::js::error::WasmError; use crate::js::error::{CompileError, InstantiationError}; #[cfg(feature = "js-serializable-module")] use crate::js::error::{DeserializeError, SerializeError}; +use crate::js::externals::Extern; use crate::js::imports::Imports; use crate::js::store::AsStoreMut; use crate::js::types::{AsJs, ExportType, ImportType}; use crate::js::RuntimeError; use crate::AsStoreRef; +use crate::IntoBytes; +#[cfg(feature = "js-serializable-module")] use bytes::Bytes; use js_sys::{Reflect, Uint8Array, WebAssembly}; -use std::borrow::Cow; use std::fmt; use std::io; use std::path::Path; #[cfg(feature = "std")] use thiserror::Error; +#[cfg(feature = "tracing")] +use tracing::{debug, warn}; use wasm_bindgen::JsValue; use wasmer_types::{ ExportsIterator, ExternType, FunctionType, GlobalType, ImportsIterator, MemoryType, Mutability, @@ -51,46 +55,6 @@ pub struct ModuleTypeHints { pub exports: Vec, } -pub trait IntoBytes { - fn into_bytes(self) -> Bytes; -} - -impl IntoBytes for Bytes { - fn into_bytes(self) -> Bytes { - self - } -} - -impl IntoBytes for Vec { - fn into_bytes(self) -> Bytes { - Bytes::from(self) - } -} - -impl IntoBytes for &[u8] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &[u8; N] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &str { - fn into_bytes(self) -> Bytes { - Bytes::from(self.as_bytes().to_vec()) - } -} - -impl IntoBytes for Cow<'_, [u8]> { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - /// A WebAssembly Module contains stateless WebAssembly /// code that has already been compiled and can be instantiated /// multiple times. @@ -170,15 +134,20 @@ impl Module { /// # } /// ``` #[allow(unreachable_code)] - pub fn new(_store: &impl AsStoreRef, bytes: impl AsRef<[u8]>) -> Result { + pub fn new(_store: &impl AsStoreRef, bytes: impl IntoBytes) -> Result { + #[allow(unused_mut)] + let mut bytes = bytes.into_bytes(); #[cfg(feature = "wat")] - let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { - CompileError::Wasm(WasmError::Generic(format!( - "Error when converting wat: {}", - e - ))) - })?; - Self::from_binary(_store, bytes.as_ref()) + if bytes.starts_with(b"\0asm") == false { + let parsed_bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { + CompileError::Wasm(WasmError::Generic(format!( + "Error when converting wat: {}", + e + ))) + })?; + bytes = Bytes::from(parsed_bytes.to_vec()); + } + Self::from_binary(_store, bytes) } /// Creates a new WebAssembly module from a file path. @@ -194,7 +163,11 @@ impl Module { /// Opposed to [`Module::new`], this function is not compatible with /// the WebAssembly text format (if the "wat" feature is enabled for /// this crate). - pub fn from_binary(_store: &impl AsStoreRef, binary: &[u8]) -> Result { + pub fn from_binary( + _store: &impl AsStoreRef, + binary: impl IntoBytes, + ) -> Result { + let binary = binary.into_bytes(); // // Self::validate(store, binary)?; unsafe { Self::from_binary_unchecked(_store, binary) } @@ -207,16 +180,34 @@ impl Module { /// This is safe since the JS vm should be safe already. /// We maintain the `unsafe` to preserve the same API as Wasmer pub unsafe fn from_binary_unchecked( - _store: &impl AsStoreRef, - binary: &[u8], + store: &impl AsStoreRef, + binary: impl IntoBytes, ) -> Result { - let js_bytes = Uint8Array::view(binary); + let binary = binary.into_bytes(); + let js_bytes = Uint8Array::view(&binary[..]); let module = WebAssembly::Module::new(&js_bytes.into()).unwrap(); + Self::from_js_module( + store, + module, + #[cfg(feature = "js-serializable-module")] + Bytes::from(binary), + ) + } + + /// Creates a new WebAssembly module skipping any kind of validation from a javascript module + /// + pub unsafe fn from_js_module( + _store: &impl AsStoreRef, + module: WebAssembly::Module, + #[cfg(feature = "js-serializable-module")] binary: impl IntoBytes, + ) -> Result { + #[cfg(feature = "js-serializable-module")] + let binary = binary.into_bytes(); // The module is now validated, so we can safely parse it's types #[cfg(feature = "wasm-types-polyfill")] let (type_hints, name) = { - let info = crate::js::module_info_polyfill::translate_module(binary).unwrap(); + let info = crate::js::module_info_polyfill::translate_module(&binary[..]).unwrap(); ( Some(ModuleTypeHints { @@ -254,9 +245,12 @@ impl Module { /// validation of the Module. pub fn validate(_store: &impl AsStoreRef, binary: &[u8]) -> Result<(), CompileError> { let js_bytes = unsafe { Uint8Array::view(binary) }; - match WebAssembly::validate(&js_bytes.into()) { - Ok(true) => Ok(()), - _ => Err(CompileError::Validate("Invalid Wasm file".to_owned())), + #[allow(unused_unsafe)] + unsafe { + match WebAssembly::validate(&js_bytes.into()) { + Ok(true) => Ok(()), + _ => Err(CompileError::Validate("Invalid Wasm file".to_owned())), + } } } @@ -264,7 +258,7 @@ impl Module { &self, store: &mut impl AsStoreMut, imports: &Imports, - ) -> Result { + ) -> Result<(crate::StoreHandle, Vec), RuntimeError> { // Ensure all imports come from the same store. if imports .into_iter() @@ -274,10 +268,71 @@ impl Module { InstantiationError::DifferentStores, ))); } - - let imports_js_obj = imports.as_jsvalue(store).into(); - Ok(WebAssembly::Instance::new(&self.module, &imports_js_obj) - .map_err(|e: JsValue| -> RuntimeError { e.into() })?) + let imports_object = js_sys::Object::new(); + let mut import_externs: Vec = vec![]; + for import_type in self.imports() { + let resolved_import = imports.get_export(import_type.module(), import_type.name()); + #[allow(unused_variables)] + if let wasmer_types::ExternType::Memory(mem_ty) = import_type.ty() { + if resolved_import.is_some() { + #[cfg(feature = "tracing")] + debug!("imported shared memory {:?}", &mem_ty); + } else { + #[cfg(feature = "tracing")] + warn!( + "Error while importing {0:?}.{1:?}: memory. Expected {2:?}", + import_type.module(), + import_type.name(), + import_type.ty(), + ); + } + } + #[allow(unused_unsafe)] + unsafe { + if let Some(import) = resolved_import { + let val = js_sys::Reflect::get(&imports_object, &import_type.module().into())?; + if !val.is_undefined() { + // If the namespace is already set + js_sys::Reflect::set( + &val, + &import_type.name().into(), + &import.as_jsvalue(&store.as_store_ref()), + )?; + } else { + // If the namespace doesn't exist + let import_namespace = js_sys::Object::new(); + js_sys::Reflect::set( + &import_namespace, + &import_type.name().into(), + &import.as_jsvalue(&store.as_store_ref()), + )?; + js_sys::Reflect::set( + &imports_object, + &import_type.module().into(), + &import_namespace.into(), + )?; + } + import_externs.push(import); + } else { + #[cfg(feature = "tracing")] + warn!( + "import not found {}:{}", + import_type.module(), + import_type.name() + ); + } + } + // in case the import is not found, the JS Wasm VM will handle + // the error for us, so we don't need to handle it + } + Ok(( + crate::StoreHandle::new( + store.as_store_mut().objects_mut(), + WebAssembly::Instance::new(&self.module, &imports_object) + .map_err(|e: JsValue| -> RuntimeError { e.into() })?, + ), + import_externs, + )) } /// Returns the name of the current module. @@ -416,39 +471,53 @@ impl Module { let imports = WebAssembly::Module::imports(&self.module); let iter = imports .iter() - .map(move |val| { - let module = Reflect::get(val.as_ref(), &"module".into()) - .unwrap() - .as_string() - .unwrap(); - let field = Reflect::get(val.as_ref(), &"name".into()) - .unwrap() - .as_string() - .unwrap(); - let kind = Reflect::get(val.as_ref(), &"kind".into()) - .unwrap() - .as_string() - .unwrap(); - let extern_type = match kind.as_str() { - "function" => { - let func_type = FunctionType::new(vec![], vec![]); - ExternType::Function(func_type) - } - "global" => { - let global_type = GlobalType::new(Type::I32, Mutability::Const); - ExternType::Global(global_type) - } - "memory" => { - let memory_type = MemoryType::new(Pages(1), None, false); - ExternType::Memory(memory_type) - } - "table" => { - let table_type = TableType::new(Type::FuncRef, 1, None); - ExternType::Table(table_type) - } - _ => unimplemented!(), - }; - ImportType::new(&module, &field, extern_type) + .enumerate() + .map(move |(i, val)| { + #[allow(unused_unsafe)] + unsafe { + let module = Reflect::get(val.as_ref(), &"module".into()) + .unwrap() + .as_string() + .unwrap(); + let field = Reflect::get(val.as_ref(), &"name".into()) + .unwrap() + .as_string() + .unwrap(); + let kind = Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap(); + let type_hint = self + .type_hints + .as_ref() + .map(|hints| hints.imports.get(i).unwrap().clone()); + let extern_type = if let Some(hint) = type_hint { + hint + } else { + match kind.as_str() { + "function" => { + let func_type = FunctionType::new(vec![], vec![]); + ExternType::Function(func_type) + } + "global" => { + let global_type = GlobalType::new(Type::I32, Mutability::Const); + ExternType::Global(global_type) + } + "memory" => { + // The javascript API does not yet expose these properties so without + // the type_hints we don't know what memory to import. + let memory_type = MemoryType::new(Pages(1), None, false); + ExternType::Memory(memory_type) + } + "table" => { + let table_type = TableType::new(Type::FuncRef, 1, None); + ExternType::Table(table_type) + } + _ => unimplemented!(), + } + }; + ImportType::new(&module, &field, extern_type) + } }) .collect::>() .into_iter(); @@ -466,10 +535,13 @@ impl Module { return Err("The exports length must match the type hints lenght".to_owned()); } for (i, val) in exports.iter().enumerate() { - let kind = Reflect::get(val.as_ref(), &"kind".into()) - .unwrap() - .as_string() - .unwrap(); + #[allow(unused_unsafe)] + let kind = unsafe { + Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap() + }; // It is safe to unwrap as we have already checked for the exports length let type_hint = type_hints.exports.get(i).unwrap(); let expected_kind = match type_hint { @@ -515,14 +587,20 @@ impl Module { .iter() .enumerate() .map(move |(i, val)| { - let field = Reflect::get(val.as_ref(), &"name".into()) - .unwrap() - .as_string() - .unwrap(); - let kind = Reflect::get(val.as_ref(), &"kind".into()) - .unwrap() - .as_string() - .unwrap(); + #[allow(unused_unsafe)] + let field = unsafe { + Reflect::get(val.as_ref(), &"name".into()) + .unwrap() + .as_string() + .unwrap() + }; + #[allow(unused_unsafe)] + let kind = unsafe { + Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap() + }; let type_hint = self .type_hints .as_ref() @@ -558,6 +636,25 @@ impl Module { ExportsIterator::new(iter, exports.length() as usize) } + /// Returns true if the module is still ok - this will be + /// false if the module was passed between threads in a + /// way that it became undefined (JS does not share objects + /// between threads except via a post_message()) + pub fn is_ok(&self) -> bool { + let val = JsValue::from(&self.module); + !val.is_undefined() && !val.is_null() + } + + // /// Get the custom sections of the module given a `name`. + // /// + // /// # Important + // /// + // /// Following the WebAssembly spec, one name can have multiple + // /// custom sections. That's why an iterator (rather than one element) + // /// is returned. + // pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + // unimplemented!(); + // } /// Get the custom sections of the module given a `name`. /// /// # Important @@ -565,7 +662,7 @@ impl Module { /// Following the WebAssembly spec, one name can have multiple /// custom sections. That's why an iterator (rather than one element) /// is returned. - pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + pub fn custom_sections<'a>(&'a self, _name: &'a str) -> impl Iterator> + 'a { // TODO: implement on JavaScript DefaultCustomSectionsIterator {} } diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index 69b350652a1..8efbcaedbf9 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -15,9 +15,11 @@ use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList}; // use std::panic::{catch_unwind, AssertUnwindSafe}; use crate::js::export::VMFunction; use crate::js::types::param_from_js; +use crate::js::types::AsJs; use js_sys::Array; use std::iter::FromIterator; use wasm_bindgen::JsValue; +use wasmer_types::RawValue; /// A WebAssembly function that can be called natively /// (using the Native ABI). @@ -64,11 +66,34 @@ macro_rules! impl_native_traits { pub fn call(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where $( $x: FromToNativeWasmType + crate::js::NativeWasmTypeInto, )* { - let params_list: Vec = vec![ $( JsValue::from_f64($x.into_raw(&mut store))),* ]; - let results = self.handle.get(store.as_store_ref().objects()).function.apply( - &JsValue::UNDEFINED, - &Array::from_iter(params_list.iter()) - )?; + #[allow(unused_unsafe)] + let params_list: Vec = unsafe { + vec![ $( RawValue { f64: $x.into_raw(store) } ),* ] + }; + let params_list: Vec = params_list + .into_iter() + .map(|a| a.as_jsvalue(&store.as_store_ref())) + .collect(); + let results = { + let mut r; + loop { + r = self.handle.get(store.as_store_ref().objects()).function.apply( + &JsValue::UNDEFINED, + &Array::from_iter(params_list.iter()) + ); + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } + Ok(wasmer_types::OnCalledAction::Finish) => { break; } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) }, + Err(trap) => { return Err(RuntimeError::user(trap)) }, + } + } + break; + } + r? + }; let mut rets_list_array = Rets::empty_array(); let mut_rets = rets_list_array.as_mut() as *mut [f64] as *mut f64; match Rets::size() { @@ -92,7 +117,6 @@ macro_rules! impl_native_traits { } Ok(unsafe { Rets::from_array(store, rets_list_array) }) } - } #[allow(unused_parens)] diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index 66d6d58ec8c..36030941fcb 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -1,10 +1,19 @@ use std::fmt; +use wasmer_types::OnCalledAction; /// We require the context to have a fixed memory address for its lifetime since /// various bits of the VM have raw pointers that point back to it. Hence we /// wrap the actual context in a box. pub(crate) struct StoreInner { pub(crate) objects: StoreObjects, + pub(crate) on_called: Option< + Box< + dyn FnOnce( + StoreMut, + ) + -> Result>, + >, + >, } /// The store represents all global state that can be manipulated by @@ -27,6 +36,7 @@ impl Store { Self { inner: Box::new(StoreInner { objects: Default::default(), + on_called: None, }), } } @@ -37,6 +47,11 @@ impl Store { pub fn same(_a: &Self, _b: &Self) -> bool { true } + + /// Returns the ID of this store + pub fn id(&self) -> StoreId { + self.inner.objects.id() + } } impl PartialEq for Store { @@ -102,6 +117,11 @@ impl<'a> StoreRef<'a> { pub fn same(a: &Self, b: &Self) -> bool { a.inner.objects.id() == b.inner.objects.id() } + + /// Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> wasmer_types::StoreSnapshot { + self.inner.objects.save_snapshot() + } } /// A temporary handle to a [`Context`]. @@ -117,6 +137,16 @@ impl<'a> StoreMut<'a> { a.inner.objects.id() == b.inner.objects.id() } + /// Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> wasmer_types::StoreSnapshot { + self.inner.objects.save_snapshot() + } + + /// Restores a snapshot back into the store + pub fn restore_snapshot(&mut self, snapshot: &wasmer_types::StoreSnapshot) { + self.inner.objects.restore_snapshot(snapshot); + } + pub(crate) fn as_raw(&self) -> *mut StoreInner { self.inner as *const StoreInner as *mut StoreInner } @@ -124,6 +154,17 @@ impl<'a> StoreMut<'a> { pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { Self { inner: &mut *raw } } + + /// Sets the unwind callback which will be invoked when the call finishes + pub fn on_called(&mut self, callback: F) + where + F: FnOnce(StoreMut<'_>) -> Result> + + Send + + Sync + + 'static, + { + self.inner.on_called.replace(Box::new(callback)); + } } /// Helper trait for a value that is convertible to a [`StoreRef`]. @@ -286,6 +327,22 @@ mod objects { (&mut high[0], &mut low[a.index()]) } } + + /// Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> wasmer_types::StoreSnapshot { + let mut ret = wasmer_types::StoreSnapshot::default(); + for (index, global) in self.globals.iter().enumerate() { + global.save_snapshot(index, &mut ret); + } + ret + } + + /// Serializes the mutable things into a snapshot + pub fn restore_snapshot(&mut self, snapshot: &wasmer_types::StoreSnapshot) { + for (index, global) in self.globals.iter_mut().enumerate() { + global.restore_snapshot(index, snapshot); + } + } } /// Handle to an object managed by a context. @@ -359,6 +416,11 @@ mod objects { self.id } + /// Overrides the store id with a new ID + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. /// /// # Safety @@ -446,6 +508,7 @@ mod objects { Instance(NonNull), } + #[allow(dead_code)] impl MaybeInstanceOwned { /// Returns underlying pointer to the VM data. #[allow(dead_code)] diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index e871be1c5d9..5ba0dd5bf6f 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -31,6 +31,7 @@ impl CoreError for T {} impl dyn CoreError + 'static { /// Returns `true` if the inner type is the same as `T`. + #[allow(dead_code)] pub fn core_is_equal(&self) -> bool { let t = core::any::TypeId::of::(); let concrete = self.type_id(); @@ -40,6 +41,7 @@ impl dyn CoreError + 'static { impl dyn CoreError + Send + Sync + 'static { /// Returns `true` if the inner type is the same as `T`. + #[allow(dead_code)] pub fn core_is_equal(&self) -> bool { let t = core::any::TypeId::of::(); let concrete = self.type_id(); @@ -50,6 +52,7 @@ impl dyn CoreError + Send + Sync + 'static { impl dyn CoreError + Send { #[inline] /// Attempts to downcast the box to a concrete type. + #[allow(dead_code)] pub fn downcast_core( self: Box, ) -> Result, Box> { @@ -64,6 +67,7 @@ impl dyn CoreError + Send { impl dyn CoreError + Send + Sync { #[inline] /// Attempts to downcast the box to a concrete type. + #[allow(dead_code)] pub fn downcast_core(self: Box) -> Result, Box> { let err: Box = self; ::downcast_core(err).map_err(|s| unsafe { @@ -76,6 +80,7 @@ impl dyn CoreError + Send + Sync { impl dyn CoreError { #[inline] /// Attempts to downcast the box to a concrete type. + #[allow(dead_code)] pub fn downcast_core( self: Box, ) -> Result, Box> { @@ -252,6 +257,30 @@ impl std::error::Error for RuntimeError { } } +pub fn generic_of_jsval>( + js: JsValue, + classname: &str, +) -> Result { + use js_sys::{Object, Reflect}; + let ctor_name = Object::get_prototype_of(&js).constructor().name(); + if ctor_name == classname { + #[allow(unused_unsafe)] + let ptr = unsafe { Reflect::get(&js, &JsValue::from_str("ptr"))? }; + match ptr.as_f64() { + Some(ptr_f64) => { + let foo = unsafe { T::from_abi(ptr_f64 as u32) }; + Ok(foo) + } + None => { + // We simply relay the js value + Err(js) + } + } + } else { + Err(js) + } +} + impl From for RuntimeError { fn from(original: JsValue) -> Self { // We try to downcast the error and see if it's diff --git a/lib/api/src/js/types.rs b/lib/api/src/js/types.rs index c7a4efd9f53..6b2da916594 100644 --- a/lib/api/src/js/types.rs +++ b/lib/api/src/js/types.rs @@ -54,3 +54,9 @@ impl AsJs for Value { } } } + +impl AsJs for wasmer_types::RawValue { + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JsValue { + unsafe { JsValue::from_f64(self.f64) } + } +} diff --git a/lib/api/src/js/value.rs b/lib/api/src/js/value.rs index 6eb2cb512aa..828cdc165f0 100644 --- a/lib/api/src/js/value.rs +++ b/lib/api/src/js/value.rs @@ -2,12 +2,13 @@ use std::convert::TryFrom; use std::fmt; use std::string::{String, ToString}; +use wasmer_types::RawValue; use wasmer_types::Type; //use crate::ExternRef; use crate::js::externals::function::Function; -use super::store::{AsStoreMut, AsStoreRef}; +use super::store::AsStoreRef; /// WebAssembly computations manipulate values of basic value types: /// * Integers (32 or 64 bit width) @@ -106,6 +107,13 @@ impl Value { } } + /// Converts the `Value` into a `RawValue`. + pub unsafe fn as_raw_value(&self, store: &impl AsStoreRef) -> RawValue { + RawValue { + f64: self.as_raw(store), + } + } + /// Converts a `f64` to a `Value`. /// /// # Safety diff --git a/lib/api/src/js/wasm_bindgen_polyfill.rs b/lib/api/src/js/wasm_bindgen_polyfill.rs index 1b5dad63a12..80f89a6a702 100644 --- a/lib/api/src/js/wasm_bindgen_polyfill.rs +++ b/lib/api/src/js/wasm_bindgen_polyfill.rs @@ -16,6 +16,7 @@ extern "C" { /// of the given type and value. /// /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[allow(unused_doc_comments)] #[wasm_bindgen(constructor, js_namespace = WebAssembly, catch)] pub fn new(global_descriptor: &Object, value: &JsValue) -> Result; @@ -23,6 +24,7 @@ extern "C" { /// returns the value of the global. /// /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[allow(unused_doc_comments)] #[wasm_bindgen(method, getter, structural, js_namespace = WebAssembly)] pub fn value(this: &Global) -> JsValue; diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 663903104ba..dcb87c06186 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -437,3 +437,6 @@ mod js; #[cfg(feature = "js")] pub use js::*; + +mod into_bytes; +pub use into_bytes::IntoBytes; diff --git a/lib/api/src/sys/exports.rs b/lib/api/src/sys/exports.rs index 571419deefb..0141f8cd9ea 100644 --- a/lib/api/src/sys/exports.rs +++ b/lib/api/src/sys/exports.rs @@ -57,6 +57,9 @@ pub enum ExportError { /// This error arises when an export is missing #[error("Missing export {0}")] Missing(String), + /// This error arises when an export is missing + #[error("Serialization failed {0}")] + SerializationFailed(String), } /// Exports is a special kind of map that allows easily unwrapping diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 8392e9b4203..92798a7564f 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -1,22 +1,32 @@ use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; -use crate::sys::store::{AsStoreMut, AsStoreRef, StoreInner, StoreMut}; +use crate::sys::store::{AsStoreMut, AsStoreRef}; use crate::sys::FunctionType; -use crate::sys::RuntimeError; use crate::sys::TypedFunction; +use crate::FunctionEnv; -use crate::{FunctionEnv, FunctionEnvMut, Value}; -use inner::StaticFunction; pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; -use std::cell::UnsafeCell; -use std::cmp::max; -use std::ffi::c_void; -use wasmer_types::RawValue; +#[cfg(feature = "compiler")] +use { + crate::{ + sys::{ + store::{StoreInner, StoreMut}, + RuntimeError, + }, + FunctionEnvMut, Value, + }, + inner::StaticFunction, + std::{cell::UnsafeCell, cmp::max, ffi::c_void}, + wasmer_types::RawValue, + wasmer_vm::{ + on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, MaybeInstanceOwned, + VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, + VMFunctionContext, VMTrampoline, + }, +}; + use wasmer_vm::{ - on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, InternalStoreHandle, - MaybeInstanceOwned, StoreHandle, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, - VMExtern, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionContext, VMFunctionKind, - VMTrampoline, + InternalStoreHandle, StoreHandle, VMExtern, VMFuncRef, VMFunction, VMFunctionKind, }; /// A WebAssembly `function` instance. @@ -41,6 +51,12 @@ pub struct Function { pub(crate) handle: StoreHandle, } +impl Into for StoreHandle { + fn into(self) -> Function { + Function { handle: self } + } +} + impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// @@ -324,6 +340,22 @@ impl Function { } } + #[allow(missing_docs)] + #[allow(unused_variables)] + #[cfg(not(feature = "compiler"))] + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: HostFunction + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + unimplemented!("this platform does not support functions without the 'compiler' feature") + } + /// Returns the [`FunctionType`] of the `Function`. /// /// # Example @@ -401,24 +433,61 @@ impl Function { *slot = arg.as_raw(store); } + // Invoke the call + self.call_wasm_raw(store, trampoline, values_vec, results)?; + Ok(()) + } + + #[cfg(feature = "compiler")] + fn call_wasm_raw( + &self, + store: &mut impl AsStoreMut, + trampoline: VMTrampoline, + mut params: Vec, + results: &mut [Value], + ) -> Result<(), RuntimeError> { // Call the trampoline. - let vm_function = self.handle.get(store.as_store_ref().objects()); - if let Err(error) = unsafe { - wasmer_call_trampoline( - store.as_store_ref().signal_handler(), - vm_function.anyfunc.as_ptr().as_ref().vmctx, - trampoline, - vm_function.anyfunc.as_ptr().as_ref().func_ptr, - values_vec.as_mut_ptr() as *mut u8, - ) - } { + let result = { + let mut r; + loop { + let vm_function = self.handle.get(store.as_store_ref().objects()); + r = unsafe { + wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + vm_function.anyfunc.as_ptr().as_ref().vmctx, + trampoline, + vm_function.anyfunc.as_ptr().as_ref().func_ptr, + params.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { + continue; + } + Ok(wasmer_types::OnCalledAction::Finish) => { + break; + } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { + return Err(RuntimeError::user(trap)) + } + Err(trap) => return Err(RuntimeError::user(trap)), + } + } + break; + } + r + }; + if let Err(error) = result { return Err(RuntimeError::from_trap(error)); } // Load the return values out of `values_vec`. + let signature = self.ty(store); for (index, &value_type) in signature.results().iter().enumerate() { unsafe { - results[index] = Value::from_raw(store, value_type, values_vec[index]); + results[index] = Value::from_raw(store, value_type, params[index]); } } @@ -517,6 +586,27 @@ impl Function { Ok(results.into_boxed_slice()) } + #[doc(hidden)] + #[allow(missing_docs)] + #[cfg(feature = "compiler")] + pub fn call_raw( + &self, + store: &mut impl AsStoreMut, + params: Vec, + ) -> Result, RuntimeError> { + let trampoline = unsafe { + self.handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + .call_trampoline + }; + let mut results = vec![Value::null(); self.result_arity(store)]; + self.call_wasm_raw(store, trampoline, params, &mut results)?; + Ok(results.into_boxed_slice()) + } + pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef { let vm_function = self.handle.get(store.as_store_ref().objects()); if vm_function.kind == VMFunctionKind::Dynamic { @@ -713,16 +803,19 @@ impl<'a> Exportable<'a> for Function { } /// Host state for a dynamic function. +#[cfg(feature = "compiler")] pub(crate) struct DynamicFunction { func: F, } +#[cfg(feature = "compiler")] impl DynamicFunction where F: Fn(*mut RawValue) -> Result<(), RuntimeError> + 'static, { // This function wraps our func, to make it compatible with the // reverse trampoline signature + #[cfg(feature = "compiler")] unsafe extern "C" fn func_wrapper( this: &mut VMDynamicFunctionContext, values_vec: *mut RawValue, @@ -739,10 +832,12 @@ where } } + #[cfg(feature = "compiler")] fn func_body_ptr(&self) -> *const VMFunctionBody { Self::func_wrapper as *const VMFunctionBody } + #[cfg(feature = "compiler")] fn call_trampoline_address(&self) -> VMTrampoline { Self::call_trampoline } @@ -773,7 +868,10 @@ mod inner { use wasmer_vm::{raise_user_trap, resume_panic, VMFunctionBody}; use crate::sys::NativeWasmTypeInto; - use crate::{AsStoreMut, AsStoreRef, ExternRef, Function, FunctionEnv, StoreMut}; + use crate::{AsStoreMut, AsStoreRef, ExternRef, FunctionEnv, StoreMut}; + + #[cfg(feature = "compiler")] + use crate::Function; /// A trait to convert a Rust value to a `WasmNativeType` value, /// or to convert `WasmNativeType` value to a Rust value. diff --git a/lib/api/src/sys/externals/global.rs b/lib/api/src/sys/externals/global.rs index 2dfc77ff06d..e43725ec265 100644 --- a/lib/api/src/sys/externals/global.rs +++ b/lib/api/src/sys/externals/global.rs @@ -4,6 +4,7 @@ use crate::sys::store::{AsStoreMut, AsStoreRef}; use crate::sys::value::Value; use crate::sys::GlobalType; use crate::sys::Mutability; +#[cfg(feature = "compiler")] use crate::sys::RuntimeError; use wasmer_vm::{InternalStoreHandle, StoreHandle, VMExtern, VMGlobal}; diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 0793513f5be..67cf908d4d9 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -11,6 +11,8 @@ use std::slice; #[cfg(feature = "tracing")] use tracing::warn; use wasmer_types::Pages; +#[cfg(feature = "compiler")] +use wasmer_types::WASM_PAGE_SIZE; use wasmer_vm::{InternalStoreHandle, LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory}; use super::MemoryView; @@ -132,6 +134,36 @@ impl Memory { self.handle.get_mut(store.objects_mut()).grow(delta.into()) } + /// Copies the memory to a new store and returns a memory reference to it + #[cfg(feature = "compiler")] + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + // Create the new memory using the parameters of the existing memory + let view = self.view(store); + let ty = self.ty(store); + let amount = view.data_size() as usize; + + let new_memory = Self::new(new_store, ty)?; + let mut new_view = new_memory.view(&new_store); + let new_view_size = new_view.data_size() as usize; + if amount > new_view_size { + let delta = amount - new_view_size; + let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + new_memory.grow(new_store, Pages(pages as u32))?; + new_view = new_memory.view(&new_store); + } + + // Copy the bytes + view.copy_to_memory(amount as u64, &new_view) + .map_err(|err| MemoryError::Generic(err.to_string()))?; + + // Return the new memory + Ok(new_memory) + } + pub(crate) fn from_vm_extern( store: &impl AsStoreRef, internal: InternalStoreHandle, diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index a872ea7c259..ee1cbaea17c 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -159,4 +159,35 @@ impl<'a> MemoryView<'a> { self.write(offset, &buf)?; Ok(()) } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < self.data_size() { + let remaining = self.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } } diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs index 727c9063239..56031e1d462 100644 --- a/lib/api/src/sys/externals/table.rs +++ b/lib/api/src/sys/externals/table.rs @@ -1,9 +1,10 @@ use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::RuntimeError; use crate::sys::TableType; -use crate::{ExternRef, Function, Value}; +use crate::Value; +#[cfg(feature = "compiler")] +use crate::{sys::RuntimeError, ExternRef, Function}; use wasmer_vm::{InternalStoreHandle, StoreHandle, TableElement, VMExtern, VMTable}; /// A WebAssembly `table` instance. diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/sys/imports.rs index 8e988dadeef..f2ceba1ba31 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/sys/imports.rs @@ -1,11 +1,15 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. +#[cfg(feature = "compiler")] +use crate::{AsStoreMut, Memory}; use crate::{Exports, Extern, Module}; use std::collections::HashMap; use std::fmt; use wasmer_compiler::LinkError; use wasmer_types::ImportError; +#[cfg(feature = "compiler")] +use wasmer_vm::VMSharedMemory; /// All of the import data used when instantiating. /// @@ -121,6 +125,37 @@ impl Imports { .insert((ns.to_string(), name.to_string()), val.into()); } + /// Imports (any) shared memory into the imports. + /// (if the module does not import memory then this function is ignored) + #[cfg(feature = "compiler")] + pub fn import_shared_memory( + &mut self, + module: &Module, + store: &mut impl AsStoreMut, + ) -> Option { + // Determine if shared memory needs to be created and imported + let shared_memory = module + .imports() + .memories() + .next() + .map(|a| *a.ty()) + .map(|ty| { + let style = store.as_store_ref().tunables().memory_style(&ty); + VMSharedMemory::new(&ty, &style).unwrap() + }); + + if let Some(memory) = shared_memory { + self.define( + "env", + "memory", + Memory::new_from_existing(store, memory.clone().into()), + ); + Some(memory) + } else { + None + } + } + /// Returns the contents of a namespace as an `Exports`. /// /// Returns `None` if the namespace doesn't exist. @@ -159,6 +194,32 @@ impl Imports { } Ok(ret) } + + /// Iterates through all the imports in this structure + pub fn iter<'a>(&'a self) -> ImportsIterator<'a> { + ImportsIterator::new(self) + } +} + +pub struct ImportsIterator<'a> { + iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, +} + +impl<'a> ImportsIterator<'a> { + fn new(imports: &'a Imports) -> Self { + let iter = imports.map.iter(); + Self { iter } + } +} + +impl<'a> Iterator for ImportsIterator<'a> { + type Item = (&'a str, &'a str, &'a Extern); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|(k, v)| (k.0.as_str(), k.1.as_str(), v)) + } } impl IntoIterator for &Imports { @@ -237,6 +298,7 @@ impl fmt::Debug for Imports { macro_rules! imports { ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => { { + #[allow(unused_mut)] let mut import_object = $crate::Imports::new(); $({ diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index ab8e9d5c293..4ad4d797096 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -1,13 +1,15 @@ use crate::sys::exports::Exports; -use crate::sys::externals::Extern; -use crate::sys::imports::Imports; use crate::sys::module::Module; +#[cfg(feature = "compiler")] use crate::sys::{LinkError, RuntimeError}; use std::fmt; use thiserror::Error; use wasmer_vm::{InstanceHandle, StoreHandle}; +#[cfg(feature = "compiler")] use super::store::AsStoreMut; +#[cfg(feature = "compiler")] +use crate::sys::{externals::Extern, imports::Imports}; /// A WebAssembly Instance is a stateful, executable /// instance of a WebAssembly [`Module`]. @@ -115,11 +117,11 @@ impl Instance { module: &Module, imports: &Imports, ) -> Result { - let imports = imports + let externs = imports .imports_for_module(module) .map_err(InstantiationError::Link)?; - let mut handle = module.instantiate(store, &imports)?; - let exports = module + let mut handle = module.instantiate(store, &externs)?; + let mut exports = module .exports() .map(|export| { let name = export.name().to_string(); @@ -129,6 +131,18 @@ impl Instance { }) .collect::(); + // If the memory is imported then also export it for backwards compatibility reasons + // (many will assume the memory is always exported) - later we can remove this + if exports.get_memory("memory").is_err() { + if let Some(memory) = externs + .iter() + .filter(|a| a.ty(store).memory().is_some()) + .next() + { + exports.insert("memory", memory.clone()); + } + } + let instance = Self { _handle: StoreHandle::new(store.objects_mut(), handle), module: module.clone(), @@ -154,9 +168,9 @@ impl Instance { module: &Module, externs: &[Extern], ) -> Result { - let imports = externs.to_vec(); - let mut handle = module.instantiate(store, &imports)?; - let exports = module + let externs = externs.to_vec(); + let mut handle = module.instantiate(store, &externs)?; + let mut exports = module .exports() .map(|export| { let name = export.name().to_string(); @@ -166,6 +180,18 @@ impl Instance { }) .collect::(); + // If the memory is imported then also export it for backwards compatibility reasons + // (many will assume the memory is always exported) - later we can remove this + if exports.get_memory("memory").is_err() { + if let Some(memory) = externs + .iter() + .filter(|a| a.ty(store).memory().is_some()) + .next() + { + exports.insert("memory", memory.clone()); + } + } + let instance = Self { _handle: StoreHandle::new(store.objects_mut(), handle), module: module.clone(), diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 28204577a22..8bceda732e9 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -330,6 +330,24 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } Ok(vec) } + + /// Reads this `WasmSlice` into a `BytesMut` + #[inline] + pub fn read_to_bytes(self) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut ret = bytes::BytesMut::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + ret.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + ret.set_len(len); + } + Ok(ret) + } } impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 3fd2461fc82..f81f0be4422 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -42,7 +42,7 @@ pub use wasmer_derive::ValueType; pub use wasmer_types::is_wasm; pub use wasmer_types::{ CpuFeature, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, - Mutability, TableType, Target, Type, + Mutability, OnCalledAction, StoreSnapshot, TableType, Target, Type, }; pub use wasmer_types::{ diff --git a/lib/api/src/sys/module.rs b/lib/api/src/sys/module.rs index 8eda10b2811..ce81bc352e5 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/sys/module.rs @@ -1,8 +1,4 @@ -use crate::sys::InstantiationError; -use crate::AsStoreMut; -use crate::AsStoreRef; use bytes::Bytes; -use std::borrow::Cow; use std::fmt; use std::io; use std::path::Path; @@ -17,6 +13,10 @@ use wasmer_types::{ CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, }; use wasmer_types::{ExportType, ImportType}; + +#[cfg(feature = "compiler")] +use crate::{sys::InstantiationError, AsStoreMut, AsStoreRef, IntoBytes}; +#[cfg(feature = "compiler")] use wasmer_vm::InstanceHandle; /// IO Error on a Module Compilation @@ -58,46 +58,6 @@ pub struct Module { module_info: Arc, } -pub trait IntoBytes { - fn into_bytes(self) -> Bytes; -} - -impl IntoBytes for Bytes { - fn into_bytes(self) -> Bytes { - self - } -} - -impl IntoBytes for Vec { - fn into_bytes(self) -> Bytes { - Bytes::from(self) - } -} - -impl IntoBytes for &[u8] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &[u8; N] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &str { - fn into_bytes(self) -> Bytes { - Bytes::from(self.as_bytes().to_vec()) - } -} - -impl IntoBytes for Cow<'_, [u8]> { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - impl Module { #[cfg(feature = "compiler")] /// Creates a new WebAssembly Module given the configuration @@ -493,6 +453,15 @@ impl Module { self.module_info.exports() } + /// Returns true if the module is still ok - this will be + /// false if the module was passed between threads in a + /// way that it became undefined (JS does not share objects + /// between threads except via a post_message()) + pub fn is_ok(&self) -> bool { + // As RUST is a type safe language modules in SYS are always ok + true + } + /// Get the custom sections of the module given a `name`. /// /// # Important diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index d5d3bc5aaa5..a907cc7d603 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -7,12 +7,14 @@ //! let add_one = instance.exports.get_function("function_name")?; //! let add_one_native: TypedFunction = add_one.native().unwrap(); //! ``` +use std::cell::Cell; use std::marker::PhantomData; use crate::sys::{ AsStoreMut, FromToNativeWasmType, Function, NativeWasmTypeInto, RuntimeError, WasmTypeList, }; -use wasmer_types::RawValue; +use crate::StoreMut; +use wasmer_types::{OnCalledAction, RawValue}; /// A WebAssembly function that can be called natively /// (using the Native ABI). @@ -45,6 +47,10 @@ impl Clone for TypedFunction } } +thread_local! { + static ON_CALLED: Cell) -> Result>>>> = Cell::new(None); +} + macro_rules! impl_native_traits { ( $( $x:ident ),* ) => { #[allow(unused_parens, non_snake_case)] @@ -87,15 +93,110 @@ macro_rules! impl_native_traits { } rets_list.as_mut() }; - unsafe { - wasmer_vm::wasmer_call_trampoline( - store.as_store_ref().signal_handler(), - anyfunc.vmctx, - anyfunc.call_trampoline, - anyfunc.func_ptr, - args_rets.as_mut_ptr() as *mut u8, - ) - }?; + + let mut r; + loop { + r = unsafe { + wasmer_vm::wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + anyfunc.vmctx, + anyfunc.call_trampoline, + anyfunc.func_ptr, + args_rets.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } + Ok(wasmer_types::OnCalledAction::Finish) => { break; } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) }, + Err(trap) => { return Err(RuntimeError::user(trap)) }, + } + } + break; + } + r?; + + let num_rets = rets_list.len(); + if !using_rets_array && num_rets > 0 { + let src_pointer = params_list.as_ptr(); + let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue; + unsafe { + // TODO: we can probably remove this copy by doing some clever `transmute`s. + // we know it's not overlapping because `using_rets_array` is false + std::ptr::copy_nonoverlapping(src_pointer, + rets_list, + num_rets); + } + } + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: + // but we can't currently detect whether that's safe. + // + // let results = unsafe { + // wasmer_vm::catch_traps_with_result(self.vmctx, || { + // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); + // // We always pass the vmctx + // f( self.vmctx, $( $x, )* ) + // }).map_err(RuntimeError::from_trap)? + // }; + // Ok(Rets::from_c_struct(results)) + } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + let anyfunc = unsafe { + *self.func + .handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + }; + // TODO: when `const fn` related features mature more, we can declare a single array + // of the correct size here. + let mut rets_list_array = Rets::empty_array(); + let rets_list: &mut [RawValue] = rets_list_array.as_mut(); + let using_rets_array; + let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() { + using_rets_array = false; + params_list.as_mut() + } else { + using_rets_array = true; + for (i, &arg) in params_list.iter().enumerate() { + rets_list[i] = arg; + } + rets_list.as_mut() + }; + + let mut r; + loop { + r = unsafe { + wasmer_vm::wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + anyfunc.vmctx, + anyfunc.call_trampoline, + anyfunc.func_ptr, + args_rets.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } + Ok(wasmer_types::OnCalledAction::Finish) => { break; } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) }, + Err(trap) => { return Err(RuntimeError::user(trap)) }, + } + } + break; + } + r?; + let num_rets = rets_list.len(); if !using_rets_array && num_rets > 0 { let src_pointer = params_list.as_ptr(); diff --git a/lib/api/src/sys/native_type.rs b/lib/api/src/sys/native_type.rs index 42f3f23939c..765b5f6b2c1 100644 --- a/lib/api/src/sys/native_type.rs +++ b/lib/api/src/sys/native_type.rs @@ -2,7 +2,9 @@ //! easily in Rust, thanks to its advanced typing system. use wasmer_types::{NativeWasmType, RawValue, Type}; -use wasmer_vm::{VMExternRef, VMFuncRef}; +use wasmer_vm::VMExternRef; +#[cfg(feature = "compiler")] +use wasmer_vm::VMFuncRef; use crate::{ExternRef, Function, TypedFunction, WasmTypeList}; diff --git a/lib/api/src/sys/ptr.rs b/lib/api/src/sys/ptr.rs index 1f51c203094..285688b8fff 100644 --- a/lib/api/src/sys/ptr.rs +++ b/lib/api/src/sys/ptr.rs @@ -97,7 +97,7 @@ impl WasmPtr { /// Checks whether the `WasmPtr` is null. #[inline] - pub fn is_null(self) -> bool { + pub fn is_null(&self) -> bool { self.offset.into() == 0 } @@ -142,19 +142,19 @@ impl WasmPtr { /// Creates a `WasmRef` from this `WasmPtr` which allows reading and /// mutating of the value being pointed to. #[inline] - pub fn deref<'a>(self, view: &'a MemoryView) -> WasmRef<'a, T> { + pub fn deref<'a>(&self, view: &'a MemoryView) -> WasmRef<'a, T> { WasmRef::new(view, self.offset.into()) } /// Reads the address pointed to by this `WasmPtr` in a memory. #[inline] - pub fn read(self, view: &MemoryView) -> Result { + pub fn read(&self, view: &MemoryView) -> Result { self.deref(view).read() } /// Writes to the address pointed to by this `WasmPtr` in a memory. #[inline] - pub fn write(self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { + pub fn write(&self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { self.deref(view).write(val) } @@ -165,7 +165,7 @@ impl WasmPtr { /// address. #[inline] pub fn slice<'a>( - self, + &self, view: &'a MemoryView, len: M::Offset, ) -> Result, MemoryAccessError> { @@ -178,7 +178,7 @@ impl WasmPtr { /// This last value is not included in the returned vector. #[inline] pub fn read_until( - self, + &self, view: &MemoryView, mut end: impl FnMut(&T) -> bool, ) -> Result, MemoryAccessError> { @@ -202,7 +202,7 @@ impl WasmPtr { /// modified. #[inline] pub fn read_utf8_string( - self, + &self, view: &MemoryView, len: M::Offset, ) -> Result { @@ -215,7 +215,10 @@ impl WasmPtr { /// This method is safe to call even if the memory is being concurrently /// modified. #[inline] - pub fn read_utf8_string_with_nul(self, view: &MemoryView) -> Result { + pub fn read_utf8_string_with_nul( + &self, + view: &MemoryView, + ) -> Result { let vec = self.read_until(view, |&byte| byte == 0)?; Ok(String::from_utf8(vec)?) } diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index 612652014a2..e777846838d 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -1,20 +1,46 @@ use crate::sys::tunables::BaseTunables; +use derivative::Derivative; use std::fmt; -use std::sync::{Arc, RwLock}; #[cfg(feature = "compiler")] use wasmer_compiler::{AsEngineRef, Engine, EngineBuilder, EngineRef, Tunables}; -use wasmer_vm::{init_traps, TrapHandler, TrapHandlerFn}; +use wasmer_types::{OnCalledAction, StoreSnapshot}; +use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn}; use wasmer_vm::StoreObjects; /// We require the context to have a fixed memory address for its lifetime since /// various bits of the VM have raw pointers that point back to it. Hence we /// wrap the actual context in a box. +#[derive(Derivative)] +#[derivative(Debug)] pub(crate) struct StoreInner { pub(crate) objects: StoreObjects, + #[derivative(Debug = "ignore")] #[cfg(feature = "compiler")] pub(crate) engine: Engine, + #[derivative(Debug = "ignore")] pub(crate) trap_handler: Option>>, + #[derivative(Debug = "ignore")] + pub(crate) on_called: Option< + Box< + dyn FnOnce( + StoreMut<'_>, + ) + -> Result>, + >, + >, +} + +impl StoreInner { + // Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> StoreSnapshot { + self.objects.save_snapshot() + } + + // Serializes the mutable things into a snapshot + pub fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) { + self.objects.restore_snapshot(snapshot); + } } /// The store represents all global state that can be manipulated by @@ -30,7 +56,6 @@ pub struct Store { pub(crate) inner: Box, #[cfg(feature = "compiler")] engine: Engine, - trap_handler: Arc>>>>, } impl Store { @@ -48,9 +73,9 @@ impl Store { objects: Default::default(), engine: engine.cloned(), trap_handler: None, + on_called: None, }), engine: engine.cloned(), - trap_handler: Arc::new(RwLock::new(None)), } } @@ -100,6 +125,11 @@ impl Store { pub fn same(a: &Self, b: &Self) -> bool { a.engine.id() == b.engine.id() } + + /// Returns the ID of this store + pub fn id(&self) -> StoreId { + self.inner.objects.id() + } } #[cfg(feature = "compiler")] @@ -111,8 +141,8 @@ impl PartialEq for Store { unsafe impl TrapHandler for Store { fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool { - if let Some(handler) = self.trap_handler.read().unwrap().as_ref() { - call(handler) + if let Some(handler) = self.inner.trap_handler.as_ref() { + call(handler.as_ref()) } else { false } @@ -261,13 +291,18 @@ impl<'a> StoreRef<'a> { a.inner.engine.id() == b.inner.engine.id() } + /// Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> StoreSnapshot { + self.inner.save_snapshot() + } + /// The signal handler #[inline] pub fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { self.inner .trap_handler .as_ref() - .map(|handler| handler as *const _) + .map(|handler| handler.as_ref() as *const _) } } @@ -297,6 +332,16 @@ impl<'a> StoreMut<'a> { a.inner.engine.id() == b.inner.engine.id() } + /// Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> StoreSnapshot { + self.inner.save_snapshot() + } + + /// Restores a snapshot back into the store + pub fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) { + self.inner.restore_snapshot(snapshot); + } + #[cfg(feature = "compiler")] pub(crate) fn tunables_and_objects_mut(&mut self) -> (&dyn Tunables, &mut StoreObjects) { (self.inner.engine.tunables(), &mut self.inner.objects) @@ -309,6 +354,17 @@ impl<'a> StoreMut<'a> { pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { Self { inner: &mut *raw } } + + /// Sets the unwind callback which will be invoked when the call finishes + pub fn on_called(&mut self, callback: F) + where + F: FnOnce(StoreMut<'_>) -> Result> + + Send + + Sync + + 'static, + { + self.inner.on_called.replace(Box::new(callback)); + } } /// Helper trait for a value that is convertible to a [`StoreRef`]. diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 5274cc2a9cb..e8866abe727 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -120,6 +120,16 @@ mod tests { fn try_clone(&self) -> Option> { None } + fn fork(&mut self) -> Result, MemoryError> { + let mem = self.mem.clone(); + Ok(Box::new(Self { + memory_definition: Some(UnsafeCell::new(VMMemoryDefinition { + base: mem.as_ptr() as _, + current_length: mem.len(), + })), + mem, + })) + } /* // this code allow custom memory to be ignoring init_memory use wasmer_vm::Trap; diff --git a/lib/api/src/sys/value.rs b/lib/api/src/sys/value.rs index f2b2a0b8a81..1c00e4bbcf6 100644 --- a/lib/api/src/sys/value.rs +++ b/lib/api/src/sys/value.rs @@ -3,13 +3,15 @@ use std::fmt; use std::string::{String, ToString}; use wasmer_types::Type; -use wasmer_vm::VMExternRef; -use wasmer_vm::VMFuncRef; +#[cfg(feature = "compiler")] +use wasmer_vm::{VMExternRef, VMFuncRef}; use crate::ExternRef; use crate::Function; -use super::store::{AsStoreMut, AsStoreRef}; +#[cfg(feature = "compiler")] +use super::store::AsStoreMut; +use super::store::AsStoreRef; pub use wasmer_types::RawValue; diff --git a/lib/api/tests/reference_types.rs b/lib/api/tests/reference_types.rs index aecd46d48ec..7e6a6967bd7 100644 --- a/lib/api/tests/reference_types.rs +++ b/lib/api/tests/reference_types.rs @@ -47,17 +47,14 @@ pub mod reference_types { } let func_to_call = - Function::new_typed_with_env(&mut store, &env, |mut env: FunctionEnvMut| -> i32 { - env.data_mut().0.store(true, Ordering::SeqCst); + Function::new_typed_with_env(&mut store, &env, |env: FunctionEnvMut| -> i32 { + env.data().0.store(true, Ordering::SeqCst); 343 }); let call_set_value: &Function = instance.exports.get_function("call_set_value")?; let results: Box<[Value]> = call_set_value.call(&mut store, &[Value::FuncRef(Some(func_to_call))])?; - assert!(env - .as_mut(&mut store.as_store_mut()) - .0 - .load(Ordering::SeqCst)); + assert!(env.as_ref(&store.as_store_ref()).0.load(Ordering::SeqCst)); assert_eq!(&*results, &[Value::I32(343)]); Ok(()) diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index d3fa6617ca7..959a5cbf256 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -32,7 +32,7 @@ wasmer-middlewares = { version = "=3.1.0", path = "../middlewares", optional = t wasmer-wasi = { version = "=3.1.0", path = "../wasi", default-features = false, features = ["host-fs", "sys"], optional = true } wasmer-types = { version = "=3.1.0", path = "../types" } wasmer-vfs = { version = "=3.1.0", path = "../vfs", optional = true, default-features = false, features = ["static-fs"] } -webc = { version = "3.0.1", optional = true } +webc = { version = "0.4.1", optional = true } enumset = "1.0.2" cfg-if = "1.0" lazy_static = "1.4" diff --git a/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs b/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs index 77030cc63b5..2e6fa3d4dc6 100644 --- a/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs +++ b/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs @@ -9,9 +9,8 @@ pub struct RemoveTestsOnDrop {} impl Drop for RemoveTestsOnDrop { fn drop(&mut self) { - return; let manifest_dir = env!("CARGO_MANIFEST_DIR"); - for entry in std::fs::read_dir(&manifest_dir).unwrap() { + for entry in std::fs::read_dir(manifest_dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); @@ -21,13 +20,13 @@ impl Drop for RemoveTestsOnDrop { } } if let Some(parent) = std::path::Path::new(&manifest_dir).parent() { - for entry in std::fs::read_dir(&parent).unwrap() { + for entry in std::fs::read_dir(parent).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); if extension == Some("obj") || extension == Some("exe") || extension == Some("o") { println!("removing {}", path.display()); - let _ = std::fs::remove_file(&path); + let _ = std::fs::remove_file(path); } } } diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index 7d59e614144..ab1bc28b3e3 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -328,14 +328,6 @@ pub unsafe extern "C" fn wasi_env_new( #[no_mangle] pub extern "C" fn wasi_env_delete(_state: Option>) {} -/// Set the memory on a [`wasi_env_t`]. -#[no_mangle] -pub unsafe extern "C" fn wasi_env_set_memory(env: &mut wasi_env_t, memory: &wasm_memory_t) { - let mut store_mut = env.store.store_mut(); - let wasi_env = env.inner.data_mut(&mut store_mut); - wasi_env.set_memory(memory.extern_.memory()); -} - #[no_mangle] pub unsafe extern "C" fn wasi_env_read_stdout( env: &mut wasi_env_t, @@ -343,8 +335,8 @@ pub unsafe extern "C" fn wasi_env_read_stdout( buffer_len: usize, ) -> isize { let inner_buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_len as usize); - let mut store_mut = env.store.store_mut(); - let state = env.inner.data_mut(&mut store_mut).state(); + let store = env.store.store(); + let state = env.inner.data(&store).state(); if let Ok(mut stdout) = state.stdout() { if let Some(stdout) = stdout.as_mut() { @@ -523,11 +515,10 @@ pub unsafe extern "C" fn wasi_env_initialize_instance( store: &mut wasm_store_t, instance: &mut wasm_instance_t, ) -> bool { - let mem = c_try!(instance.inner.exports.get_memory("memory"); otherwise false); wasi_env .inner - .data_mut(&mut store.inner.store_mut()) - .set_memory(mem.clone()); + .initialize(&mut store.inner.store_mut(), &instance.inner) + .unwrap(); true } diff --git a/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs b/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs index 60b2d300845..cd6fa870a8f 100644 --- a/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs +++ b/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs @@ -109,7 +109,7 @@ pub struct RemoveTestsOnDrop {} impl Drop for RemoveTestsOnDrop { fn drop(&mut self) { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - for entry in std::fs::read_dir(&manifest_dir).unwrap() { + for entry in std::fs::read_dir(manifest_dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); @@ -119,7 +119,7 @@ impl Drop for RemoveTestsOnDrop { } } if let Some(parent) = std::path::Path::new(&manifest_dir).parent() { - for entry in std::fs::read_dir(&parent).unwrap() { + for entry in std::fs::read_dir(parent).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); diff --git a/lib/cache/src/filesystem.rs b/lib/cache/src/filesystem.rs index b16e4601a86..376d19139e9 100644 --- a/lib/cache/src/filesystem.rs +++ b/lib/cache/src/filesystem.rs @@ -98,7 +98,13 @@ impl Cache for FileSystemCache { key.to_string() }; let path = self.path.join(filename); - Module::deserialize_from_file(store, path) + let ret = Module::deserialize_from_file(store, path.clone()); + if ret.is_err() { + // If an error occurs while deserializing then we can not trust it anymore + // so delete the cache file + let _ = std::fs::remove_file(path); + } + ret } fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> { diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs index bcffabff54f..822b26501ec 100644 --- a/lib/cli-compiler/src/commands/compile.rs +++ b/lib/cli-compiler/src/commands/compile.rs @@ -104,6 +104,7 @@ impl Compile { &target, memory_styles, table_styles, + None, )?; artifact.serialize_to_file(self.output.as_ref())?; eprintln!( diff --git a/lib/compiler-cranelift/Cargo.toml b/lib/compiler-cranelift/Cargo.toml index ca3a4b52230..425bc3873d7 100644 --- a/lib/compiler-cranelift/Cargo.toml +++ b/lib/compiler-cranelift/Cargo.toml @@ -38,3 +38,4 @@ wasm = ["std", "unwind"] unwind = ["cranelift-codegen/unwind", "gimli"] std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmer-compiler/std", "wasmer-types/std"] core = ["hashbrown", "cranelift-codegen/core", "cranelift-frontend/core"] +verbose = [] diff --git a/lib/compiler-cranelift/build.rs b/lib/compiler-cranelift/build.rs index 5e03bc2e028..0be0760c071 100644 --- a/lib/compiler-cranelift/build.rs +++ b/lib/compiler-cranelift/build.rs @@ -7,7 +7,7 @@ use std::process::Command; use std::str; fn main() { - let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() { + let git_rev = match Command::new("git").args(["rev-parse", "HEAD"]).output() { Ok(output) => str::from_utf8(&output.stdout).unwrap().trim().to_string(), Err(_) => env!("CARGO_PKG_VERSION").to_string(), }; diff --git a/lib/compiler-cranelift/src/compiler.rs b/lib/compiler-cranelift/src/compiler.rs index 807c07fab9b..9f2481bb0a2 100644 --- a/lib/compiler-cranelift/src/compiler.rs +++ b/lib/compiler-cranelift/src/compiler.rs @@ -52,6 +52,10 @@ impl CraneliftCompiler { } impl Compiler for CraneliftCompiler { + fn name(&self) -> &str { + "cranelift" + } + /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler-cranelift/src/translator/func_translator.rs b/lib/compiler-cranelift/src/translator/func_translator.rs index 0f31a97a4eb..39d77d9681b 100644 --- a/lib/compiler-cranelift/src/translator/func_translator.rs +++ b/lib/compiler-cranelift/src/translator/func_translator.rs @@ -15,7 +15,6 @@ use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; use cranelift_codegen::timing; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use tracing::info; use wasmer_compiler::wasmparser; use wasmer_compiler::{ wasm_unsupported, wptype_to_type, FunctionBinaryReader, ModuleTranslationState, @@ -80,7 +79,8 @@ impl FuncTranslator { environ: &mut FE, ) -> WasmResult<()> { let _tt = timing::wasm_translate_function(); - info!( + #[cfg(feature = "verbose")] + tracing::info!( "translate({} bytes, {}{})", reader.bytes_remaining(), func.name, diff --git a/lib/compiler-llvm/src/compiler.rs b/lib/compiler-llvm/src/compiler.rs index 8f888dc6e16..fd7a86d00b1 100644 --- a/lib/compiler-llvm/src/compiler.rs +++ b/lib/compiler-llvm/src/compiler.rs @@ -187,6 +187,10 @@ impl LLVMCompiler { } impl Compiler for LLVMCompiler { + fn name(&self) -> &str { + "llvm" + } + /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index 7784a451ffc..5cee5d4ec5b 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -51,6 +51,10 @@ impl SinglepassCompiler { } impl Compiler for SinglepassCompiler { + fn name(&self) -> &str { + "singlepass" + } + /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler/src/artifact_builders/artifact_builder.rs b/lib/compiler/src/artifact_builders/artifact_builder.rs index bbbc93e7f0a..69e68ac1bc5 100644 --- a/lib/compiler/src/artifact_builders/artifact_builder.rs +++ b/lib/compiler/src/artifact_builders/artifact_builder.rs @@ -8,20 +8,18 @@ use crate::EngineInner; use crate::Features; use crate::{ModuleEnvironment, ModuleMiddlewareChain}; use enumset::EnumSet; -use std::mem; use wasmer_types::entity::PrimaryMap; #[cfg(feature = "compiler")] use wasmer_types::CompileModuleInfo; -use wasmer_types::MetadataHeader; -use wasmer_types::SerializeError; use wasmer_types::{ CompileError, CpuFeature, CustomSection, Dwarf, FunctionIndex, LocalFunctionIndex, MemoryIndex, - MemoryStyle, ModuleInfo, OwnedDataInitializer, Relocation, SectionIndex, SignatureIndex, + MemoryStyle, ModuleInfo, OwnedDataInitializer, Pages, Relocation, SectionIndex, SignatureIndex, TableIndex, TableStyle, Target, }; use wasmer_types::{ CompiledFunctionFrameInfo, FunctionBody, SerializableCompilation, SerializableModule, }; +use wasmer_types::{MetadataHeader, SerializeError}; /// A compiled wasm module, ready to be instantiated. pub struct ArtifactBuild { @@ -45,6 +43,7 @@ impl ArtifactBuild { target: &Target, memory_styles: PrimaryMap, table_styles: PrimaryMap, + module_start: Option, ) -> Result { let environ = ModuleEnvironment::new(); let features = inner_engine.features().clone(); @@ -121,6 +120,7 @@ impl ArtifactBuild { compile_info, data_initializers, cpu_features: cpu_features.as_u64(), + module_start, }; Ok(Self { serializable }) } @@ -145,6 +145,11 @@ impl ArtifactBuild { Self { serializable } } + /// Returns the memory start address for this compiled module + pub fn get_memory_start(&self) -> Option { + self.serializable.module_start + } + /// Get Functions Bodies ref pub fn get_function_bodies_ref(&self) -> &PrimaryMap { &self.serializable.compilation.function_bodies @@ -223,7 +228,7 @@ impl ArtifactCreate for ArtifactBuild { fn serialize(&self) -> Result, SerializeError> { let serialized_data = self.serializable.serialize()?; - assert!(mem::align_of::() <= MetadataHeader::ALIGN); + assert!(std::mem::align_of::() <= MetadataHeader::ALIGN); let mut metadata_binary = vec![]; metadata_binary.extend(Self::MAGIC_HEADER); diff --git a/lib/compiler/src/artifact_builders/trampoline.rs b/lib/compiler/src/artifact_builders/trampoline.rs index 5a7de39c118..6c634e148af 100644 --- a/lib/compiler/src/artifact_builders/trampoline.rs +++ b/lib/compiler/src/artifact_builders/trampoline.rs @@ -33,7 +33,7 @@ fn make_trampoline( ) { match target.triple().architecture { Architecture::Aarch64(_) => { - code.extend(&AARCH64_TRAMPOLINE); + code.extend(AARCH64_TRAMPOLINE); relocations.push(Relocation { kind: RelocationKind::Abs8, reloc_target: RelocationTarget::LibCall(libcall), @@ -42,7 +42,7 @@ fn make_trampoline( }); } Architecture::X86_64 => { - code.extend(&X86_64_TRAMPOLINE); + code.extend(X86_64_TRAMPOLINE); relocations.push(Relocation { kind: RelocationKind::Abs8, reloc_target: RelocationTarget::LibCall(libcall), diff --git a/lib/compiler/src/compiler.rs b/lib/compiler/src/compiler.rs index 61a4dcaef14..cb1d35f04cb 100644 --- a/lib/compiler/src/compiler.rs +++ b/lib/compiler/src/compiler.rs @@ -79,6 +79,9 @@ where /// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. pub trait Compiler: Send { + /// Gets the name of this compiler + fn name(&self) -> &str; + /// Validates a module. /// /// It returns the a succesful Result in case is valid, `CompileError` in case is not. diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index b436c2da8d6..d61c3958ed8 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -28,11 +28,11 @@ use wasmer_types::MetadataHeader; use wasmer_types::SerializableCompilation; use wasmer_types::{ CompileError, CpuFeature, DataInitializer, DeserializeError, FunctionIndex, LocalFunctionIndex, - MemoryIndex, ModuleInfo, OwnedDataInitializer, SerializableModule, SerializeError, - SignatureIndex, TableIndex, + MemoryIndex, ModuleInfo, OwnedDataInitializer, SignatureIndex, TableIndex, }; #[cfg(feature = "static-artifact-create")] use wasmer_types::{CompileModuleInfo, Target}; +use wasmer_types::{SerializableModule, SerializeError}; use wasmer_vm::{FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex, VMTrampoline}; use wasmer_vm::{InstanceAllocator, InstanceHandle, StoreObjects, TrapHandlerFn, VMExtern}; @@ -83,6 +83,7 @@ impl Artifact { engine.target(), memory_styles, table_styles, + tunables.module_start(), )?; Self::from_parts(&mut inner_engine, artifact) @@ -684,6 +685,7 @@ impl Artifact { compile_info: metadata.compile_info, data_initializers: metadata.data_initializers, cpu_features: metadata.cpu_features, + module_start: None, }); let finished_function_lengths = finished_functions diff --git a/lib/compiler/src/engine/code_memory.rs b/lib/compiler/src/engine/code_memory.rs index 9c033347032..2cb3ef79995 100644 --- a/lib/compiler/src/engine/code_memory.rs +++ b/lib/compiler/src/engine/code_memory.rs @@ -39,7 +39,7 @@ impl CodeMemory { &mut self.unwind_registry } - /// Allocate a single contiguous block of memory for the functions and custom sections, and copy the data in place. + /// Allocate a single contiguous block of memory at a fixed virtual address for the functions and custom sections, and copy the data in place. #[allow(clippy::type_complexity)] pub fn allocate( &mut self, diff --git a/lib/compiler/src/engine/inner.rs b/lib/compiler/src/engine/inner.rs index 84e8774222f..0347b87b729 100644 --- a/lib/compiler/src/engine/inner.rs +++ b/lib/compiler/src/engine/inner.rs @@ -42,6 +42,7 @@ pub struct Engine { engine_id: EngineId, #[cfg(not(target_arch = "wasm32"))] tunables: Arc, + name: String, } impl Engine { @@ -54,9 +55,11 @@ impl Engine { ) -> Self { #[cfg(not(target_arch = "wasm32"))] let tunables = BaseTunables::for_target(&target); + let compiler = compiler_config.compiler(); + let name = format!("engine-{}", compiler.name()); Self { inner: Arc::new(Mutex::new(EngineInner { - compiler: Some(compiler_config.compiler()), + compiler: Some(compiler), features, #[cfg(not(target_arch = "wasm32"))] code_memory: vec![], @@ -67,9 +70,15 @@ impl Engine { engine_id: EngineId::default(), #[cfg(not(target_arch = "wasm32"))] tunables: Arc::new(tunables), + name, } } + /// Returns the name of this engine + pub fn name(&self) -> &str { + self.name.as_str() + } + /// Create a headless `Engine` /// /// A headless engine is an engine without any compiler attached. @@ -102,6 +111,7 @@ impl Engine { engine_id: EngineId::default(), #[cfg(not(target_arch = "wasm32"))] tunables: Arc::new(tunables), + name: "engine-headless".to_string(), } } @@ -223,6 +233,16 @@ impl AsEngineRef for Engine { } } +impl std::fmt::Debug for Engine { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Engine") + .field("target", &self.target) + .field("engine_id", &self.engine_id) + .field("name", &self.name) + .finish() + } +} + /// The inner contents of `Engine` pub struct EngineInner { #[cfg(feature = "compiler")] diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index e1c0dab5e62..59ef659d379 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -45,7 +45,7 @@ struct RuntimeErrorInner { /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). wasm_trace: Vec, /// The native backtrace - native_trace: Backtrace, + native_trace: Option, } fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { @@ -191,7 +191,7 @@ impl RuntimeError { inner: Arc::new(RuntimeErrorInner { source, wasm_trace, - native_trace, + native_trace: Some(native_trace), }), } } diff --git a/lib/compiler/src/engine/tunables.rs b/lib/compiler/src/engine/tunables.rs index d67f15bee3a..9eab1ce85f1 100644 --- a/lib/compiler/src/engine/tunables.rs +++ b/lib/compiler/src/engine/tunables.rs @@ -13,6 +13,11 @@ use wasmer_vm::{VMMemoryDefinition, VMTableDefinition}; /// An engine delegates the creation of memories, tables, and globals /// to a foreign implementor of this trait. pub trait Tunables { + /// Fixed virtual memory address for the compiled module + fn module_start(&self) -> Option { + None + } + /// Construct a `MemoryStyle` for the provided `MemoryType` fn memory_style(&self, memory: &MemoryType) -> MemoryStyle; @@ -267,3 +272,87 @@ impl Tunables for BaseTunables { VMTable::from_definition(ty, style, vm_definition_location) } } + +impl Tunables for Box { + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + self.as_ref().memory_style(memory) + } + + fn table_style(&self, table: &TableType) -> TableStyle { + self.as_ref().table_style(table) + } + + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result { + self.as_ref().create_host_memory(ty, style) + } + + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_memory(ty, style, vm_definition_location) + } + + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + self.as_ref().create_host_table(ty, style) + } + + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_table(ty, style, vm_definition_location) + } +} + +impl Tunables for std::sync::Arc { + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + self.as_ref().memory_style(memory) + } + + fn table_style(&self, table: &TableType) -> TableStyle { + self.as_ref().table_style(table) + } + + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result { + self.as_ref().create_host_memory(ty, style) + } + + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_memory(ty, style, vm_definition_location) + } + + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + self.as_ref().create_host_table(ty, style) + } + + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_table(ty, style, vm_definition_location) + } +} diff --git a/lib/compiler/src/traits.rs b/lib/compiler/src/traits.rs index 8b0cb748794..170a436cf12 100644 --- a/lib/compiler/src/traits.rs +++ b/lib/compiler/src/traits.rs @@ -42,7 +42,7 @@ pub trait ArtifactCreate: Send + Sync + Upcastable { /// Serializes an artifact into a file path fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { let serialized = self.serialize()?; - fs::write(&path, serialized)?; + fs::write(path, serialized)?; Ok(()) } } diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index f60f0cc72dc..33571234e5a 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -23,9 +23,9 @@ use std::f64; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; use wasmer::{ - imports, namespace, AsStoreMut, ExportError, Exports, Function, FunctionEnv, FunctionEnvMut, - FunctionType, Global, Imports, Instance, Memory, MemoryType, Module, Pages, RuntimeError, - Table, TableType, TypedFunction, Value, WasmPtr, + imports, namespace, AsStoreMut, AsStoreRef, ExportError, Exports, Function, FunctionEnv, + FunctionEnvMut, FunctionType, Global, Imports, Instance, Memory, MemoryType, Module, Pages, + RuntimeError, Table, TableType, TypedFunction, Value, WasmPtr, }; use wasmer_types::Type as ValType; @@ -131,7 +131,7 @@ impl EmEnv { } } - pub fn set_memory(&mut self, memory: Memory) { + pub fn set_memory(&self, memory: Memory) { let mut w = self.memory.write().unwrap(); *w = Some(memory); } @@ -141,15 +141,12 @@ impl EmEnv { (*self.memory.read().unwrap()).as_ref().cloned().unwrap() } - pub fn set_functions(&mut self, funcs: EmscriptenFunctions) { - self.funcs = Arc::new(Mutex::new(funcs)); + pub fn set_functions(&self, funcs: EmscriptenFunctions) { + let mut w = self.funcs.lock().unwrap(); + *w = funcs; } - pub fn set_data( - &mut self, - data: &EmscriptenGlobalsData, - mapped_dirs: HashMap, - ) { + pub fn set_data(&self, data: &EmscriptenGlobalsData, mapped_dirs: HashMap) { let mut w = self.data.lock().unwrap(); *w = Some(EmscriptenData::new(data.clone(), mapped_dirs)); } @@ -887,7 +884,7 @@ pub fn run_emscripten_instance( if let Ok(func) = instance.exports.get_typed_function(&env, "setThrew") { emfuncs.set_threw = Some(func); } - env.data_mut().set_functions(emfuncs); + env.data().set_functions(emfuncs); set_up_emscripten(&mut env, instance)?; @@ -928,12 +925,12 @@ fn store_module_arguments(env: &mut FunctionEnvMut, args: Vec<&str>) -> ( } pub fn emscripten_set_up_memory( - store: &mut impl AsStoreMut, + store: &impl AsStoreRef, env: &FunctionEnv, memory: &Memory, globals: &EmscriptenGlobalsData, ) -> Result<(), String> { - env.as_mut(store).set_memory(memory.clone()); + env.as_ref(store).set_memory(memory.clone()); let memory = memory.view(store); let dynamictop_ptr = WasmPtr::::new(globals.dynamictop_ptr).deref(&memory); let dynamic_base = globals.dynamic_base; diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index 2c905e30c88..36ee1299c81 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -110,6 +110,8 @@ pub fn sbrk(mut ctx: FunctionEnvMut, increment: i32) -> i32 { increment, total_memory ); + drop(dynamictop_ptr); + if increment > 0 && new_dynamic_top < old_dynamic_top || new_dynamic_top < 0 { abort_on_cannot_grow_memory_old(ctx); return -1; diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 4eebc53e25e..da659cacccb 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -62,6 +62,7 @@ mod libcalls; mod memory; mod module; mod serialize; +mod store; mod table; mod trapcode; mod types; @@ -105,7 +106,7 @@ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; pub use crate::memory::MemoryStyle; pub use crate::table::TableStyle; -pub use crate::trapcode::TrapCode; +pub use crate::trapcode::{OnCalledAction, TrapCode}; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; pub use crate::utils::is_wasm; @@ -128,6 +129,8 @@ pub use crate::compilation::symbols::{Symbol, SymbolRegistry}; pub use crate::compilation::trap::TrapInformation; pub use crate::compilation::unwind::CompiledFunctionUnwindInfo; +pub use crate::store::StoreSnapshot; + /// Offset in bytes from the beginning of the function. pub type CodeOffset = u32; diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 2c986d90870..415d2f88cd2 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -59,6 +59,8 @@ pub unsafe trait MemorySize: Copy { + PartialOrd + Clone + Copy + + Sync + + Send + ValueType + Into + From @@ -76,7 +78,8 @@ pub unsafe trait MemorySize: Copy { + TryFrom + Add + Sum - + AddAssign; + + AddAssign + + 'static; /// Type used to pass this value as an argument or return value for a Wasm function. type Native: super::NativeWasmType; diff --git a/lib/types/src/serialize.rs b/lib/types/src/serialize.rs index 7df4fca9b5f..4b8d3be2bb4 100644 --- a/lib/types/src/serialize.rs +++ b/lib/types/src/serialize.rs @@ -1,4 +1,5 @@ use crate::entity::PrimaryMap; +use crate::Pages; use crate::{ compilation::target::CpuFeature, CompileModuleInfo, CompiledFunctionFrameInfo, CustomSection, DeserializeError, Dwarf, Features, FunctionBody, FunctionIndex, LocalFunctionIndex, @@ -61,6 +62,8 @@ pub struct SerializableModule { pub data_initializers: Box<[OwnedDataInitializer]>, /// CPU Feature flags for this compilation pub cpu_features: u64, + /// The start memory address of this serializable module + pub module_start: Option, } fn to_serialize_error(err: impl std::error::Error) -> SerializeError { @@ -160,7 +163,7 @@ impl SerializableModule { /// Serializes an artifact into a file path pub fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { let serialized = self.serialize()?; - fs::write(&path, serialized)?; + fs::write(path, serialized)?; Ok(()) } } diff --git a/lib/types/src/store.rs b/lib/types/src/store.rs new file mode 100644 index 00000000000..cd8fbc68c40 --- /dev/null +++ b/lib/types/src/store.rs @@ -0,0 +1,72 @@ +use std::collections::HashMap; +use std::io::Read; + +/// Represents a snapshot of parts of the store that mutate +/// (such as globals and tables) +#[derive(Debug, Default)] +pub struct StoreSnapshot { + /// Global values at the time the snapshot was taken + pub globals: HashMap, +} + +impl StoreSnapshot { + /// Serializes the snapshot into a set of bytes + pub fn serialize(&self) -> Vec { + let capacity = 32usize * self.globals.len(); + let mut ret = Vec::with_capacity(capacity); + + ret.extend_from_slice(&1u32.to_le_bytes()); + ret.extend_from_slice(&(self.globals.len() as u32).to_le_bytes()); + for (index, val) in self.globals.iter() { + ret.extend_from_slice(&index.to_le_bytes()); + ret.extend_from_slice(&val.to_le_bytes()); + } + ret + } + + /// Deserializes the bytes back into a store snapshot + pub fn deserialize(data: &[u8]) -> std::io::Result { + let mut ret = Self::default(); + + // Read all the sections + let mut reader = data; + loop { + let mut ty_arr = [0u8; 4]; + if let Err(err) = reader.read_exact(&mut ty_arr) { + if err.kind() == std::io::ErrorKind::UnexpectedEof { + break; + } + return Err(err); + } + + let ty = u32::from_le_bytes(ty_arr); + match ty { + 1u32 => { + // Read all the globals + let mut len_arr = [0u8; 4]; + reader.read_exact(&mut len_arr)?; + let len = u32::from_le_bytes(len_arr) as usize; + for _ in 0..len { + // Read the key + let mut key_arr = [0u8; 4]; + reader.read_exact(&mut key_arr)?; + let key = u32::from_le_bytes(key_arr); + + // Read the value + let mut val_arr = [0u8; 16]; + reader.read_exact(&mut val_arr)?; + let val = u128::from_le_bytes(val_arr); + + // Set the value in the snapshot + ret.globals.insert(key, val); + } + } + _ => { + break; + } + } + } + + Ok(ret) + } +} diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 7403f0f3e56..134a4b3ced1 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -120,6 +120,18 @@ impl FromStr for TrapCode { } } +/// After the stack is unwound via asyncify what +/// should the call loop do next +#[derive(Debug)] +pub enum OnCalledAction { + /// Will call the function again + InvokeAgain, + /// Will return the result of the invocation + Finish, + /// Traps with an error + Trap(Box), +} + #[cfg(test)] mod tests { use super::*; diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index 3a59d1db9c2..7a03b76cb2b 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -25,6 +25,9 @@ scopeguard = "1.1.0" lazy_static = "1.4.0" region = { version = "3.0" } corosensei = { version = "0.1.2" } +derivative = { version = "^2" } +# - Optional shared dependencies. +tracing = { version = "0.1", optional = true } [target.'cfg(target_vendor = "apple")'.dependencies] mach = "0.3.2" diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index 68427062f7c..d9df605e8ab 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -7,6 +7,7 @@ use crate::store::InternalStoreHandle; use crate::table::VMTable; use crate::vmcontext::VMFunctionKind; use crate::{MaybeInstanceOwned, VMCallerCheckedAnyfunc}; +use derivative::Derivative; use std::any::Any; use wasmer_types::FunctionType; @@ -26,9 +27,12 @@ pub enum VMExtern { } /// A function export value. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMFunction { /// Pointer to the `VMCallerCheckedAnyfunc` which contains data needed to /// call the function and check its signature. + #[derivative(Debug = "ignore")] pub anyfunc: MaybeInstanceOwned, /// The function type, used for compatibility checking. @@ -39,5 +43,6 @@ pub struct VMFunction { pub kind: VMFunctionKind, /// Associated data owned by a host function. + #[derivative(Debug = "ignore")] pub host_data: Box, } diff --git a/lib/vm/src/extern_ref.rs b/lib/vm/src/extern_ref.rs index f99a7e93f51..79f71b876b2 100644 --- a/lib/vm/src/extern_ref.rs +++ b/lib/vm/src/extern_ref.rs @@ -1,11 +1,14 @@ +use derivative::Derivative; use std::any::Any; - use wasmer_types::RawValue; use crate::store::InternalStoreHandle; /// Underlying object referenced by a `VMExternRef`. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMExternObj { + #[derivative(Debug = "ignore")] contents: Box, } diff --git a/lib/vm/src/function_env.rs b/lib/vm/src/function_env.rs index ccedf04385e..f2f8b728408 100644 --- a/lib/vm/src/function_env.rs +++ b/lib/vm/src/function_env.rs @@ -1,7 +1,11 @@ +use derivative::Derivative; use std::any::Any; /// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMFunctionEnvironment { + #[derivative(Debug = "ignore")] contents: Box, } diff --git a/lib/vm/src/global.rs b/lib/vm/src/global.rs index 682a66fb294..a899f7cd64c 100644 --- a/lib/vm/src/global.rs +++ b/lib/vm/src/global.rs @@ -1,10 +1,14 @@ use crate::{store::MaybeInstanceOwned, vmcontext::VMGlobalDefinition}; +use derivative::Derivative; use std::{cell::UnsafeCell, ptr::NonNull}; -use wasmer_types::GlobalType; +use wasmer_types::{GlobalType, StoreSnapshot}; /// A Global instance +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMGlobal { ty: GlobalType, + #[derivative(Debug = "ignore")] vm_global_definition: MaybeInstanceOwned, } @@ -30,4 +34,37 @@ impl VMGlobal { pub fn vmglobal(&self) -> NonNull { self.vm_global_definition.as_ptr() } + + /// Copies this global + pub fn copy_on_write(&self) -> Self { + unsafe { + Self { + ty: self.ty, + vm_global_definition: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new( + self.vm_global_definition.as_ptr().as_ref().clone(), + ))), + } + } + } + + /// Saves the global value into the snapshot + pub fn save_snapshot(&self, index: usize, snapshot: &mut StoreSnapshot) { + let entry = snapshot.globals.entry(index as u32).or_default(); + + let val = unsafe { self.vm_global_definition.as_ptr().as_ref().val.u128 }; + *entry = val; + } + + /// Restores the global value from the snapshot + pub fn restore_snapshot(&mut self, index: usize, snapshot: &StoreSnapshot) { + let index = index as u32; + if let Some(entry) = snapshot.globals.get(&index) { + let existing = unsafe { self.vm_global_definition.as_ptr().as_ref().val.u128 }; + if existing != *entry { + unsafe { + self.vm_global_definition.as_ptr().as_mut().val.u128 = *entry; + } + } + } + } } diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index ead0e9e27f4..ae3f360e871 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -1,5 +1,6 @@ use super::{Instance, InstanceHandle}; -use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition}; +use crate::vmcontext::VMTableDefinition; +use crate::VMMemoryDefinition; use std::alloc::{self, Layout}; use std::convert::TryFrom; use std::mem; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 08678aa37e0..1ca89c00135 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -116,6 +116,27 @@ impl WasmMmap { Ok(prev_pages) } + + /// Copies the memory + /// (in this case it performs a copy-on-write to save memory) + pub fn fork(&mut self) -> Result { + let mem_length = self.size.bytes().0; + let mut alloc = self + .alloc + .fork(Some(mem_length)) + .map_err(MemoryError::Generic)?; + let base_ptr = alloc.as_mut_ptr(); + Ok(Self { + vm_memory_definition: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new( + VMMemoryDefinition { + base: base_ptr, + current_length: mem_length, + }, + ))), + alloc, + size: self.size, + }) + } } /// A linear memory instance. @@ -157,18 +178,6 @@ pub struct VMOwnedMemory { unsafe impl Send for VMOwnedMemory {} unsafe impl Sync for VMOwnedMemory {} -/// A shared linear memory instance. -#[derive(Debug, Clone)] -pub struct VMSharedMemory { - // The underlying allocation. - mmap: Arc>, - // Configuration of this memory - config: VMMemoryConfig, -} - -unsafe impl Send for VMSharedMemory {} -unsafe impl Sync for VMSharedMemory {} - impl VMOwnedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// @@ -270,9 +279,7 @@ impl VMOwnedMemory { }, }) } -} -impl VMOwnedMemory { /// Converts this owned memory into shared memory pub fn to_shared(self) -> VMSharedMemory { VMSharedMemory { @@ -280,6 +287,14 @@ impl VMOwnedMemory { config: self.config, } } + + /// Copies this memory to a new memory + pub fn fork(&mut self) -> Result { + Ok(Self { + mmap: self.mmap.fork()?, + config: self.config.clone(), + }) + } } impl LinearMemory for VMOwnedMemory { @@ -316,8 +331,26 @@ impl LinearMemory for VMOwnedMemory { fn try_clone(&self) -> Option> { None } + + /// Copies this memory to a new memory + fn fork(&mut self) -> Result, MemoryError> { + let forked = Self::fork(self)?; + Ok(Box::new(forked)) + } +} + +/// A shared linear memory instance. +#[derive(Debug, Clone)] +pub struct VMSharedMemory { + // The underlying allocation. + mmap: Arc>, + // Configuration of this memory + config: VMMemoryConfig, } +unsafe impl Send for VMSharedMemory {} +unsafe impl Sync for VMSharedMemory {} + impl VMSharedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// @@ -341,6 +374,15 @@ impl VMSharedMemory { ) -> Result { Ok(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?.to_shared()) } + + /// Copies this memory to a new memory + pub fn fork(&mut self) -> Result { + let mut guard = self.mmap.write().unwrap(); + Ok(Self { + mmap: Arc::new(RwLock::new(guard.fork()?)), + config: self.config.clone(), + }) + } } impl LinearMemory for VMSharedMemory { @@ -379,9 +421,15 @@ impl LinearMemory for VMSharedMemory { guard.vm_memory_definition.as_ptr() } - /// Owned memory can not be cloned (this will always return None) + /// Shared memory can always be cloned fn try_clone(&self) -> Option> { - None + Some(Box::new(self.clone())) + } + + /// Copies this memory to a new memory + fn fork(&mut self) -> Result, MemoryError> { + let forked = Self::fork(self)?; + Ok(Box::new(forked)) } } @@ -445,6 +493,11 @@ impl LinearMemory for VMMemory { unsafe fn initialize_with_data(&self, start: usize, data: &[u8]) -> Result<(), Trap> { self.0.initialize_with_data(start, data) } + + /// Copies this memory to a new memory + fn fork(&mut self) -> Result, MemoryError> { + self.0.fork() + } } impl VMMemory { @@ -497,12 +550,17 @@ impl VMMemory { /// are natively supported /// - VMOwnedMemory -> VMMemory /// - Box -> VMMemory - pub fn from_custom(memory: IntoVMMemory) -> VMMemory + pub fn from_custom(memory: IntoVMMemory) -> Self where - IntoVMMemory: Into, + IntoVMMemory: Into, { memory.into() } + + /// Copies this memory to a new memory + pub fn fork(&mut self) -> Result, MemoryError> { + LinearMemory::fork(self) + } } #[doc(hidden)] @@ -555,4 +613,7 @@ where initialize_memory_with_data(memory, start, data) } + + /// Copies this memory to a new memory + fn fork(&mut self) -> Result, MemoryError>; } diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 6b3dcdd19cd..f5440cba851 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -9,6 +9,8 @@ use more_asserts::assert_lt; use std::io; use std::ptr; use std::slice; +#[cfg(feature = "tracing")] +use tracing::trace; /// Round `size` up to the nearest multiple of `page_size`. fn round_up_to_page_size(size: usize, page_size: usize) -> usize { @@ -25,6 +27,34 @@ pub struct Mmap { // the coordination all happens at the OS layer. ptr: usize, len: usize, + // Backing file that will be closed when the memory mapping goes out of scope + fd: FdGuard, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FdGuard(pub i32); + +impl Default for FdGuard { + fn default() -> Self { + Self(-1) + } +} + +impl Clone for FdGuard { + fn clone(&self) -> Self { + unsafe { Self(libc::dup(self.0)) } + } +} + +impl Drop for FdGuard { + fn drop(&mut self) { + if self.0 >= 0 { + unsafe { + libc::close(self.0); + } + self.0 = -1; + } + } } impl Mmap { @@ -37,6 +67,7 @@ impl Mmap { Self { ptr: empty.as_ptr() as usize, len: 0, + fd: FdGuard::default(), } } @@ -66,6 +97,28 @@ impl Mmap { return Ok(Self::new()); } + // Open a temporary file (which is used for swapping) + let fd = unsafe { + let file = libc::tmpfile(); + if file.is_null() { + return Err(format!( + "failed to create temporary file - {}", + io::Error::last_os_error() + )); + } + FdGuard(libc::fileno(file)) + }; + + // First we initialize it with zeros + unsafe { + if libc::ftruncate(fd.0, mapping_size as libc::off_t) < 0 { + return Err("could not truncate tmpfile".to_string()); + } + } + + // Compute the flags + let flags = libc::MAP_FILE | libc::MAP_SHARED; + Ok(if accessible_size == mapping_size { // Allocate a single read-write region at once. let ptr = unsafe { @@ -73,8 +126,8 @@ impl Mmap { ptr::null_mut(), mapping_size, libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_PRIVATE | libc::MAP_ANON, - -1, + flags, + fd.0, 0, ) }; @@ -85,6 +138,7 @@ impl Mmap { Self { ptr: ptr as usize, len: mapping_size, + fd, } } else { // Reserve the mapping size. @@ -93,8 +147,8 @@ impl Mmap { ptr::null_mut(), mapping_size, libc::PROT_NONE, - libc::MAP_PRIVATE | libc::MAP_ANON, - -1, + flags, + fd.0, 0, ) }; @@ -105,6 +159,7 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, len: mapping_size, + fd, }; if accessible_size != 0 { @@ -137,8 +192,7 @@ impl Mmap { if mapping_size == 0 { return Ok(Self::new()); } - - Ok(if accessible_size == mapping_size { + if accessible_size == mapping_size { // Allocate a single read-write region at once. let ptr = unsafe { VirtualAlloc( @@ -152,10 +206,11 @@ impl Mmap { return Err(io::Error::last_os_error().to_string()); } - Self { + Ok(Self { ptr: ptr as usize, len: mapping_size, - } + fd: FdGuard::default(), + }) } else { // Reserve the mapping size. let ptr = @@ -167,6 +222,7 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, len: mapping_size, + fd: FdGuard::default(), }; if accessible_size != 0 { @@ -174,8 +230,8 @@ impl Mmap { result.make_accessible(0, accessible_size)?; } - result - }) + Ok(result) + } } /// Make the memory starting at `start` and extending for `len` bytes accessible. @@ -256,6 +312,135 @@ impl Mmap { pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Copies the memory to a new swap file (using copy-on-write if available) + #[cfg(not(target_os = "windows"))] + pub fn fork(&mut self, hint_used: Option) -> Result { + // Empty memory is an edge case + if self.len == 0 { + return Ok(Self::new()); + } + + // First we sync all the data to the backing file + unsafe { + libc::fsync(self.fd.0); + } + + // Open a new temporary file (which is used for swapping for the forked memory) + let fd = unsafe { + let file = libc::tmpfile(); + if file.is_null() { + return Err(format!( + "failed to create temporary file - {}", + io::Error::last_os_error() + )); + } + FdGuard(libc::fileno(file)) + }; + + // Attempt to do a shallow copy (needs a backing file system that supports it) + unsafe { + if libc::ioctl(fd.0, 0x94, 9, self.fd.0) != 0 + // FICLONE + { + #[cfg(feature = "tracing")] + trace!("memory copy started"); + + // Determine host much to copy + let len = match hint_used { + Some(a) => a, + None => self.len, + }; + + // The shallow copy failed so we have to do it the hard way + let mut off_in: libc::off_t = 0; + let mut off_out: libc::off_t = 0; + #[cfg(not(target_vendor = "apple"))] + let ret = libc::copy_file_range(self.fd.0, &mut off_in, fd.0, &mut off_out, len, 0); + + // FIXME: implement better copy on Apple targets. + // Mac libc does not have copy_file_range. + // It does have fcopyfile, which we should probably use. + // This is just a quick fix to get it working. + #[cfg(target_vendor = "apple")] + let ret = { + use std::{io::Seek, os::unix::io::FromRawFd}; + + let mut f1 = std::fs::File::from_raw_fd(self.fd.0); + let pos = f1.stream_position().map_err(|err| err.to_string())?; + + let mut f2 = std::fs::File::from_raw_fd(fd.0); + std::io::copy(&mut f1, &mut f2).map_err(|err| err.to_string())?; + f2.seek(std::io::SeekFrom::Start(0)) + .map_err(|err| err.to_string())?; + f1.seek(std::io::SeekFrom::Start(pos)) + .map_err(|err| err.to_string())?; + 0 + }; + + if ret < 0 { + return Err(format!( + "failed to copy temporary file data - {}", + io::Error::last_os_error() + )); + } + + #[cfg(feature = "tracing")] + trace!("memory copy finished (size={})", len); + } + } + + // Compute the flags + let flags = libc::MAP_FILE | libc::MAP_SHARED; + + // Allocate a single read-write region at once. + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + self.len, + libc::PROT_READ | libc::PROT_WRITE, + flags, + fd.0, + 0, + ) + }; + if ptr as isize == -1_isize { + return Err(io::Error::last_os_error().to_string()); + } + + Ok(Self { + ptr: ptr as usize, + len: self.len, + fd, + }) + } + + /// Copies the memory to a new swap file (using copy-on-write if available) + #[cfg(target_os = "windows")] + pub fn fork(&mut self, hint_used: Option) -> Result { + // Create a new memory which we will copy to + let new_mmap = Self::with_at_least(self.len)?; + + #[cfg(feature = "tracing")] + trace!("memory copy started"); + + // Determine host much to copy + let len = match hint_used { + Some(a) => a, + None => self.len, + }; + + // Copy the data to the new memory + let dst = new_mmap.ptr as *mut u8; + let src = self.ptr as *const u8; + unsafe { + std::ptr::copy_nonoverlapping(src, dst, len); + } + + #[cfg(feature = "tracing")] + trace!("memory copy finished (size={})", len); + Ok(new_mmap) + } } impl Drop for Mmap { diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 69ed19856c7..3eeecfb6df8 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -7,6 +7,8 @@ use std::{ sync::atomic::{AtomicU64, Ordering}, }; +use wasmer_types::StoreSnapshot; + use crate::VMExternObj; use crate::{InstanceHandle, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable}; @@ -60,7 +62,7 @@ impl_context_object! { } /// Set of objects managed by a context. -#[derive(Default)] +#[derive(Debug, Default)] pub struct StoreObjects { id: StoreId, memories: Vec, @@ -101,6 +103,22 @@ impl StoreObjects { (&mut high[0], &mut low[a.index()]) } } + + /// Serializes the mutable things into a snapshot + pub fn save_snapshot(&self) -> StoreSnapshot { + let mut ret = StoreSnapshot::default(); + for (index, global) in self.globals.iter().enumerate() { + global.save_snapshot(index, &mut ret); + } + ret + } + + /// Serializes the mutable things into a snapshot + pub fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) { + for (index, global) in self.globals.iter_mut().enumerate() { + global.restore_snapshot(index, snapshot); + } + } } /// Handle to an object managed by a context. @@ -176,6 +194,11 @@ impl StoreHandle { self.id } + /// Overrides the store id with a new ID + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. /// /// # Safety diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index b29d8c883c4..00c2555c2a3 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -10,6 +10,7 @@ use crate::vmcontext::VMTableDefinition; use crate::Trap; use crate::VMExternRef; use crate::VMFuncRef; +use derivative::Derivative; use std::cell::UnsafeCell; use std::convert::TryFrom; use std::fmt; @@ -69,13 +70,17 @@ impl Default for TableElement { } /// A table instance. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMTable { + #[derivative(Debug = "ignore")] vec: Vec, maximum: Option, /// The WebAssembly table description. table: TableType, /// Our chosen implementation style. style: TableStyle, + #[derivative(Debug = "ignore")] vm_table_definition: MaybeInstanceOwned, } @@ -306,6 +311,14 @@ impl VMTable { Ok(()) } + /// Copies the table into a new table + pub fn copy_on_write(&self) -> Result { + let mut ret = Self::new(&self.table, &self.style)?; + ret.copy(self, 0, 0, self.size()) + .map_err(|trap| format!("failed to copy the table - {:?}", trap))?; + Ok(ret) + } + /// Copy `len` elements from `table[src_index..]` to `table[dst_index..]`. /// /// # Errors diff --git a/rust-toolchain b/rust-toolchain index 58e4eb6b299..8725364a8ec 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.63 +1.64 diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index d3a244b3e7d..4c7bb0c90d6 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -426,7 +426,7 @@ fn call_signature_mismatch(config: crate::Config) -> Result<()> { ) "#; - let module = Module::new(&store, &binary)?; + let module = Module::new(&store, binary)?; let err = Instance::new(&mut store, &module, &imports! {}).expect_err("expected error"); assert_eq!( format!("{}", err), diff --git a/tests/compilers/typed_functions.rs b/tests/compilers/typed_functions.rs index 93fdd6db4d7..bdf40dd44ae 100644 --- a/tests/compilers/typed_functions.rs +++ b/tests/compilers/typed_functions.rs @@ -309,7 +309,7 @@ fn static_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { let mut store = config.store(); fn f(mut env: FunctionEnvMut, a: i32, b: i64, c: f32, d: f64) -> (f64, f32, i64, i32) { - let mut guard = env.data_mut().0.lock().unwrap(); + let mut guard = env.data().0.lock().unwrap(); assert_eq!(*guard, 100); *guard = 101; @@ -323,7 +323,7 @@ fn static_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { c: f32, d: f64, ) -> Result<(f64, f32, i64, i32), Infallible> { - let mut guard = env.data_mut().0.lock().unwrap(); + let mut guard = env.data().0.lock().unwrap(); assert_eq!(*guard, 100); *guard = 101; @@ -448,7 +448,7 @@ fn dynamic_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { ], ), |mut env, values| { - let mut guard = env.data_mut().0.lock().unwrap(); + let mut guard = env.data().0.lock().unwrap(); assert_eq!(*guard, 100); *guard = 101; From 838bfe2c718d680f2706084c1926e99a5abf67d0 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 12:22:01 +0100 Subject: [PATCH 02/56] Make Compiler::name() optional Makes the new Compiler::name() method optional by adding a default implementation. This avoid a breaking public API change. --- lib/compiler/src/compiler.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/compiler/src/compiler.rs b/lib/compiler/src/compiler.rs index cb1d35f04cb..03f760241f0 100644 --- a/lib/compiler/src/compiler.rs +++ b/lib/compiler/src/compiler.rs @@ -79,8 +79,11 @@ where /// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. pub trait Compiler: Send { - /// Gets the name of this compiler - fn name(&self) -> &str; + /// Returns a descriptive name for this compiler. + // TODO!: Probably want to make this mandatory on the next major bump. + fn name(&self) -> &str { + "UNKNOWN" + } /// Validates a module. /// From 2cd094b5ed75bfa739cba5837a863e206fa7cd48 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 12:40:56 +0100 Subject: [PATCH 03/56] Remove un-used Import/ExportError variant SerializationFailed Wasn't used anywhere. Maybe just a botched rebase leftover. --- lib/api/src/js/exports.rs | 3 --- lib/api/src/sys/exports.rs | 3 --- 2 files changed, 6 deletions(-) diff --git a/lib/api/src/js/exports.rs b/lib/api/src/js/exports.rs index 339095bafa0..024ab8b9696 100644 --- a/lib/api/src/js/exports.rs +++ b/lib/api/src/js/exports.rs @@ -53,9 +53,6 @@ pub enum ExportError { /// This error arises when an export is missing #[error("Missing export {0}")] Missing(String), - /// This error arises when an export is missing - #[error("Serialization failed {0}")] - SerializationFailed(String), } /// Exports is a special kind of map that allows easily unwrapping diff --git a/lib/api/src/sys/exports.rs b/lib/api/src/sys/exports.rs index 0141f8cd9ea..571419deefb 100644 --- a/lib/api/src/sys/exports.rs +++ b/lib/api/src/sys/exports.rs @@ -57,9 +57,6 @@ pub enum ExportError { /// This error arises when an export is missing #[error("Missing export {0}")] Missing(String), - /// This error arises when an export is missing - #[error("Serialization failed {0}")] - SerializationFailed(String), } /// Exports is a special kind of map that allows easily unwrapping From 7c679055d82b6350cd65a352766778bc6910b3e9 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 13:17:40 +0100 Subject: [PATCH 04/56] Rewrite some use statements Smaller diff, and more sensible that way. --- lib/api/src/sys/externals/global.rs | 1 - lib/api/src/sys/externals/memory.rs | 4 +--- lib/api/src/sys/externals/table.rs | 1 - lib/api/src/sys/imports.rs | 6 ++---- lib/api/src/sys/instance.rs | 1 - lib/api/src/sys/value.rs | 4 +--- 6 files changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/api/src/sys/externals/global.rs b/lib/api/src/sys/externals/global.rs index e43725ec265..2dfc77ff06d 100644 --- a/lib/api/src/sys/externals/global.rs +++ b/lib/api/src/sys/externals/global.rs @@ -4,7 +4,6 @@ use crate::sys::store::{AsStoreMut, AsStoreRef}; use crate::sys::value::Value; use crate::sys::GlobalType; use crate::sys::Mutability; -#[cfg(feature = "compiler")] use crate::sys::RuntimeError; use wasmer_vm::{InternalStoreHandle, StoreHandle, VMExtern, VMGlobal}; diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 67cf908d4d9..fddcb2995ab 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -11,8 +11,6 @@ use std::slice; #[cfg(feature = "tracing")] use tracing::warn; use wasmer_types::Pages; -#[cfg(feature = "compiler")] -use wasmer_types::WASM_PAGE_SIZE; use wasmer_vm::{InternalStoreHandle, LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory}; use super::MemoryView; @@ -151,7 +149,7 @@ impl Memory { let new_view_size = new_view.data_size() as usize; if amount > new_view_size { let delta = amount - new_view_size; - let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1; new_memory.grow(new_store, Pages(pages as u32))?; new_view = new_memory.view(&new_store); } diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs index 56031e1d462..52137330846 100644 --- a/lib/api/src/sys/externals/table.rs +++ b/lib/api/src/sys/externals/table.rs @@ -3,7 +3,6 @@ use crate::sys::externals::Extern; use crate::sys::store::{AsStoreMut, AsStoreRef}; use crate::sys::TableType; use crate::Value; -#[cfg(feature = "compiler")] use crate::{sys::RuntimeError, ExternRef, Function}; use wasmer_vm::{InternalStoreHandle, StoreHandle, TableElement, VMExtern, VMTable}; diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/sys/imports.rs index f2ceba1ba31..2730c13735e 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/sys/imports.rs @@ -1,8 +1,6 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. -#[cfg(feature = "compiler")] -use crate::{AsStoreMut, Memory}; use crate::{Exports, Extern, Module}; use std::collections::HashMap; use std::fmt; @@ -131,7 +129,7 @@ impl Imports { pub fn import_shared_memory( &mut self, module: &Module, - store: &mut impl AsStoreMut, + store: &mut impl crate::AsStoreMut, ) -> Option { // Determine if shared memory needs to be created and imported let shared_memory = module @@ -148,7 +146,7 @@ impl Imports { self.define( "env", "memory", - Memory::new_from_existing(store, memory.clone().into()), + crate::Memory::new_from_existing(store, memory.clone().into()), ); Some(memory) } else { diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index 4ad4d797096..a3df77d1445 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -1,6 +1,5 @@ use crate::sys::exports::Exports; use crate::sys::module::Module; -#[cfg(feature = "compiler")] use crate::sys::{LinkError, RuntimeError}; use std::fmt; use thiserror::Error; diff --git a/lib/api/src/sys/value.rs b/lib/api/src/sys/value.rs index 1c00e4bbcf6..fe4fb5c4b5f 100644 --- a/lib/api/src/sys/value.rs +++ b/lib/api/src/sys/value.rs @@ -9,8 +9,6 @@ use wasmer_vm::{VMExternRef, VMFuncRef}; use crate::ExternRef; use crate::Function; -#[cfg(feature = "compiler")] -use super::store::AsStoreMut; use super::store::AsStoreRef; pub use wasmer_types::RawValue; @@ -113,7 +111,7 @@ impl Value { /// /// # Safety /// - pub unsafe fn from_raw(store: &mut impl AsStoreMut, ty: Type, raw: RawValue) -> Self { + pub unsafe fn from_raw(store: &mut impl crate::AsStoreMut, ty: Type, raw: RawValue) -> Self { match ty { Type::I32 => Self::I32(raw.i32), Type::I64 => Self::I64(raw.i64), From 7f63cad239d82f9926cf32d153273d7ad1b0c419 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 13:18:25 +0100 Subject: [PATCH 05/56] Revert WasmPtr breaking changes They are not needed. --- lib/api/src/sys/ptr.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/api/src/sys/ptr.rs b/lib/api/src/sys/ptr.rs index 285688b8fff..1f51c203094 100644 --- a/lib/api/src/sys/ptr.rs +++ b/lib/api/src/sys/ptr.rs @@ -97,7 +97,7 @@ impl WasmPtr { /// Checks whether the `WasmPtr` is null. #[inline] - pub fn is_null(&self) -> bool { + pub fn is_null(self) -> bool { self.offset.into() == 0 } @@ -142,19 +142,19 @@ impl WasmPtr { /// Creates a `WasmRef` from this `WasmPtr` which allows reading and /// mutating of the value being pointed to. #[inline] - pub fn deref<'a>(&self, view: &'a MemoryView) -> WasmRef<'a, T> { + pub fn deref<'a>(self, view: &'a MemoryView) -> WasmRef<'a, T> { WasmRef::new(view, self.offset.into()) } /// Reads the address pointed to by this `WasmPtr` in a memory. #[inline] - pub fn read(&self, view: &MemoryView) -> Result { + pub fn read(self, view: &MemoryView) -> Result { self.deref(view).read() } /// Writes to the address pointed to by this `WasmPtr` in a memory. #[inline] - pub fn write(&self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { + pub fn write(self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { self.deref(view).write(val) } @@ -165,7 +165,7 @@ impl WasmPtr { /// address. #[inline] pub fn slice<'a>( - &self, + self, view: &'a MemoryView, len: M::Offset, ) -> Result, MemoryAccessError> { @@ -178,7 +178,7 @@ impl WasmPtr { /// This last value is not included in the returned vector. #[inline] pub fn read_until( - &self, + self, view: &MemoryView, mut end: impl FnMut(&T) -> bool, ) -> Result, MemoryAccessError> { @@ -202,7 +202,7 @@ impl WasmPtr { /// modified. #[inline] pub fn read_utf8_string( - &self, + self, view: &MemoryView, len: M::Offset, ) -> Result { @@ -215,10 +215,7 @@ impl WasmPtr { /// This method is safe to call even if the memory is being concurrently /// modified. #[inline] - pub fn read_utf8_string_with_nul( - &self, - view: &MemoryView, - ) -> Result { + pub fn read_utf8_string_with_nul(self, view: &MemoryView) -> Result { let vec = self.read_until(view, |&byte| byte == 0)?; Ok(String::from_utf8(vec)?) } From 7503fdacd35cb48d172ca7edc63a465c1b5614ac Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 13:32:58 +0100 Subject: [PATCH 06/56] Remove module_start from ArtifactBuild::new, provide builder instead Remove the added module_start argument in ArtifactBuild::new() to avoid a breaking change. Introduce a with_module_start() method instead. --- lib/compiler/src/artifact_builders/artifact_builder.rs | 9 +++++++-- lib/compiler/src/engine/artifact.rs | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/artifact_builders/artifact_builder.rs b/lib/compiler/src/artifact_builders/artifact_builder.rs index 69e68ac1bc5..e95061fb29b 100644 --- a/lib/compiler/src/artifact_builders/artifact_builder.rs +++ b/lib/compiler/src/artifact_builders/artifact_builder.rs @@ -43,7 +43,6 @@ impl ArtifactBuild { target: &Target, memory_styles: PrimaryMap, table_styles: PrimaryMap, - module_start: Option, ) -> Result { let environ = ModuleEnvironment::new(); let features = inner_engine.features().clone(); @@ -120,11 +119,17 @@ impl ArtifactBuild { compile_info, data_initializers, cpu_features: cpu_features.as_u64(), - module_start, + module_start: None, }; Ok(Self { serializable }) } + /// Specify the fixed virtual memory address for the compiled module + pub fn with_module_start(mut self, module_start: Option) -> Self { + self.serializable.module_start = module_start; + self + } + /// Compile a data buffer into a `ArtifactBuild`, which may then be instantiated. #[cfg(not(feature = "compiler"))] #[cfg(not(target_arch = "wasm32"))] diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index d61c3958ed8..35383189fec 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -83,8 +83,8 @@ impl Artifact { engine.target(), memory_styles, table_styles, - tunables.module_start(), - )?; + )? + .with_module_start(tunables.module_start()); Self::from_parts(&mut inner_engine, artifact) } From 27627c0823d6578bcff4f7156764ea37573238d6 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 13:51:34 +0100 Subject: [PATCH 07/56] Fix compiler CLI ArtifactBuild construction Argument was removed. --- lib/cli-compiler/src/commands/compile.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs index 822b26501ec..bcffabff54f 100644 --- a/lib/cli-compiler/src/commands/compile.rs +++ b/lib/cli-compiler/src/commands/compile.rs @@ -104,7 +104,6 @@ impl Compile { &target, memory_styles, table_styles, - None, )?; artifact.serialize_to_file(self.output.as_ref())?; eprintln!( From 9b6c0bb716d1dc5c583c1fe5f45a7503dd1e3a8c Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 20:20:50 +0100 Subject: [PATCH 08/56] Restore C api wasi from master Wasi changes are reserved for the full merge. --- lib/c-api/src/wasm_c_api/wasi/mod.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index ab1bc28b3e3..7d59e614144 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -328,6 +328,14 @@ pub unsafe extern "C" fn wasi_env_new( #[no_mangle] pub extern "C" fn wasi_env_delete(_state: Option>) {} +/// Set the memory on a [`wasi_env_t`]. +#[no_mangle] +pub unsafe extern "C" fn wasi_env_set_memory(env: &mut wasi_env_t, memory: &wasm_memory_t) { + let mut store_mut = env.store.store_mut(); + let wasi_env = env.inner.data_mut(&mut store_mut); + wasi_env.set_memory(memory.extern_.memory()); +} + #[no_mangle] pub unsafe extern "C" fn wasi_env_read_stdout( env: &mut wasi_env_t, @@ -335,8 +343,8 @@ pub unsafe extern "C" fn wasi_env_read_stdout( buffer_len: usize, ) -> isize { let inner_buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_len as usize); - let store = env.store.store(); - let state = env.inner.data(&store).state(); + let mut store_mut = env.store.store_mut(); + let state = env.inner.data_mut(&mut store_mut).state(); if let Ok(mut stdout) = state.stdout() { if let Some(stdout) = stdout.as_mut() { @@ -515,10 +523,11 @@ pub unsafe extern "C" fn wasi_env_initialize_instance( store: &mut wasm_store_t, instance: &mut wasm_instance_t, ) -> bool { + let mem = c_try!(instance.inner.exports.get_memory("memory"); otherwise false); wasi_env .inner - .initialize(&mut store.inner.store_mut(), &instance.inner) - .unwrap(); + .data_mut(&mut store.inner.store_mut()) + .set_memory(mem.clone()); true } From cd5dfe66699790be41f2b6651d0e26b4c991f2f9 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 20:44:58 +0100 Subject: [PATCH 09/56] Fix various clippy lints --- lib/api/src/js/externals/function.rs | 6 +++--- lib/api/src/sys/externals/function.rs | 6 +++--- lib/api/src/sys/imports.rs | 2 +- lib/api/src/sys/instance.rs | 12 ++---------- lib/api/src/sys/native.rs | 7 ++++--- lib/api/src/sys/store.rs | 13 +++++-------- lib/emscripten/src/memory.rs | 2 +- lib/wasi/src/syscalls/mod.rs | 2 +- 8 files changed, 20 insertions(+), 30 deletions(-) diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 92f1388dd61..7e480fd98f4 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -61,9 +61,9 @@ pub struct Function { pub(crate) handle: StoreHandle, } -impl Into for StoreHandle { - fn into(self) -> Function { - Function { handle: self } +impl From> for Function { + fn into(handle: StoreHandle) -> Self { + Self { handle } } } diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 92798a7564f..3fa62b6b43b 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -51,9 +51,9 @@ pub struct Function { pub(crate) handle: StoreHandle, } -impl Into for StoreHandle { - fn into(self) -> Function { - Function { handle: self } +impl From> for Function { + fn from(handle: StoreHandle) -> Self { + Self { handle } } } diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/sys/imports.rs index 2730c13735e..a08d1b6adad 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/sys/imports.rs @@ -194,7 +194,7 @@ impl Imports { } /// Iterates through all the imports in this structure - pub fn iter<'a>(&'a self) -> ImportsIterator<'a> { + pub fn iter(&self) -> ImportsIterator<'_> { ImportsIterator::new(self) } } diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index a3df77d1445..4e0d90d3f18 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -133,11 +133,7 @@ impl Instance { // If the memory is imported then also export it for backwards compatibility reasons // (many will assume the memory is always exported) - later we can remove this if exports.get_memory("memory").is_err() { - if let Some(memory) = externs - .iter() - .filter(|a| a.ty(store).memory().is_some()) - .next() - { + if let Some(memory) = externs.iter().find(|a| a.ty(store).memory().is_some()) { exports.insert("memory", memory.clone()); } } @@ -182,11 +178,7 @@ impl Instance { // If the memory is imported then also export it for backwards compatibility reasons // (many will assume the memory is always exported) - later we can remove this if exports.get_memory("memory").is_err() { - if let Some(memory) = externs - .iter() - .filter(|a| a.ty(store).memory().is_some()) - .next() - { + if let Some(memory) = externs.iter().find(|a| a.ty(store).memory().is_some()) { exports.insert("memory", memory.clone()); } } diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index a907cc7d603..04389e16513 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -13,8 +13,9 @@ use std::marker::PhantomData; use crate::sys::{ AsStoreMut, FromToNativeWasmType, Function, NativeWasmTypeInto, RuntimeError, WasmTypeList, }; -use crate::StoreMut; -use wasmer_types::{OnCalledAction, RawValue}; +use wasmer_types::RawValue; + +use super::store::OnCalledHandler; /// A WebAssembly function that can be called natively /// (using the Native ABI). @@ -48,7 +49,7 @@ impl Clone for TypedFunction } thread_local! { - static ON_CALLED: Cell) -> Result>>>> = Cell::new(None); + static ON_CALLED: Cell> = Cell::new(None); } macro_rules! impl_native_traits { diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index e777846838d..8032e13da2e 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -8,6 +8,10 @@ use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn}; use wasmer_vm::StoreObjects; +pub type OnCalledHandler = Box< + dyn FnOnce(StoreMut<'_>) -> Result>, +>; + /// We require the context to have a fixed memory address for its lifetime since /// various bits of the VM have raw pointers that point back to it. Hence we /// wrap the actual context in a box. @@ -21,14 +25,7 @@ pub(crate) struct StoreInner { #[derivative(Debug = "ignore")] pub(crate) trap_handler: Option>>, #[derivative(Debug = "ignore")] - pub(crate) on_called: Option< - Box< - dyn FnOnce( - StoreMut<'_>, - ) - -> Result>, - >, - >, + pub(crate) on_called: Option, } impl StoreInner { diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index 36ee1299c81..bd0a3442e26 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -110,7 +110,7 @@ pub fn sbrk(mut ctx: FunctionEnvMut, increment: i32) -> i32 { increment, total_memory ); - drop(dynamictop_ptr); + let _ = dynamictop_ptr; if increment > 0 && new_dynamic_top < old_dynamic_top || new_dynamic_top < 0 { abort_on_cannot_grow_memory_old(ctx); diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 8179bd10fad..ebf755d920a 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -5548,7 +5548,7 @@ pub unsafe fn sock_send_file( // Write it down to the socket let bytes_written = wasi_try_ok!(__sock_actor_mut(&ctx, sock, Rights::SOCK_SEND, |socket| { - let buf = (&buf[..]).to_vec(); + let buf = (buf[..]).to_vec(); socket.send_bytes::(Bytes::from(buf)) })); total_written += bytes_written as u64; From 0719b57a23a02930fa9676a9d2168037dc4df525 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 21:04:28 +0100 Subject: [PATCH 10/56] Fix From<> implementation Invalid method name... --- lib/api/src/js/externals/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 7e480fd98f4..23866afedca 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -62,7 +62,7 @@ pub struct Function { } impl From> for Function { - fn into(handle: StoreHandle) -> Self { + fn from(handle: StoreHandle) -> Self { Self { handle } } } From 77f11d2bbd2b4c9d720d9967c97063a740a954f8 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 21:04:54 +0100 Subject: [PATCH 11/56] chore: Remove unused function --- lib/api/src/js/trap.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 5ba0dd5bf6f..91388ffdf3a 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -257,30 +257,6 @@ impl std::error::Error for RuntimeError { } } -pub fn generic_of_jsval>( - js: JsValue, - classname: &str, -) -> Result { - use js_sys::{Object, Reflect}; - let ctor_name = Object::get_prototype_of(&js).constructor().name(); - if ctor_name == classname { - #[allow(unused_unsafe)] - let ptr = unsafe { Reflect::get(&js, &JsValue::from_str("ptr"))? }; - match ptr.as_f64() { - Some(ptr_f64) => { - let foo = unsafe { T::from_abi(ptr_f64 as u32) }; - Ok(foo) - } - None => { - // We simply relay the js value - Err(js) - } - } - } else { - Err(js) - } -} - impl From for RuntimeError { fn from(original: JsValue) -> Self { // We try to downcast the error and see if it's From 283e53f5b9b095168b85565e29f51c4d37720515 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 15 Dec 2022 23:15:09 +0100 Subject: [PATCH 12/56] Fix musl build Musl libc does not have a libc::copy_file_range. Since apple does not have it either, we factor out the implementation into a separate function. This also fixes up the copy code so it actually works correctly... --- lib/vm/src/mmap.rs | 96 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 22 deletions(-) diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index f5440cba851..4d367b7ac8e 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -7,6 +7,8 @@ use more_asserts::assert_le; use more_asserts::assert_lt; use std::io; +use std::io::Read; +use std::io::Write; use std::ptr; use std::slice; #[cfg(feature = "tracing")] @@ -355,28 +357,19 @@ impl Mmap { // The shallow copy failed so we have to do it the hard way let mut off_in: libc::off_t = 0; let mut off_out: libc::off_t = 0; - #[cfg(not(target_vendor = "apple"))] - let ret = libc::copy_file_range(self.fd.0, &mut off_in, fd.0, &mut off_out, len, 0); - - // FIXME: implement better copy on Apple targets. - // Mac libc does not have copy_file_range. - // It does have fcopyfile, which we should probably use. - // This is just a quick fix to get it working. - #[cfg(target_vendor = "apple")] - let ret = { - use std::{io::Seek, os::unix::io::FromRawFd}; - - let mut f1 = std::fs::File::from_raw_fd(self.fd.0); - let pos = f1.stream_position().map_err(|err| err.to_string())?; - - let mut f2 = std::fs::File::from_raw_fd(fd.0); - std::io::copy(&mut f1, &mut f2).map_err(|err| err.to_string())?; - f2.seek(std::io::SeekFrom::Start(0)) - .map_err(|err| err.to_string())?; - f1.seek(std::io::SeekFrom::Start(pos)) - .map_err(|err| err.to_string())?; - 0 - }; + + cfg_if::cfg_if! { + if #[cfg(not(any(target_env = "musl", target_vendor = "apple")))] + { + let ret = libc::copy_file_range(self.fd.0, &mut off_in, fd.0, &mut off_out, len, 0); + } else { + // TODO: don't use as casts... + let ret = match copy_file_range_impl(self.fd.0, off_in as u64, fd.0, off_out as u64, len) { + Ok(_) => 0, + Err(_err) => -1, + }; + } + } if ret < 0 { return Err(format!( @@ -443,6 +436,65 @@ impl Mmap { } } +/// Rust implementation of libc::copy_file_range. +/// +/// Needed because that function is not available on all platforms. +// TODO: better implementation! (this is very quick, low effort) +// TODO: this function needs tests! +#[allow(dead_code)] +fn copy_file_range_impl( + source_fd: i32, + source_offset: u64, + out_fd: i32, + out_offset: u64, + len: usize, +) -> Result<(), std::io::Error> { + use std::{ + io::{Seek, SeekFrom}, + os::unix::io::FromRawFd, + }; + + let mut f1 = unsafe { std::fs::File::from_raw_fd(source_fd) }; + let f1_original_pos = f1.stream_position()?; + + // TODO: don't cast with as + f1.seek(SeekFrom::Start(source_offset))?; + + let mut f2 = unsafe { std::fs::File::from_raw_fd(out_fd) }; + let f2_original_pos = f2.stream_position()?; + f2.seek(SeekFrom::Start(out_offset))?; + + let mut reader = std::io::BufReader::new(f1); + let mut writer = std::io::BufWriter::new(f2); + + let mut buffer = vec![0u8; 4096]; + + let mut offset = 0; + let end = len.saturating_sub(buffer.len()); + while offset < end { + let read = reader.read(&mut buffer)?; + writer.write_all(&buffer)?; + offset += read; + } + // Need to read the last chunk. + let remaining = len - offset; + if remaining > 0 { + reader.read_exact(&mut buffer[0..remaining])?; + writer.write_all(&buffer[0..remaining])?; + } + + writer.flush()?; + + // Restore files to original position. + let mut f1 = reader.into_inner(); + f1.seek(SeekFrom::Start(f1_original_pos))?; + + let mut f2 = writer.into_inner()?; + f2.seek(SeekFrom::Start(f2_original_pos))?; + + Ok(()) +} + impl Drop for Mmap { #[cfg(not(target_os = "windows"))] fn drop(&mut self) { From af313838b1970fc4cca01228ec670f1ad1255ece Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 00:14:35 +0100 Subject: [PATCH 13/56] Use byte slice for constructing JS modules Re-implement the change from IntoBytes to plain &[u8]. This was probably messed up during a rebase. --- lib/api/src/js/module.rs | 21 +++++++-------------- lib/api/src/js/trap.rs | 1 - 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 6d003cabaa4..b02d2bcbfd0 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -134,9 +134,8 @@ impl Module { /// # } /// ``` #[allow(unreachable_code)] - pub fn new(_store: &impl AsStoreRef, bytes: impl IntoBytes) -> Result { - #[allow(unused_mut)] - let mut bytes = bytes.into_bytes(); + pub fn new(_store: &impl AsStoreRef, bytes: impl AsRef<[u8]>) -> Result { + let bytes = bytes.as_ref(); #[cfg(feature = "wat")] if bytes.starts_with(b"\0asm") == false { let parsed_bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { @@ -145,7 +144,7 @@ impl Module { e ))) })?; - bytes = Bytes::from(parsed_bytes.to_vec()); + return Self::from_binary(_store, parsed_bytes.as_ref()); } Self::from_binary(_store, bytes) } @@ -163,12 +162,7 @@ impl Module { /// Opposed to [`Module::new`], this function is not compatible with /// the WebAssembly text format (if the "wat" feature is enabled for /// this crate). - pub fn from_binary( - _store: &impl AsStoreRef, - binary: impl IntoBytes, - ) -> Result { - let binary = binary.into_bytes(); - // + pub fn from_binary(_store: &impl AsStoreRef, binary: &[u8]) -> Result { // Self::validate(store, binary)?; unsafe { Self::from_binary_unchecked(_store, binary) } } @@ -181,17 +175,16 @@ impl Module { /// We maintain the `unsafe` to preserve the same API as Wasmer pub unsafe fn from_binary_unchecked( store: &impl AsStoreRef, - binary: impl IntoBytes, + binary: &[u8], ) -> Result { - let binary = binary.into_bytes(); - let js_bytes = Uint8Array::view(&binary[..]); + let js_bytes = Uint8Array::view(binary); let module = WebAssembly::Module::new(&js_bytes.into()).unwrap(); Self::from_js_module( store, module, #[cfg(feature = "js-serializable-module")] - Bytes::from(binary), + binary, ) } diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 91388ffdf3a..f38aa13e7be 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -2,7 +2,6 @@ use std::error::Error; use std::fmt; use std::sync::Arc; -use wasm_bindgen::convert::FromWasmAbi; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; use wasm_bindgen_downcast::DowncastJS; From d66fedf87fc54980899102441b869390f1b3b3fd Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 00:30:42 +0100 Subject: [PATCH 14/56] Replace complicated type with type alias The same as in sys/store.rs --- lib/api/src/js/store.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index 36030941fcb..9f2bbada841 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -1,19 +1,20 @@ use std::fmt; use wasmer_types::OnCalledAction; +/// Call handler for a store. +// TODO: better documentation! +// TODO: this type is duplicated in sys/store.rs. +// Maybe want to move it to wasmer_types... +pub type OnCalledHandler = Box< + dyn FnOnce(StoreMut<'_>) -> Result>, +>; + /// We require the context to have a fixed memory address for its lifetime since /// various bits of the VM have raw pointers that point back to it. Hence we /// wrap the actual context in a box. pub(crate) struct StoreInner { pub(crate) objects: StoreObjects, - pub(crate) on_called: Option< - Box< - dyn FnOnce( - StoreMut, - ) - -> Result>, - >, - >, + pub(crate) on_called: Option, } /// The store represents all global state that can be manipulated by From 117ef3c6ee757252273a50b8feaa84958222dc48 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 00:31:08 +0100 Subject: [PATCH 15/56] Document a type + add TODO --- lib/api/src/sys/store.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index 8032e13da2e..d69e6861cff 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -8,6 +8,8 @@ use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn}; use wasmer_vm::StoreObjects; +/// Call handler for a store. +// TODO: better documentation! pub type OnCalledHandler = Box< dyn FnOnce(StoreMut<'_>) -> Result>, >; From 7ac71bc28ac6a7b4ee9dbda217e0c46ab59748de Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 00:33:24 +0100 Subject: [PATCH 16/56] Remove redundant commented out function Leftover... --- lib/api/src/js/module.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index b02d2bcbfd0..83424f6569f 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -638,16 +638,6 @@ impl Module { !val.is_undefined() && !val.is_null() } - // /// Get the custom sections of the module given a `name`. - // /// - // /// # Important - // /// - // /// Following the WebAssembly spec, one name can have multiple - // /// custom sections. That's why an iterator (rather than one element) - // /// is returned. - // pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { - // unimplemented!(); - // } /// Get the custom sections of the module given a `name`. /// /// # Important From 7884891ec13b6e8bfcde96e5a1f431b35f64923b Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 01:02:41 +0100 Subject: [PATCH 17/56] Feature gate copy_file_range_impl on unix Only available there... --- lib/vm/src/mmap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 4d367b7ac8e..3778114707f 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -441,6 +441,7 @@ impl Mmap { /// Needed because that function is not available on all platforms. // TODO: better implementation! (this is very quick, low effort) // TODO: this function needs tests! +#[cfg(target_family = "unix")] #[allow(dead_code)] fn copy_file_range_impl( source_fd: i32, From 01477d4541fac48d905203d0687d4042332d9a63 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 11:44:21 +0100 Subject: [PATCH 18/56] tests: Disable pointless test-js-core The core (no-std) feature is currently broken and doesn't compile. This test just worked by accident by actually enabling std. No point in running this test until the feature is fixed. --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0123ebf7c97..a1df014d535 100644 --- a/Makefile +++ b/Makefile @@ -491,8 +491,10 @@ test-packages: test-js: test-js-api test-js-wasi -test-js-core: - cd lib/api && wasm-pack test --node -- --no-default-features --features js,core,wasm-types-polyfill,wat +# TODO: disabled because the no-std / core feature doesn't actually work at the moment. +# See https://github.com/wasmerio/wasmer/issues/3429 +# test-js-core: +# cd lib/api && wasm-pack test --node -- --no-default-features --features js,core,wasm-types-polyfill,wat test-js-api: cd lib/api && wasm-pack test --node -- --no-default-features --features js-default,wat From a298f5884c18325708e8d6bbb89247051069533b Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Fri, 16 Dec 2022 12:14:40 +0100 Subject: [PATCH 19/56] ci: Disable make test-js-core test The core (no-std) feature is currently broken and doesn't compile. No point in running this test until the feature is fixed. --- .github/workflows/test-js.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test-js.yaml b/.github/workflows/test-js.yaml index 75ba39ca114..c721b49cb5e 100644 --- a/.github/workflows/test-js.yaml +++ b/.github/workflows/test-js.yaml @@ -46,6 +46,3 @@ jobs: - name: Compile Wasmer to WebAssembly and test with a JavaScript host run: make test-js - - - name: Compile Wasmer to WebAssembly and test with a JavaScript host (no-std) - run: make test-js-core \ No newline at end of file From 7940dd71155668bbb969d9c528ec03c00ebc3e5b Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 19 Dec 2022 12:21:29 +0100 Subject: [PATCH 20/56] Re-implement copy_file_range function + add test --- lib/vm/src/mmap.rs | 192 ++++++++++++++++++++++++++------------------- 1 file changed, 110 insertions(+), 82 deletions(-) diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 3778114707f..c8a23b1909a 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -319,6 +319,8 @@ impl Mmap { #[cfg(not(target_os = "windows"))] pub fn fork(&mut self, hint_used: Option) -> Result { // Empty memory is an edge case + + use std::os::unix::prelude::FromRawFd; if self.len == 0 { return Ok(Self::new()); } @@ -355,28 +357,11 @@ impl Mmap { }; // The shallow copy failed so we have to do it the hard way - let mut off_in: libc::off_t = 0; - let mut off_out: libc::off_t = 0; - - cfg_if::cfg_if! { - if #[cfg(not(any(target_env = "musl", target_vendor = "apple")))] - { - let ret = libc::copy_file_range(self.fd.0, &mut off_in, fd.0, &mut off_out, len, 0); - } else { - // TODO: don't use as casts... - let ret = match copy_file_range_impl(self.fd.0, off_in as u64, fd.0, off_out as u64, len) { - Ok(_) => 0, - Err(_err) => -1, - }; - } - } - - if ret < 0 { - return Err(format!( - "failed to copy temporary file data - {}", - io::Error::last_os_error() - )); - } + + let mut source = std::fs::File::from_raw_fd(self.fd.0); + let mut out = std::fs::File::from_raw_fd(fd.0); + copy_file_range(&mut source, 0, &mut out, 0, len) + .map_err(|err| format!("Could not copy memory: {err}"))?; #[cfg(feature = "tracing")] trace!("memory copy finished (size={})", len); @@ -436,66 +421,6 @@ impl Mmap { } } -/// Rust implementation of libc::copy_file_range. -/// -/// Needed because that function is not available on all platforms. -// TODO: better implementation! (this is very quick, low effort) -// TODO: this function needs tests! -#[cfg(target_family = "unix")] -#[allow(dead_code)] -fn copy_file_range_impl( - source_fd: i32, - source_offset: u64, - out_fd: i32, - out_offset: u64, - len: usize, -) -> Result<(), std::io::Error> { - use std::{ - io::{Seek, SeekFrom}, - os::unix::io::FromRawFd, - }; - - let mut f1 = unsafe { std::fs::File::from_raw_fd(source_fd) }; - let f1_original_pos = f1.stream_position()?; - - // TODO: don't cast with as - f1.seek(SeekFrom::Start(source_offset))?; - - let mut f2 = unsafe { std::fs::File::from_raw_fd(out_fd) }; - let f2_original_pos = f2.stream_position()?; - f2.seek(SeekFrom::Start(out_offset))?; - - let mut reader = std::io::BufReader::new(f1); - let mut writer = std::io::BufWriter::new(f2); - - let mut buffer = vec![0u8; 4096]; - - let mut offset = 0; - let end = len.saturating_sub(buffer.len()); - while offset < end { - let read = reader.read(&mut buffer)?; - writer.write_all(&buffer)?; - offset += read; - } - // Need to read the last chunk. - let remaining = len - offset; - if remaining > 0 { - reader.read_exact(&mut buffer[0..remaining])?; - writer.write_all(&buffer[0..remaining])?; - } - - writer.flush()?; - - // Restore files to original position. - let mut f1 = reader.into_inner(); - f1.seek(SeekFrom::Start(f1_original_pos))?; - - let mut f2 = writer.into_inner()?; - f2.seek(SeekFrom::Start(f2_original_pos))?; - - Ok(()) -} - impl Drop for Mmap { #[cfg(not(target_os = "windows"))] fn drop(&mut self) { @@ -522,6 +447,54 @@ fn _assert() { _assert_send_sync::(); } +/// Copy a range of a file to another file. +// We could also use libc::copy_file_range on some systems, but it's +// hard to do this because it is not available on many libc implementations. +// (not on Mac OS, musl, ...) +#[cfg(target_family = "unix")] +fn copy_file_range( + source: &mut std::fs::File, + source_offset: u64, + out: &mut std::fs::File, + out_offset: u64, + len: usize, +) -> Result<(), std::io::Error> { + use std::io::{Seek, SeekFrom}; + + let source_original_pos = source.stream_position()?; + source.seek(SeekFrom::Start(source_offset))?; + + // TODO: don't cast with as + + let out_original_pos = out.stream_position()?; + out.seek(SeekFrom::Start(out_offset))?; + + // TODO: don't do this horrible "triple buffering" below". + // let mut reader = std::io::BufReader::new(source); + + // TODO: larger buffer? + let mut buffer = vec![0u8; 4096]; + + let mut to_read = len; + while to_read > 0 { + let chunk_size = std::cmp::min(to_read, buffer.len()); + let read = source.read(&mut buffer[0..chunk_size])?; + out.write_all(&buffer[0..read])?; + to_read -= read; + } + + // Need to read the last chunk. + out.flush()?; + + // Restore files to original position. + source.seek(SeekFrom::Start(source_original_pos))?; + out.flush()?; + out.sync_data()?; + out.seek(SeekFrom::Start(out_original_pos))?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -533,4 +506,59 @@ mod tests { assert_eq!(round_up_to_page_size(4096, 4096), 4096); assert_eq!(round_up_to_page_size(4097, 4096), 8192); } + + #[cfg(not(target = "wasm32-unknown-unknown"))] + #[test] + fn test_copy_file_range() -> Result<(), std::io::Error> { + // I know tempfile:: exists, but this doesn't bring in an extra + // dependency. + + use std::{fs::OpenOptions, io::Seek}; + + let dir = std::env::temp_dir().join("wasmer/copy_file_range"); + if dir.is_dir() { + std::fs::remove_dir_all(&dir).unwrap() + } + std::fs::create_dir_all(&dir).unwrap(); + + let pa = dir.join("a"); + let pb = dir.join("b"); + + let data: Vec = (0..100).collect(); + let mut a = OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(&pa) + .unwrap(); + a.write_all(&data).unwrap(); + + let datb: Vec = (100..200).collect(); + let mut b = OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(&pb) + .unwrap(); + b.write_all(&datb).unwrap(); + + a.seek(io::SeekFrom::Start(30)).unwrap(); + b.seek(io::SeekFrom::Start(99)).unwrap(); + copy_file_range(&mut a, 10, &mut b, 40, 15).unwrap(); + + assert_eq!(a.stream_position().unwrap(), 30); + assert_eq!(b.stream_position().unwrap(), 99); + + b.seek(io::SeekFrom::Start(0)).unwrap(); + let mut out = Vec::new(); + let len = b.read_to_end(&mut out).unwrap(); + assert_eq!(len, 100); + assert_eq!(out[0..40], datb[0..40]); + assert_eq!(out[40..55], data[10..25]); + assert_eq!(out[55..100], datb[55..100]); + + // TODO: needs more variant tests, but this is enough for now. + + Ok(()) + } } From 11774ee5b42243f59571f5b542eee2975c2a5b25 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 19 Dec 2022 15:32:54 +0100 Subject: [PATCH 21/56] Gate copy_file_range test on unix envs --- lib/vm/src/mmap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index c8a23b1909a..ebd844c0dde 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -507,7 +507,7 @@ mod tests { assert_eq!(round_up_to_page_size(4097, 4096), 8192); } - #[cfg(not(target = "wasm32-unknown-unknown"))] + #[cfg(target_family = "unix")] #[test] fn test_copy_file_range() -> Result<(), std::io::Error> { // I know tempfile:: exists, but this doesn't bring in an extra From 9c12b87eb03580a1f5bd89fc5ab1dbb72518ca17 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 19 Dec 2022 18:04:00 +0100 Subject: [PATCH 22/56] Rename fork() to duplicate() Renames all the memory fork() functions to duplicate(), since this is what they actually do. They enable forking, but that is a seprate functionality. --- lib/api/src/js/export.rs | 2 +- lib/api/src/js/externals/memory.rs | 4 ++-- lib/api/src/sys/tunables.rs | 4 +++- lib/vm/src/memory.rs | 30 +++++++++++++++--------------- lib/vm/src/mmap.rs | 4 ++-- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/lib/api/src/js/export.rs b/lib/api/src/js/export.rs index 1cf18286a83..6a40c86c810 100644 --- a/lib/api/src/js/export.rs +++ b/lib/api/src/js/export.rs @@ -54,7 +54,7 @@ impl VMMemory { } /// Copies this memory to a new memory - pub fn fork(&self) -> Result { + pub fn duplicate(&self) -> Result { let new_memory = crate::Memory::new_internal(self.ty.clone())?; #[cfg(feature = "tracing")] diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 5678e5c3e7b..969fb9588bd 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -265,9 +265,9 @@ impl Memory { } /// Copies this memory to a new memory - pub fn fork(&mut self, store: &impl AsStoreRef) -> Result { + pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result { let mem = self.handle.get(store.as_store_ref().objects()); - mem.fork() + mem.duplicate() } } diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index e8866abe727..aa69f29030e 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -117,10 +117,12 @@ mod tests { .unwrap() } } + fn try_clone(&self) -> Option> { None } - fn fork(&mut self) -> Result, MemoryError> { + + fn duplicate(&mut self) -> Result, MemoryError> { let mem = self.mem.clone(); Ok(Box::new(Self { memory_definition: Some(UnsafeCell::new(VMMemoryDefinition { diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 1ca89c00135..d3d288a0c04 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -119,11 +119,11 @@ impl WasmMmap { /// Copies the memory /// (in this case it performs a copy-on-write to save memory) - pub fn fork(&mut self) -> Result { + pub fn duplicate(&mut self) -> Result { let mem_length = self.size.bytes().0; let mut alloc = self .alloc - .fork(Some(mem_length)) + .duplicate(Some(mem_length)) .map_err(MemoryError::Generic)?; let base_ptr = alloc.as_mut_ptr(); Ok(Self { @@ -289,9 +289,9 @@ impl VMOwnedMemory { } /// Copies this memory to a new memory - pub fn fork(&mut self) -> Result { + pub fn duplicate(&mut self) -> Result { Ok(Self { - mmap: self.mmap.fork()?, + mmap: self.mmap.duplicate()?, config: self.config.clone(), }) } @@ -333,8 +333,8 @@ impl LinearMemory for VMOwnedMemory { } /// Copies this memory to a new memory - fn fork(&mut self) -> Result, MemoryError> { - let forked = Self::fork(self)?; + fn duplicate(&mut self) -> Result, MemoryError> { + let forked = Self::duplicate(self)?; Ok(Box::new(forked)) } } @@ -376,10 +376,10 @@ impl VMSharedMemory { } /// Copies this memory to a new memory - pub fn fork(&mut self) -> Result { + pub fn duplicate(&mut self) -> Result { let mut guard = self.mmap.write().unwrap(); Ok(Self { - mmap: Arc::new(RwLock::new(guard.fork()?)), + mmap: Arc::new(RwLock::new(guard.duplicate()?)), config: self.config.clone(), }) } @@ -427,8 +427,8 @@ impl LinearMemory for VMSharedMemory { } /// Copies this memory to a new memory - fn fork(&mut self) -> Result, MemoryError> { - let forked = Self::fork(self)?; + fn duplicate(&mut self) -> Result, MemoryError> { + let forked = Self::duplicate(self)?; Ok(Box::new(forked)) } } @@ -495,8 +495,8 @@ impl LinearMemory for VMMemory { } /// Copies this memory to a new memory - fn fork(&mut self) -> Result, MemoryError> { - self.0.fork() + fn duplicate(&mut self) -> Result, MemoryError> { + self.0.duplicate() } } @@ -558,8 +558,8 @@ impl VMMemory { } /// Copies this memory to a new memory - pub fn fork(&mut self) -> Result, MemoryError> { - LinearMemory::fork(self) + pub fn duplicate(&mut self) -> Result, MemoryError> { + LinearMemory::duplicate(self) } } @@ -615,5 +615,5 @@ where } /// Copies this memory to a new memory - fn fork(&mut self) -> Result, MemoryError>; + fn duplicate(&mut self) -> Result, MemoryError>; } diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index ebd844c0dde..3c0c5763bfb 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -317,7 +317,7 @@ impl Mmap { /// Copies the memory to a new swap file (using copy-on-write if available) #[cfg(not(target_os = "windows"))] - pub fn fork(&mut self, hint_used: Option) -> Result { + pub fn duplicate(&mut self, hint_used: Option) -> Result { // Empty memory is an edge case use std::os::unix::prelude::FromRawFd; @@ -395,7 +395,7 @@ impl Mmap { /// Copies the memory to a new swap file (using copy-on-write if available) #[cfg(target_os = "windows")] - pub fn fork(&mut self, hint_used: Option) -> Result { + pub fn duplicate(&mut self, hint_used: Option) -> Result { // Create a new memory which we will copy to let new_mmap = Self::with_at_least(self.len)?; From da8e4a1a4570b89b0487e4f955f79855b411adad Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 19 Dec 2022 19:04:49 +0100 Subject: [PATCH 23/56] chore: Remove redunant code change Doesn't actually do anything. --- lib/emscripten/src/memory.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index bd0a3442e26..2c905e30c88 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -110,8 +110,6 @@ pub fn sbrk(mut ctx: FunctionEnvMut, increment: i32) -> i32 { increment, total_memory ); - let _ = dynamictop_ptr; - if increment > 0 && new_dynamic_top < old_dynamic_top || new_dynamic_top < 0 { abort_on_cannot_grow_memory_old(ctx); return -1; From bc78b91fef7aadc6f7a287ca867979d973289bc8 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 19 Dec 2022 19:13:04 +0100 Subject: [PATCH 24/56] Revert toolchain change from 1.63 to 1.64 No idea why this was done, but it doesn't seem neccessary. --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 8725364a8ec..58e4eb6b299 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.64 +1.63 From b8a276a0aeb89c7ba1e633fae6cee03ebe2ef2c8 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 20 Dec 2022 13:34:38 +0100 Subject: [PATCH 25/56] Bumped rust toolchain to 1.64 for all CI steps --- .github/workflows/benchmark.yaml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/cloudcompiler.yaml | 2 +- .github/workflows/documentation.yaml | 2 +- .github/workflows/test-js.yaml | 2 +- .github/workflows/test-sys.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index 24ca8005219..c303bddb705 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -24,7 +24,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Configure cargo data directory # After this point, all cargo registry and crate data is stored in # $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be8ae0880ca..50e63bf087e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,7 +100,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.target }} - uses: Swatinem/rust-cache@v1 if: matrix.use_sccache != true diff --git a/.github/workflows/cloudcompiler.yaml b/.github/workflows/cloudcompiler.yaml index 4bd3f1f5c43..6a050d7fad2 100644 --- a/.github/workflows/cloudcompiler.yaml +++ b/.github/workflows/cloudcompiler.yaml @@ -21,7 +21,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.target }} - name: Install wasm32-wasi target shell: bash diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index ae018dd312d..c2827118ae0 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -16,7 +16,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Install LLVM shell: bash run: | diff --git a/.github/workflows/test-js.yaml b/.github/workflows/test-js.yaml index c721b49cb5e..d2ec7111099 100644 --- a/.github/workflows/test-js.yaml +++ b/.github/workflows/test-js.yaml @@ -33,7 +33,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Install NodeJS uses: actions/setup-node@v2 diff --git a/.github/workflows/test-sys.yaml b/.github/workflows/test-sys.yaml index f440ede6fda..0e067958c06 100644 --- a/.github/workflows/test-sys.yaml +++ b/.github/workflows/test-sys.yaml @@ -113,7 +113,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.target }} - uses: Swatinem/rust-cache@v1 if: matrix.use_sccache != true From bc140d402d7871b5e0cd9f943a35b440cb4b42fd Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 20 Dec 2022 13:47:50 +0100 Subject: [PATCH 26/56] Bumped rust-toolchain too --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 58e4eb6b299..8725364a8ec 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.63 +1.64 From 6149733f56f59e142cfa70b5bac33eaef34e3651 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 21 Dec 2022 10:55:37 +0100 Subject: [PATCH 27/56] Added iter_globals and set_global to StoreObjects --- lib/vm/src/store.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 3eeecfb6df8..62e281b761d 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -1,3 +1,4 @@ +use core::slice::Iter; use std::{ cell::UnsafeCell, fmt, @@ -9,9 +10,9 @@ use std::{ use wasmer_types::StoreSnapshot; -use crate::VMExternObj; - -use crate::{InstanceHandle, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable}; +use crate::{ + InstanceHandle, VMExternObj, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable, +}; /// Unique ID to identify a context. /// @@ -104,6 +105,19 @@ impl StoreObjects { } } + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> Iter { + self.globals.iter() + } + + /// Set a global, at index idx. Will panic if idx is out of range + pub fn set_global(&self, idx: usize, val: u128) { + assert!(idx >= self.globals.len()); + unsafe { + self.globals[idx].vmglobal().as_mut().val.u128 = val; + } + } + /// Serializes the mutable things into a snapshot pub fn save_snapshot(&self) -> StoreSnapshot { let mut ret = StoreSnapshot::default(); From 6eba96059326bac9d42cf66e4c7a9ec095b90757 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 21 Dec 2022 12:25:27 +0100 Subject: [PATCH 28/56] Rename to and fixed assert --- lib/vm/src/store.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 62e281b761d..8394a36a7a9 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -111,8 +111,10 @@ impl StoreObjects { } /// Set a global, at index idx. Will panic if idx is out of range - pub fn set_global(&self, idx: usize, val: u128) { - assert!(idx >= self.globals.len()); + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, val: u128) { + assert!(idx < self.globals.len()); unsafe { self.globals[idx].vmglobal().as_mut().val.u128 = val; } From de443dcf15ca5bb122ca6d869542f5775788f7d1 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 22 Dec 2022 11:23:46 +0100 Subject: [PATCH 29/56] Remove StoreSnapshot functionality The StoreSnapshot isn't necessary in the public API. Thanks to the new global accessors on StoreObjects, it is possible to implement this externally instead. --- lib/api/src/js/export.rs | 26 +-------------- lib/api/src/js/mod.rs | 4 +-- lib/api/src/js/store.rs | 31 ----------------- lib/api/src/sys/mod.rs | 2 +- lib/api/src/sys/store.rs | 29 +--------------- lib/types/src/lib.rs | 3 -- lib/types/src/store.rs | 72 ---------------------------------------- lib/vm/src/global.rs | 23 +------------ lib/vm/src/store.rs | 18 ---------- 9 files changed, 6 insertions(+), 202 deletions(-) delete mode 100644 lib/types/src/store.rs diff --git a/lib/api/src/js/export.rs b/lib/api/src/js/export.rs index 6a40c86c810..180f1d76213 100644 --- a/lib/api/src/js/export.rs +++ b/lib/api/src/js/export.rs @@ -10,8 +10,7 @@ use std::fmt; use tracing::trace; use wasm_bindgen::{JsCast, JsValue}; use wasmer_types::{ - ExternType, FunctionType, GlobalType, MemoryError, MemoryType, Pages, StoreSnapshot, TableType, - WASM_PAGE_SIZE, + ExternType, FunctionType, GlobalType, MemoryError, MemoryType, Pages, TableType, WASM_PAGE_SIZE, }; /// Represents linear memory that is managed by the javascript runtime @@ -110,29 +109,6 @@ impl VMGlobal { pub(crate) fn new(global: Global, ty: GlobalType) -> Self { Self { global, ty } } - - /// Saves the global value into the snapshot - pub fn save_snapshot(&self, index: usize, snapshot: &mut StoreSnapshot) { - if let Some(val) = self.global.as_f64() { - let entry = snapshot.globals.entry(index as u32).or_default(); - *entry = val as u128; - } - } - - /// Restores the global value from the snapshot - pub fn restore_snapshot(&mut self, index: usize, snapshot: &StoreSnapshot) { - let index = index as u32; - if let Some(entry) = snapshot.globals.get(&index) { - if let Some(existing) = self.global.as_f64() { - let existing = existing as u128; - if existing == *entry { - return; - } - } - let value = JsValue::from_f64(*entry as _); - self.global.set_value(&value); - } - } } unsafe impl Send for VMGlobal {} diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 1404b6fff10..0fb80d6dd88 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -78,8 +78,8 @@ pub mod vm { pub use wasmer_types::is_wasm; pub use wasmer_types::{ - Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, StoreSnapshot, - ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, + Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, ValueType, + WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; #[cfg(feature = "wat")] diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index 9f2bbada841..fb0ab551543 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -118,11 +118,6 @@ impl<'a> StoreRef<'a> { pub fn same(a: &Self, b: &Self) -> bool { a.inner.objects.id() == b.inner.objects.id() } - - /// Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> wasmer_types::StoreSnapshot { - self.inner.objects.save_snapshot() - } } /// A temporary handle to a [`Context`]. @@ -138,16 +133,6 @@ impl<'a> StoreMut<'a> { a.inner.objects.id() == b.inner.objects.id() } - /// Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> wasmer_types::StoreSnapshot { - self.inner.objects.save_snapshot() - } - - /// Restores a snapshot back into the store - pub fn restore_snapshot(&mut self, snapshot: &wasmer_types::StoreSnapshot) { - self.inner.objects.restore_snapshot(snapshot); - } - pub(crate) fn as_raw(&self) -> *mut StoreInner { self.inner as *const StoreInner as *mut StoreInner } @@ -328,22 +313,6 @@ mod objects { (&mut high[0], &mut low[a.index()]) } } - - /// Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> wasmer_types::StoreSnapshot { - let mut ret = wasmer_types::StoreSnapshot::default(); - for (index, global) in self.globals.iter().enumerate() { - global.save_snapshot(index, &mut ret); - } - ret - } - - /// Serializes the mutable things into a snapshot - pub fn restore_snapshot(&mut self, snapshot: &wasmer_types::StoreSnapshot) { - for (index, global) in self.globals.iter_mut().enumerate() { - global.restore_snapshot(index, snapshot); - } - } } /// Handle to an object managed by a context. diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index f81f0be4422..f729812f749 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -42,7 +42,7 @@ pub use wasmer_derive::ValueType; pub use wasmer_types::is_wasm; pub use wasmer_types::{ CpuFeature, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, - Mutability, OnCalledAction, StoreSnapshot, TableType, Target, Type, + Mutability, OnCalledAction, TableType, Target, Type, }; pub use wasmer_types::{ diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index d69e6861cff..b1406bb88d4 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -3,7 +3,7 @@ use derivative::Derivative; use std::fmt; #[cfg(feature = "compiler")] use wasmer_compiler::{AsEngineRef, Engine, EngineBuilder, EngineRef, Tunables}; -use wasmer_types::{OnCalledAction, StoreSnapshot}; +use wasmer_types::OnCalledAction; use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn}; use wasmer_vm::StoreObjects; @@ -30,18 +30,6 @@ pub(crate) struct StoreInner { pub(crate) on_called: Option, } -impl StoreInner { - // Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> StoreSnapshot { - self.objects.save_snapshot() - } - - // Serializes the mutable things into a snapshot - pub fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) { - self.objects.restore_snapshot(snapshot); - } -} - /// The store represents all global state that can be manipulated by /// WebAssembly programs. It consists of the runtime representation /// of all instances of functions, tables, memories, and globals that @@ -290,11 +278,6 @@ impl<'a> StoreRef<'a> { a.inner.engine.id() == b.inner.engine.id() } - /// Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> StoreSnapshot { - self.inner.save_snapshot() - } - /// The signal handler #[inline] pub fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { @@ -331,16 +314,6 @@ impl<'a> StoreMut<'a> { a.inner.engine.id() == b.inner.engine.id() } - /// Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> StoreSnapshot { - self.inner.save_snapshot() - } - - /// Restores a snapshot back into the store - pub fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) { - self.inner.restore_snapshot(snapshot); - } - #[cfg(feature = "compiler")] pub(crate) fn tunables_and_objects_mut(&mut self) -> (&dyn Tunables, &mut StoreObjects) { (self.inner.engine.tunables(), &mut self.inner.objects) diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index da659cacccb..a9d708dedf1 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -62,7 +62,6 @@ mod libcalls; mod memory; mod module; mod serialize; -mod store; mod table; mod trapcode; mod types; @@ -129,8 +128,6 @@ pub use crate::compilation::symbols::{Symbol, SymbolRegistry}; pub use crate::compilation::trap::TrapInformation; pub use crate::compilation::unwind::CompiledFunctionUnwindInfo; -pub use crate::store::StoreSnapshot; - /// Offset in bytes from the beginning of the function. pub type CodeOffset = u32; diff --git a/lib/types/src/store.rs b/lib/types/src/store.rs deleted file mode 100644 index cd8fbc68c40..00000000000 --- a/lib/types/src/store.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::collections::HashMap; -use std::io::Read; - -/// Represents a snapshot of parts of the store that mutate -/// (such as globals and tables) -#[derive(Debug, Default)] -pub struct StoreSnapshot { - /// Global values at the time the snapshot was taken - pub globals: HashMap, -} - -impl StoreSnapshot { - /// Serializes the snapshot into a set of bytes - pub fn serialize(&self) -> Vec { - let capacity = 32usize * self.globals.len(); - let mut ret = Vec::with_capacity(capacity); - - ret.extend_from_slice(&1u32.to_le_bytes()); - ret.extend_from_slice(&(self.globals.len() as u32).to_le_bytes()); - for (index, val) in self.globals.iter() { - ret.extend_from_slice(&index.to_le_bytes()); - ret.extend_from_slice(&val.to_le_bytes()); - } - ret - } - - /// Deserializes the bytes back into a store snapshot - pub fn deserialize(data: &[u8]) -> std::io::Result { - let mut ret = Self::default(); - - // Read all the sections - let mut reader = data; - loop { - let mut ty_arr = [0u8; 4]; - if let Err(err) = reader.read_exact(&mut ty_arr) { - if err.kind() == std::io::ErrorKind::UnexpectedEof { - break; - } - return Err(err); - } - - let ty = u32::from_le_bytes(ty_arr); - match ty { - 1u32 => { - // Read all the globals - let mut len_arr = [0u8; 4]; - reader.read_exact(&mut len_arr)?; - let len = u32::from_le_bytes(len_arr) as usize; - for _ in 0..len { - // Read the key - let mut key_arr = [0u8; 4]; - reader.read_exact(&mut key_arr)?; - let key = u32::from_le_bytes(key_arr); - - // Read the value - let mut val_arr = [0u8; 16]; - reader.read_exact(&mut val_arr)?; - let val = u128::from_le_bytes(val_arr); - - // Set the value in the snapshot - ret.globals.insert(key, val); - } - } - _ => { - break; - } - } - } - - Ok(ret) - } -} diff --git a/lib/vm/src/global.rs b/lib/vm/src/global.rs index a899f7cd64c..bddb55575e1 100644 --- a/lib/vm/src/global.rs +++ b/lib/vm/src/global.rs @@ -1,7 +1,7 @@ use crate::{store::MaybeInstanceOwned, vmcontext::VMGlobalDefinition}; use derivative::Derivative; use std::{cell::UnsafeCell, ptr::NonNull}; -use wasmer_types::{GlobalType, StoreSnapshot}; +use wasmer_types::GlobalType; /// A Global instance #[derive(Derivative)] @@ -46,25 +46,4 @@ impl VMGlobal { } } } - - /// Saves the global value into the snapshot - pub fn save_snapshot(&self, index: usize, snapshot: &mut StoreSnapshot) { - let entry = snapshot.globals.entry(index as u32).or_default(); - - let val = unsafe { self.vm_global_definition.as_ptr().as_ref().val.u128 }; - *entry = val; - } - - /// Restores the global value from the snapshot - pub fn restore_snapshot(&mut self, index: usize, snapshot: &StoreSnapshot) { - let index = index as u32; - if let Some(entry) = snapshot.globals.get(&index) { - let existing = unsafe { self.vm_global_definition.as_ptr().as_ref().val.u128 }; - if existing != *entry { - unsafe { - self.vm_global_definition.as_ptr().as_mut().val.u128 = *entry; - } - } - } - } } diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 8394a36a7a9..4e0c4676525 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -8,8 +8,6 @@ use std::{ sync::atomic::{AtomicU64, Ordering}, }; -use wasmer_types::StoreSnapshot; - use crate::{ InstanceHandle, VMExternObj, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable, }; @@ -119,22 +117,6 @@ impl StoreObjects { self.globals[idx].vmglobal().as_mut().val.u128 = val; } } - - /// Serializes the mutable things into a snapshot - pub fn save_snapshot(&self) -> StoreSnapshot { - let mut ret = StoreSnapshot::default(); - for (index, global) in self.globals.iter().enumerate() { - global.save_snapshot(index, &mut ret); - } - ret - } - - /// Serializes the mutable things into a snapshot - pub fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) { - for (index, global) in self.globals.iter_mut().enumerate() { - global.restore_snapshot(index, snapshot); - } - } } /// Handle to an object managed by a context. From 9607291a3964f2402ec0ad7d8f165b56be8b9176 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 22 Dec 2022 12:41:41 +0100 Subject: [PATCH 30/56] Remove un-needed feature flags for DynamicFunction These are not present in master branch anymore, and ar enot neccessary. --- lib/api/src/sys/externals/function.rs | 31 ++++++++++----------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 3fa62b6b43b..73d9e4d9624 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -1,34 +1,31 @@ +use wasmer_types::RawValue; +use wasmer_vm::{ + on_host_stack, raise_user_trap, resume_panic, InternalStoreHandle, StoreHandle, VMContext, + VMDynamicFunctionContext, VMExtern, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionKind, + VMTrampoline, +}; + use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::FunctionType; -use crate::sys::TypedFunction; +use crate::sys::{FunctionType, RuntimeError, TypedFunction}; use crate::FunctionEnv; pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; + #[cfg(feature = "compiler")] use { crate::{ - sys::{ - store::{StoreInner, StoreMut}, - RuntimeError, - }, + sys::store::{StoreInner, StoreMut}, FunctionEnvMut, Value, }, inner::StaticFunction, std::{cell::UnsafeCell, cmp::max, ffi::c_void}, - wasmer_types::RawValue, wasmer_vm::{ - on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, MaybeInstanceOwned, - VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, - VMFunctionContext, VMTrampoline, + wasmer_call_trampoline, MaybeInstanceOwned, VMCallerCheckedAnyfunc, VMFunctionContext, }, }; -use wasmer_vm::{ - InternalStoreHandle, StoreHandle, VMExtern, VMFuncRef, VMFunction, VMFunctionKind, -}; - /// A WebAssembly `function` instance. /// /// A function instance is the runtime representation of a function. @@ -803,19 +800,16 @@ impl<'a> Exportable<'a> for Function { } /// Host state for a dynamic function. -#[cfg(feature = "compiler")] pub(crate) struct DynamicFunction { func: F, } -#[cfg(feature = "compiler")] impl DynamicFunction where F: Fn(*mut RawValue) -> Result<(), RuntimeError> + 'static, { // This function wraps our func, to make it compatible with the // reverse trampoline signature - #[cfg(feature = "compiler")] unsafe extern "C" fn func_wrapper( this: &mut VMDynamicFunctionContext, values_vec: *mut RawValue, @@ -832,12 +826,10 @@ where } } - #[cfg(feature = "compiler")] fn func_body_ptr(&self) -> *const VMFunctionBody { Self::func_wrapper as *const VMFunctionBody } - #[cfg(feature = "compiler")] fn call_trampoline_address(&self) -> VMTrampoline { Self::call_trampoline } @@ -870,7 +862,6 @@ mod inner { use crate::sys::NativeWasmTypeInto; use crate::{AsStoreMut, AsStoreRef, ExternRef, FunctionEnv, StoreMut}; - #[cfg(feature = "compiler")] use crate::Function; /// A trait to convert a Rust value to a `WasmNativeType` value, From 019ea5fee279f0d33dc03c692f359bc7a37b8487 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 22 Dec 2022 13:34:57 +0100 Subject: [PATCH 31/56] chore: Remove allow attribute Duplicated... --- lib/api/src/js/externals/memory.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 969fb9588bd..44ff19fc08a 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -93,7 +93,6 @@ impl Memory { pub(crate) fn new_internal(ty: MemoryType) -> Result { let descriptor = js_sys::Object::new(); #[allow(unused_unsafe)] - #[allow(unused_unsafe)] unsafe { js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); if let Some(max) = ty.maximum { From a499deadeb6ea6ac2223748e87d4a0387e66f260 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 22 Dec 2022 16:38:05 +0100 Subject: [PATCH 32/56] Bump webc to 4.0.0 The earlier 0.4.1 downgrade was a "hack". There alrady is a 3.x version that was used previously, but is broken. We replaced that with 0.4.1, which was the latest publish, but we can't downgrade to a previous version... So 0.4.1 was re-relased as 4.0.0. --- Cargo.lock | 36 +++++++----------------------------- lib/c-api/Cargo.toml | 2 +- lib/cli/Cargo.toml | 2 +- lib/registry/Cargo.toml | 4 ++-- lib/vfs/Cargo.toml | 2 +- lib/wasi/Cargo.toml | 2 +- 6 files changed, 13 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe14460a972..eb067b12f3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4252,7 +4252,7 @@ dependencies = [ "wasmer-types", "wasmer-vfs", "wasmer-wasi", - "webc 0.4.1", + "webc", ] [[package]] @@ -4351,7 +4351,7 @@ dependencies = [ "wasmer-wasm-interface", "wasmer-wast", "wasmparser 0.51.4", - "webc 3.0.1", + "webc", ] [[package]] @@ -4582,7 +4582,7 @@ dependencies = [ "toml", "url", "wasmer-toml", - "webc 3.0.1", + "webc", "whoami", ] @@ -4637,7 +4637,7 @@ dependencies = [ "thiserror", "tracing", "typetag", - "webc 3.0.1", + "webc", ] [[package]] @@ -4702,7 +4702,7 @@ dependencies = [ "wasmer-vnet", "wasmer-wasi-local-networking", "wasmer-wasi-types", - "webc 3.0.1", + "webc", "winapi", ] @@ -5027,31 +5027,9 @@ dependencies = [ [[package]] name = "webc" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cbb86b1e6e0b9bcd03b5e530987bb18fbef8dcac56c3f238039be085ce1b40d" -dependencies = [ - "anyhow", - "base64", - "indexmap", - "leb128", - "lexical-sort", - "memchr", - "path-clean", - "rand 0.8.5", - "serde", - "serde_cbor", - "serde_json", - "sha2", - "url", - "walkdir", -] - -[[package]] -name = "webc" -version = "3.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef87e7b955d5d1feaa8697ae129f1a9ce8859e151574ad3baceae9413b48d2f0" +checksum = "1b44d4d5ad9ecc7392210891a8a9207c04f6984a594be82075f5e7abe9271fcc" dependencies = [ "anyhow", "base64", diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index 959a5cbf256..6f5190fa317 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -32,7 +32,7 @@ wasmer-middlewares = { version = "=3.1.0", path = "../middlewares", optional = t wasmer-wasi = { version = "=3.1.0", path = "../wasi", default-features = false, features = ["host-fs", "sys"], optional = true } wasmer-types = { version = "=3.1.0", path = "../types" } wasmer-vfs = { version = "=3.1.0", path = "../vfs", optional = true, default-features = false, features = ["static-fs"] } -webc = { version = "0.4.1", optional = true } +webc = { version = "4.0.0", optional = true } enumset = "1.0.2" cfg-if = "1.0" lazy_static = "1.4" diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 0062558e3d9..dd21064c96c 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -70,7 +70,7 @@ toml = "0.5.9" url = "2.3.1" libc = { version = "^0.2", default-features = false } nuke-dir = { version = "0.1.0", optional = true } -webc = { version = "3.0.1", optional = true } +webc = { version = "4.0.0", optional = true } isatty = "0.1.9" dialoguer = "0.10.2" tldextract = "0.6.0" diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index af96584af6e..e5ffcb61dee 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -25,7 +25,7 @@ tar = "0.4.38" flate2 = "1.0.24" semver = "1.0.14" lzma-rs = "0.2.0" -webc = { version ="3.0.1", features = ["mmap"] } +webc = { version ="4.0.0", features = ["mmap"] } hex = "0.4.3" tokio = "1.21.2" tempdir = "0.3.7" @@ -36,4 +36,4 @@ filetime = "0.2.19" tldextract = "0.6.0" console = "0.15.2" indicatif = "0.17.2" -lazy_static = "1.4.0" \ No newline at end of file +lazy_static = "1.4.0" diff --git a/lib/vfs/Cargo.toml b/lib/vfs/Cargo.toml index 2a02b3a64e2..cd44f06d4f0 100644 --- a/lib/vfs/Cargo.toml +++ b/lib/vfs/Cargo.toml @@ -13,7 +13,7 @@ tracing = { version = "0.1" } typetag = { version = "0.1", optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } slab = { version = "0.4", optional = true } -webc = { version = "3.0.1", optional = true } +webc = { version = "4.0.0", optional = true } anyhow = { version = "1.0.66", optional = true } [features] diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 9d8c13bf414..b975d874c8b 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -28,7 +28,7 @@ bincode = { version = "1.3", optional = true } chrono = { version = "^0.4", default-features = false, features = [ "wasmbind", "std", "clock" ], optional = true } derivative = { version = "^2" } bytes = "1" -webc = { version = "3.0.1", optional = true, default-features = false, features = ["std", "mmap"] } +webc = { version = "4.0.0", optional = true, default-features = false, features = ["std", "mmap"] } serde_cbor = { version = "0.11.2", optional = true } anyhow = { version = "1.0.66", optional = true } wasmer-emscripten = { path = "../emscripten", version = "=3.1.0", optional = true } From af164229cc74ce83c8fc9c1c37fda6fc23a0b711 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 22 Dec 2022 18:52:30 +0100 Subject: [PATCH 33/56] Remove un-needed is_ok() methods No longer required, module sharing now works properly --- lib/api/src/js/module.rs | 9 --------- lib/api/src/sys/module.rs | 9 --------- 2 files changed, 18 deletions(-) diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 83424f6569f..081a4cfbd4a 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -629,15 +629,6 @@ impl Module { ExportsIterator::new(iter, exports.length() as usize) } - /// Returns true if the module is still ok - this will be - /// false if the module was passed between threads in a - /// way that it became undefined (JS does not share objects - /// between threads except via a post_message()) - pub fn is_ok(&self) -> bool { - let val = JsValue::from(&self.module); - !val.is_undefined() && !val.is_null() - } - /// Get the custom sections of the module given a `name`. /// /// # Important diff --git a/lib/api/src/sys/module.rs b/lib/api/src/sys/module.rs index ce81bc352e5..cbd58f47d85 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/sys/module.rs @@ -453,15 +453,6 @@ impl Module { self.module_info.exports() } - /// Returns true if the module is still ok - this will be - /// false if the module was passed between threads in a - /// way that it became undefined (JS does not share objects - /// between threads except via a post_message()) - pub fn is_ok(&self) -> bool { - // As RUST is a type safe language modules in SYS are always ok - true - } - /// Get the custom sections of the module given a `name`. /// /// # Important From 422dfc52f39537c233db0fa357a7354c699a1869 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Thu, 22 Dec 2022 19:26:36 +0100 Subject: [PATCH 34/56] Add cargo deny license exception for webc 4.0 --- deny.toml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/deny.toml b/deny.toml index 839d4e4090b..897dfca8690 100644 --- a/deny.toml +++ b/deny.toml @@ -49,7 +49,13 @@ notice = "warn" # output a note when they are encountered. ignore = [ #"RUSTSEC-0000-0000", + ] +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +git-fetch-with-cli = true # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories # will still output a note when they are encountered. @@ -105,13 +111,21 @@ confidence-threshold = 0.8 exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list - { allow = ["LicenseRef-LICENSE.txt"], name = "webc", version = "*" }, ] + + # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the # licensing information -#[[licenses.clarify]] +[[licenses.clarify]] +name = "webc" +version = "*" +expression = "BSL-1.0" +license-files = [ + { path = "LICENSE.txt", hash = 0xa2180a97 } +] + # The name of the crate the clarification applies to #name = "ring" # The optional version constraint for the crate From 4a71dbd81cf60c22c3c31939c73dd7a2911e154b Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Tue, 27 Dec 2022 13:54:07 +0100 Subject: [PATCH 35/56] Revert Mmap FD implementation Revert the FD backed mmap implementation to the previous plain memory functionality. This preserves current performance characteristics for users. Wasix will use a custom memory implementation which uses FD backing. Also adds a duplicate() method. --- lib/vm/src/mmap.rs | 326 ++++++--------------------------------------- 1 file changed, 41 insertions(+), 285 deletions(-) diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 3c0c5763bfb..78f6c1c8f91 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -7,12 +7,8 @@ use more_asserts::assert_le; use more_asserts::assert_lt; use std::io; -use std::io::Read; -use std::io::Write; use std::ptr; use std::slice; -#[cfg(feature = "tracing")] -use tracing::trace; /// Round `size` up to the nearest multiple of `page_size`. fn round_up_to_page_size(size: usize, page_size: usize) -> usize { @@ -28,35 +24,8 @@ pub struct Mmap { // `unsafe impl`. This type is sendable across threads and shareable since // the coordination all happens at the OS layer. ptr: usize, - len: usize, - // Backing file that will be closed when the memory mapping goes out of scope - fd: FdGuard, -} - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct FdGuard(pub i32); - -impl Default for FdGuard { - fn default() -> Self { - Self(-1) - } -} - -impl Clone for FdGuard { - fn clone(&self) -> Self { - unsafe { Self(libc::dup(self.0)) } - } -} - -impl Drop for FdGuard { - fn drop(&mut self) { - if self.0 >= 0 { - unsafe { - libc::close(self.0); - } - self.0 = -1; - } - } + total_size: usize, + accessible_size: usize, } impl Mmap { @@ -68,8 +37,8 @@ impl Mmap { let empty = Vec::::new(); Self { ptr: empty.as_ptr() as usize, - len: 0, - fd: FdGuard::default(), + total_size: 0, + accessible_size: 0, } } @@ -99,28 +68,6 @@ impl Mmap { return Ok(Self::new()); } - // Open a temporary file (which is used for swapping) - let fd = unsafe { - let file = libc::tmpfile(); - if file.is_null() { - return Err(format!( - "failed to create temporary file - {}", - io::Error::last_os_error() - )); - } - FdGuard(libc::fileno(file)) - }; - - // First we initialize it with zeros - unsafe { - if libc::ftruncate(fd.0, mapping_size as libc::off_t) < 0 { - return Err("could not truncate tmpfile".to_string()); - } - } - - // Compute the flags - let flags = libc::MAP_FILE | libc::MAP_SHARED; - Ok(if accessible_size == mapping_size { // Allocate a single read-write region at once. let ptr = unsafe { @@ -128,8 +75,8 @@ impl Mmap { ptr::null_mut(), mapping_size, libc::PROT_READ | libc::PROT_WRITE, - flags, - fd.0, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, 0, ) }; @@ -139,8 +86,8 @@ impl Mmap { Self { ptr: ptr as usize, - len: mapping_size, - fd, + total_size: mapping_size, + accessible_size, } } else { // Reserve the mapping size. @@ -149,8 +96,8 @@ impl Mmap { ptr::null_mut(), mapping_size, libc::PROT_NONE, - flags, - fd.0, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, 0, ) }; @@ -160,8 +107,8 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, - len: mapping_size, - fd, + total_size: mapping_size, + accessible_size, }; if accessible_size != 0 { @@ -194,7 +141,8 @@ impl Mmap { if mapping_size == 0 { return Ok(Self::new()); } - if accessible_size == mapping_size { + + Ok(if accessible_size == mapping_size { // Allocate a single read-write region at once. let ptr = unsafe { VirtualAlloc( @@ -208,11 +156,10 @@ impl Mmap { return Err(io::Error::last_os_error().to_string()); } - Ok(Self { + Self { ptr: ptr as usize, len: mapping_size, - fd: FdGuard::default(), - }) + } } else { // Reserve the mapping size. let ptr = @@ -224,7 +171,6 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, len: mapping_size, - fd: FdGuard::default(), }; if accessible_size != 0 { @@ -232,8 +178,8 @@ impl Mmap { result.make_accessible(0, accessible_size)?; } - Ok(result) - } + result + }) } /// Make the memory starting at `start` and extending for `len` bytes accessible. @@ -244,8 +190,8 @@ impl Mmap { let page_size = region::page::size(); assert_eq!(start & (page_size - 1), 0); assert_eq!(len & (page_size - 1), 0); - assert_lt!(len, self.len); - assert_lt!(start, self.len - len); + assert_lt!(len, self.total_size); + assert_lt!(start, self.total_size - len); // Commit the accessible size. let ptr = self.ptr as *const u8; @@ -287,12 +233,22 @@ impl Mmap { /// Return the allocated memory as a slice of u8. pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) } + unsafe { slice::from_raw_parts(self.ptr as *const u8, self.total_size) } + } + + /// Return the allocated memory as a slice of u8. + pub fn as_slice_accessible(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr as *const u8, self.accessible_size) } } /// Return the allocated memory as a mutable slice of u8. pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.len) } + unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.total_size) } + } + + /// Return the allocated memory as a mutable slice of u8. + pub fn as_mut_slice_accessible(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.accessible_size) } } /// Return the allocated memory as a pointer to u8. @@ -307,7 +263,7 @@ impl Mmap { /// Return the length of the allocated memory. pub fn len(&self) -> usize { - self.len + self.total_size } /// Return whether any memory has been allocated. @@ -315,117 +271,20 @@ impl Mmap { self.len() == 0 } - /// Copies the memory to a new swap file (using copy-on-write if available) - #[cfg(not(target_os = "windows"))] - pub fn duplicate(&mut self, hint_used: Option) -> Result { - // Empty memory is an edge case - - use std::os::unix::prelude::FromRawFd; - if self.len == 0 { - return Ok(Self::new()); - } - - // First we sync all the data to the backing file - unsafe { - libc::fsync(self.fd.0); - } - - // Open a new temporary file (which is used for swapping for the forked memory) - let fd = unsafe { - let file = libc::tmpfile(); - if file.is_null() { - return Err(format!( - "failed to create temporary file - {}", - io::Error::last_os_error() - )); - } - FdGuard(libc::fileno(file)) - }; - - // Attempt to do a shallow copy (needs a backing file system that supports it) - unsafe { - if libc::ioctl(fd.0, 0x94, 9, self.fd.0) != 0 - // FICLONE - { - #[cfg(feature = "tracing")] - trace!("memory copy started"); - - // Determine host much to copy - let len = match hint_used { - Some(a) => a, - None => self.len, - }; - - // The shallow copy failed so we have to do it the hard way - - let mut source = std::fs::File::from_raw_fd(self.fd.0); - let mut out = std::fs::File::from_raw_fd(fd.0); - copy_file_range(&mut source, 0, &mut out, 0, len) - .map_err(|err| format!("Could not copy memory: {err}"))?; - - #[cfg(feature = "tracing")] - trace!("memory copy finished (size={})", len); - } - } - - // Compute the flags - let flags = libc::MAP_FILE | libc::MAP_SHARED; - - // Allocate a single read-write region at once. - let ptr = unsafe { - libc::mmap( - ptr::null_mut(), - self.len, - libc::PROT_READ | libc::PROT_WRITE, - flags, - fd.0, - 0, - ) - }; - if ptr as isize == -1_isize { - return Err(io::Error::last_os_error().to_string()); - } - - Ok(Self { - ptr: ptr as usize, - len: self.len, - fd, - }) - } - - /// Copies the memory to a new swap file (using copy-on-write if available) - #[cfg(target_os = "windows")] - pub fn duplicate(&mut self, hint_used: Option) -> Result { - // Create a new memory which we will copy to - let new_mmap = Self::with_at_least(self.len)?; - - #[cfg(feature = "tracing")] - trace!("memory copy started"); - - // Determine host much to copy - let len = match hint_used { - Some(a) => a, - None => self.len, - }; - - // Copy the data to the new memory - let dst = new_mmap.ptr as *mut u8; - let src = self.ptr as *const u8; - unsafe { - std::ptr::copy_nonoverlapping(src, dst, len); - } - - #[cfg(feature = "tracing")] - trace!("memory copy finished (size={})", len); - Ok(new_mmap) + /// Duplicate in a new memory mapping. + pub fn duplicate(&mut self, _size_hint: Option) -> Result { + let mut new = Self::accessible_reserved(self.accessible_size, self.total_size)?; + new.as_mut_slice_accessible() + .copy_from_slice(self.as_slice_accessible()); + Ok(new) } } impl Drop for Mmap { #[cfg(not(target_os = "windows"))] fn drop(&mut self) { - if self.len != 0 { - let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) }; + if self.total_size != 0 { + let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.total_size) }; assert_eq!(r, 0, "munmap failed: {}", io::Error::last_os_error()); } } @@ -447,54 +306,6 @@ fn _assert() { _assert_send_sync::(); } -/// Copy a range of a file to another file. -// We could also use libc::copy_file_range on some systems, but it's -// hard to do this because it is not available on many libc implementations. -// (not on Mac OS, musl, ...) -#[cfg(target_family = "unix")] -fn copy_file_range( - source: &mut std::fs::File, - source_offset: u64, - out: &mut std::fs::File, - out_offset: u64, - len: usize, -) -> Result<(), std::io::Error> { - use std::io::{Seek, SeekFrom}; - - let source_original_pos = source.stream_position()?; - source.seek(SeekFrom::Start(source_offset))?; - - // TODO: don't cast with as - - let out_original_pos = out.stream_position()?; - out.seek(SeekFrom::Start(out_offset))?; - - // TODO: don't do this horrible "triple buffering" below". - // let mut reader = std::io::BufReader::new(source); - - // TODO: larger buffer? - let mut buffer = vec![0u8; 4096]; - - let mut to_read = len; - while to_read > 0 { - let chunk_size = std::cmp::min(to_read, buffer.len()); - let read = source.read(&mut buffer[0..chunk_size])?; - out.write_all(&buffer[0..read])?; - to_read -= read; - } - - // Need to read the last chunk. - out.flush()?; - - // Restore files to original position. - source.seek(SeekFrom::Start(source_original_pos))?; - out.flush()?; - out.sync_data()?; - out.seek(SeekFrom::Start(out_original_pos))?; - - Ok(()) -} - #[cfg(test)] mod tests { use super::*; @@ -506,59 +317,4 @@ mod tests { assert_eq!(round_up_to_page_size(4096, 4096), 4096); assert_eq!(round_up_to_page_size(4097, 4096), 8192); } - - #[cfg(target_family = "unix")] - #[test] - fn test_copy_file_range() -> Result<(), std::io::Error> { - // I know tempfile:: exists, but this doesn't bring in an extra - // dependency. - - use std::{fs::OpenOptions, io::Seek}; - - let dir = std::env::temp_dir().join("wasmer/copy_file_range"); - if dir.is_dir() { - std::fs::remove_dir_all(&dir).unwrap() - } - std::fs::create_dir_all(&dir).unwrap(); - - let pa = dir.join("a"); - let pb = dir.join("b"); - - let data: Vec = (0..100).collect(); - let mut a = OpenOptions::new() - .read(true) - .write(true) - .create_new(true) - .open(&pa) - .unwrap(); - a.write_all(&data).unwrap(); - - let datb: Vec = (100..200).collect(); - let mut b = OpenOptions::new() - .read(true) - .write(true) - .create_new(true) - .open(&pb) - .unwrap(); - b.write_all(&datb).unwrap(); - - a.seek(io::SeekFrom::Start(30)).unwrap(); - b.seek(io::SeekFrom::Start(99)).unwrap(); - copy_file_range(&mut a, 10, &mut b, 40, 15).unwrap(); - - assert_eq!(a.stream_position().unwrap(), 30); - assert_eq!(b.stream_position().unwrap(), 99); - - b.seek(io::SeekFrom::Start(0)).unwrap(); - let mut out = Vec::new(); - let len = b.read_to_end(&mut out).unwrap(); - assert_eq!(len, 100); - assert_eq!(out[0..40], datb[0..40]); - assert_eq!(out[40..55], data[10..25]); - assert_eq!(out[55..100], datb[55..100]); - - // TODO: needs more variant tests, but this is enough for now. - - Ok(()) - } } From bd3c3e1fbc580d91272141131e479044cba277d3 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Tue, 27 Dec 2022 13:57:37 +0100 Subject: [PATCH 36/56] Remove redundant verbose feature from compiler-cranelift Was added to toggle trace messages off, but this can be done much easier with tracing compile time flags. --- lib/compiler-cranelift/Cargo.toml | 1 - lib/compiler-cranelift/src/translator/func_translator.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/compiler-cranelift/Cargo.toml b/lib/compiler-cranelift/Cargo.toml index 425bc3873d7..ca3a4b52230 100644 --- a/lib/compiler-cranelift/Cargo.toml +++ b/lib/compiler-cranelift/Cargo.toml @@ -38,4 +38,3 @@ wasm = ["std", "unwind"] unwind = ["cranelift-codegen/unwind", "gimli"] std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmer-compiler/std", "wasmer-types/std"] core = ["hashbrown", "cranelift-codegen/core", "cranelift-frontend/core"] -verbose = [] diff --git a/lib/compiler-cranelift/src/translator/func_translator.rs b/lib/compiler-cranelift/src/translator/func_translator.rs index 39d77d9681b..d8a4db96a60 100644 --- a/lib/compiler-cranelift/src/translator/func_translator.rs +++ b/lib/compiler-cranelift/src/translator/func_translator.rs @@ -79,8 +79,7 @@ impl FuncTranslator { environ: &mut FE, ) -> WasmResult<()> { let _tt = timing::wasm_translate_function(); - #[cfg(feature = "verbose")] - tracing::info!( + tracing::trace!( "translate({} bytes, {}{})", reader.bytes_remaining(), func.name, From fdce8a6c00ae5b02bfdc16e11a1d04b60376fe90 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 28 Dec 2022 14:43:28 +0100 Subject: [PATCH 37/56] chore: Formatting... --- lib/cache/src/cache.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/cache/src/cache.rs b/lib/cache/src/cache.rs index a2b4f5ec58a..340163db956 100644 --- a/lib/cache/src/cache.rs +++ b/lib/cache/src/cache.rs @@ -4,7 +4,7 @@ use crate::hash::Hash; use std::error::Error; -use wasmer::{Module, AsEngineRef}; +use wasmer::{AsEngineRef, Module}; /// A generic cache for storing and loading compiled wasm modules. pub trait Cache { @@ -17,7 +17,11 @@ pub trait Cache { /// /// # Safety /// This function is unsafe as the cache store could be tampered with. - unsafe fn load(&self, engine: &impl AsEngineRef, key: Hash) -> Result; + unsafe fn load( + &self, + engine: &impl AsEngineRef, + key: Hash, + ) -> Result; /// Store a [`Module`] into the cache with the given [`Hash`]. fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError>; From 87d6fb854a4d3a3e2d805ba9cede0568dce83e04 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 28 Dec 2022 14:56:15 +0100 Subject: [PATCH 38/56] Fix webc Bindings usage Incorrect type... --- lib/wasi/src/runners/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi/src/runners/mod.rs b/lib/wasi/src/runners/mod.rs index 4cac42921f2..cbfad9ca137 100644 --- a/lib/wasi/src/runners/mod.rs +++ b/lib/wasi/src/runners/mod.rs @@ -107,7 +107,7 @@ impl Bindings for WitBindings { container: &WapmContainer, value: &serde_cbor::Value, ) -> Result { - let value: webc::WitBindingsExtended = + let value: webc::BindingsExtended = serde_cbor::from_slice(&serde_cbor::to_vec(value).unwrap()) .map_err(|e| format!("could not parse WitBindings annotations: {e}"))?; From 4f2948caa0b617d35a638284183c59c792b5bb02 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 28 Dec 2022 14:56:33 +0100 Subject: [PATCH 39/56] Add iter_globals and set_global_unchecked to js Store Was previously added to the sys Store as well. Needed for snapshotting / thread spawning in wasix. --- lib/api/src/js/store.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index fb0ab551543..830841d0642 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -210,6 +210,8 @@ impl AsStoreMut for &'_ mut T { pub use objects::*; mod objects { + use wasm_bindgen::JsValue; + use crate::js::{ export::{VMFunction, VMGlobal, VMMemory, VMTable}, function_env::VMFunctionEnvironment, @@ -313,6 +315,21 @@ mod objects { (&mut high[0], &mut low[a.index()]) } } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, val: u128) { + assert!(idx < self.globals.len()); + + let value = JsValue::from(val); + self.globals[idx].global.set_value(&value); + } } /// Handle to an object managed by a context. From 0de8340781f5c2166b08d3e7b27bc4be4038880d Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 28 Dec 2022 16:18:26 +0100 Subject: [PATCH 40/56] Fix webc runner exports detection Related to webc upgrade --- lib/wasi/src/runners/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi/src/runners/mod.rs b/lib/wasi/src/runners/mod.rs index cbfad9ca137..4965bbd6415 100644 --- a/lib/wasi/src/runners/mod.rs +++ b/lib/wasi/src/runners/mod.rs @@ -111,7 +111,7 @@ impl Bindings for WitBindings { serde_cbor::from_slice(&serde_cbor::to_vec(value).unwrap()) .map_err(|e| format!("could not parse WitBindings annotations: {e}"))?; - let mut wit_bindgen_filepath = value.wit.exports; + let mut wit_bindgen_filepath = value.exports().unwrap_or_default().to_string(); for v in container.get_volumes() { let schema = format!("{v}://"); From cbce9b8757758e5dc03b1fa97ac91ef45380d05a Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 28 Dec 2022 16:20:18 +0100 Subject: [PATCH 41/56] Remove unused import (dependent on feature flags) --- lib/wasi/src/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs index b05c5c302db..ba0df86f276 100644 --- a/lib/wasi/src/utils.rs +++ b/lib/wasi/src/utils.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; #[cfg(not(feature = "js"))] use wasmer::vm::VMSharedMemory; -use wasmer::{AsStoreMut, Imports, Memory, Module}; +use wasmer::{AsStoreMut, Imports, Module}; use wasmer_wasi_types::wasi::Errno; #[allow(dead_code)] @@ -75,7 +75,7 @@ pub fn wasi_import_shared_memory( imports.define( "env", "memory", - Memory::new_from_existing(store, memory.into()), + wasmer::Memory::new_from_existing(store, memory.into()), ); } }; From 3f14b00602a6803184d83c8e2bc7c82a955ab5eb Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 28 Dec 2022 17:27:07 +0100 Subject: [PATCH 42/56] Fix Mmap creation on Windows Forgot to update field names... --- lib/vm/src/mmap.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 78f6c1c8f91..11fa061a204 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -158,7 +158,8 @@ impl Mmap { Self { ptr: ptr as usize, - len: mapping_size, + total_size: mapping_size, + accessible_size, } } else { // Reserve the mapping size. @@ -170,7 +171,8 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, - len: mapping_size, + total_size: mapping_size, + accessible_size, }; if accessible_size != 0 { @@ -210,8 +212,8 @@ impl Mmap { let page_size = region::page::size(); assert_eq!(start & (page_size - 1), 0); assert_eq!(len & (page_size - 1), 0); - assert_lt!(len, self.len); - assert_lt!(start, self.len - len); + assert_lt!(len, self.len()); + assert_lt!(start, self.len() - len); // Commit the accessible size. let ptr = self.ptr as *const u8; @@ -291,7 +293,7 @@ impl Drop for Mmap { #[cfg(target_os = "windows")] fn drop(&mut self) { - if self.len != 0 { + if self.len() != 0 { use winapi::ctypes::c_void; use winapi::um::memoryapi::VirtualFree; use winapi::um::winnt::MEM_RELEASE; From b0d118d349aae8886b42c42e6ed7b350b421ff7d Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 2 Jan 2023 12:26:08 +0100 Subject: [PATCH 43/56] ci: Disable test-js-core test no_std functionality doesn't work at all at the moment. Was already commented out in the Makefile. --- .github/workflows/test.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a6be7757ad1..7eb86f97aaa 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -104,9 +104,11 @@ jobs: - name: make test-js run: | make test-js - - name: make test-js-core - run: | - make test-js-core + + # The no_std functionality doesn't work at the moment - no point in testing it. + # - name: make test-js-core + # run: | + # make test-js-core test_wasm_build: name: Test wasm build From d45f58d7d31c0c219dd9d0bf99ff689a8d933e8f Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Mon, 2 Jan 2023 12:43:48 +0100 Subject: [PATCH 44/56] ci: Use 1.64 toolchain Missed this during the last merge. --- .github/workflows/test.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7eb86f97aaa..2b40693c1c7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -41,7 +41,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 components: rustfmt, clippy - name: Install libtinfo shell: bash @@ -93,7 +93,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Install NodeJS uses: actions/setup-node@v2 with: @@ -241,7 +241,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.metadata.target }} - name: Install Windows-GNU linker if: ${{ matrix.metadata.build == 'windows-gnu' }} @@ -470,7 +470,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.metadata.target }} - name: Choco install LLVM uses: crazy-max/ghaction-chocolatey@v2 @@ -579,7 +579,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.metadata.target }} - name: Cache uses: whywaita/actions-cache-s3@v2 From bf2f855d3b8747891c4a8a04ca9b618d1688c63c Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 2 Jan 2023 16:04:06 +0100 Subject: [PATCH 45/56] Split js::Instance::from_module_and_instance externs/memory tests in a separate function --- lib/api/src/js/instance.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index ff327e38d34..43623cd329a 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -66,7 +66,9 @@ impl Instance { .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; - let self_instance = Self::from_module_and_instance(store, module, externs, instance)?; + let instance = instance.get(store.objects_mut()).clone(); + let mut self_instance = Self::from_module_and_instance(store, module, instance)?; + self_instance.check_memory(store, externs); //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; Ok(self_instance) } @@ -105,15 +107,13 @@ impl Instance { pub fn from_module_and_instance( mut store: &mut impl AsStoreMut, module: &Module, - externs: Vec, - handle: StoreHandle, + instance: WebAssembly::Instance, ) -> Result { use crate::js::externals::VMExtern; - let instance = handle.get(store.objects_mut()); let instance_exports = instance.exports(); - let mut exports = module + let exports = module .exports() .map(|export_type| { let name = export_type.name(); @@ -130,23 +130,27 @@ impl Instance { }) .collect::>()?; - // If the memory is imported then also export it for backwards compatibility reasons - // (many will assume the memory is always exported) - later we can remove this - if exports.get_memory("memory").is_err() { + let handle = StoreHandle::new(store.as_store_mut().objects_mut(), instance); + Ok(Self { + _handle: handle, + module: module.clone(), + exports, + }) + } + + /// This will check the memory is correctly setup + /// If the memory is imported then also export it for backwards compatibility reasons + /// (many will assume the memory is always exported) - later we can remove this + pub fn check_memory(&mut self, store: &mut impl AsStoreMut, externs: Vec) { + if self.exports.get_memory("memory").is_err() { if let Some(memory) = externs .iter() .filter(|a| a.ty(store).memory().is_some()) .next() { - exports.insert("memory", memory.clone()); + self.exports.insert("memory", memory.clone()); } } - - Ok(Self { - _handle: handle, - module: module.clone(), - exports, - }) } /// Gets the [`Module`] associated with this instance. From 9f7822d33e1310ec107e106729235a5fd15020fe Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 2 Jan 2023 16:24:49 +0100 Subject: [PATCH 46/56] Rename js::Instance::check_memory to ensure_memory_export --- lib/api/src/js/instance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index 43623cd329a..63633ed3008 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -68,7 +68,7 @@ impl Instance { let instance = instance.get(store.objects_mut()).clone(); let mut self_instance = Self::from_module_and_instance(store, module, instance)?; - self_instance.check_memory(store, externs); + self_instance.ensure_memory_export(store, externs); //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; Ok(self_instance) } @@ -141,7 +141,7 @@ impl Instance { /// This will check the memory is correctly setup /// If the memory is imported then also export it for backwards compatibility reasons /// (many will assume the memory is always exported) - later we can remove this - pub fn check_memory(&mut self, store: &mut impl AsStoreMut, externs: Vec) { + pub fn ensure_memory_export(&mut self, store: &mut impl AsStoreMut, externs: Vec) { if self.exports.get_memory("memory").is_err() { if let Some(memory) = externs .iter() From 332ad85e9419880182720fa395e839a482154b1d Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 3 Jan 2023 14:07:16 +0100 Subject: [PATCH 47/56] Added TODO on asynctify code, linking to ticket #3451 --- lib/api/src/js/externals/function.rs | 1 + lib/api/src/js/mod.rs | 1 + lib/api/src/js/native.rs | 1 + lib/api/src/sys/externals/function.rs | 1 + lib/api/src/sys/mod.rs | 1 + lib/api/src/sys/native.rs | 1 + lib/types/src/lib.rs | 1 + 7 files changed, 7 insertions(+) diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 23866afedca..1194d58576c 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -418,6 +418,7 @@ impl Function { let result = { let mut r; + //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 loop { r = js_sys::Reflect::apply( &self.handle.get(store.as_store_ref().objects()).function, diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 0fb80d6dd88..96c85ea174c 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -77,6 +77,7 @@ pub mod vm { } pub use wasmer_types::is_wasm; +//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index 8efbcaedbf9..915f1efa0b9 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -76,6 +76,7 @@ macro_rules! impl_native_traits { .collect(); let results = { let mut r; + //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 loop { r = self.handle.get(store.as_store_ref().objects()).function.apply( &JsValue::UNDEFINED, diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 73d9e4d9624..0b053215d73 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -446,6 +446,7 @@ impl Function { // Call the trampoline. let result = { let mut r; + //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 loop { let vm_function = self.handle.get(store.as_store_ref().objects()); r = unsafe { diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index f729812f749..71ba2efa0d3 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -40,6 +40,7 @@ pub use wasmer_compiler::{ pub use wasmer_compiler::{Features, FrameInfo, LinkError, RuntimeError, Tunables}; pub use wasmer_derive::ValueType; pub use wasmer_types::is_wasm; +//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ CpuFeature, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, OnCalledAction, TableType, Target, Type, diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index 04389e16513..539a6e15663 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -96,6 +96,7 @@ macro_rules! impl_native_traits { }; let mut r; + //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 loop { r = unsafe { wasmer_vm::wasmer_call_trampoline( diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index a9d708dedf1..e42c0d3dbc6 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -105,6 +105,7 @@ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; pub use crate::memory::MemoryStyle; pub use crate::table::TableStyle; +//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 pub use crate::trapcode::{OnCalledAction, TrapCode}; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; From a05037663632f65b620ec5bc1b9c3dc72ab850d4 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 4 Jan 2023 18:02:18 +0100 Subject: [PATCH 48/56] [JS] Rmoved js-serializable-module feature from wasm-types-polyfill --- lib/api/Cargo.toml | 2 +- lib/api/src/js/module.rs | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index c6b700301cd..7e1939737b9 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -118,7 +118,7 @@ jit = ["engine"] js = ["wasm-bindgen", "js-sys"] js-default = ["js", "std", "wasm-types-polyfill"] -wasm-types-polyfill = ["js", "wasmparser", "js-serializable-module" ] +wasm-types-polyfill = ["js", "wasmparser"] js-serializable-module = [] diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 081a4cfbd4a..b58aab358ad 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -180,12 +180,7 @@ impl Module { let js_bytes = Uint8Array::view(binary); let module = WebAssembly::Module::new(&js_bytes.into()).unwrap(); - Self::from_js_module( - store, - module, - #[cfg(feature = "js-serializable-module")] - binary, - ) + Self::from_js_module(store, module, binary) } /// Creates a new WebAssembly module skipping any kind of validation from a javascript module @@ -193,9 +188,8 @@ impl Module { pub unsafe fn from_js_module( _store: &impl AsStoreRef, module: WebAssembly::Module, - #[cfg(feature = "js-serializable-module")] binary: impl IntoBytes, + binary: impl IntoBytes, ) -> Result { - #[cfg(feature = "js-serializable-module")] let binary = binary.into_bytes(); // The module is now validated, so we can safely parse it's types #[cfg(feature = "wasm-types-polyfill")] From 212f836e300ab7523b0966115e89dafd746ed92a Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 4 Jan 2023 18:21:17 +0100 Subject: [PATCH 49/56] Missed some sources to add a TODO mark about asynctify --- lib/api/src/sys/native.rs | 2 +- lib/api/src/sys/store.rs | 1 + lib/types/src/trapcode.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index 539a6e15663..b4f535c41c4 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -96,7 +96,6 @@ macro_rules! impl_native_traits { }; let mut r; - //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 loop { r = unsafe { wasmer_vm::wasmer_call_trampoline( @@ -188,6 +187,7 @@ macro_rules! impl_native_traits { }; let store_mut = store.as_store_mut(); if let Some(callback) = store_mut.inner.on_called.take() { + //TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 match callback(store_mut) { Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } Ok(wasmer_types::OnCalledAction::Finish) => { break; } diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index b1406bb88d4..ff6975f475d 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -327,6 +327,7 @@ impl<'a> StoreMut<'a> { Self { inner: &mut *raw } } + //TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 /// Sets the unwind callback which will be invoked when the call finishes pub fn on_called(&mut self, callback: F) where diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 134a4b3ced1..5b0145d41c4 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -120,6 +120,7 @@ impl FromStr for TrapCode { } } +//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 /// After the stack is unwound via asyncify what /// should the call loop do next #[derive(Debug)] From 9fbc7f4ea5687f9046d3eeaea3bde302d7563f70 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 4 Jan 2023 18:38:17 +0100 Subject: [PATCH 50/56] Removed default Compiler::name() implementation --- lib/compiler/src/compiler.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/compiler.rs b/lib/compiler/src/compiler.rs index 03f760241f0..db14a998972 100644 --- a/lib/compiler/src/compiler.rs +++ b/lib/compiler/src/compiler.rs @@ -80,10 +80,9 @@ where /// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. pub trait Compiler: Send { /// Returns a descriptive name for this compiler. - // TODO!: Probably want to make this mandatory on the next major bump. - fn name(&self) -> &str { - "UNKNOWN" - } + /// + /// Note that this is an API breaking change since 3.0 + fn name(&self) -> &str; /// Validates a module. /// From 5b5d032583d7d6b9220704ad5627eff2d556dda0 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 4 Jan 2023 19:19:12 +0100 Subject: [PATCH 51/56] Fixed linter --- lib/compiler/src/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler/src/compiler.rs b/lib/compiler/src/compiler.rs index db14a998972..be25c218a70 100644 --- a/lib/compiler/src/compiler.rs +++ b/lib/compiler/src/compiler.rs @@ -80,7 +80,7 @@ where /// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. pub trait Compiler: Send { /// Returns a descriptive name for this compiler. - /// + /// /// Note that this is an API breaking change since 3.0 fn name(&self) -> &str; From 8d7190b8db187839fb5dc442f1424e6558440293 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 5 Jan 2023 09:24:36 +0100 Subject: [PATCH 52/56] Changed TODO mark about asyncify --- lib/api/src/js/externals/function.rs | 2 +- lib/api/src/js/mod.rs | 2 +- lib/api/src/js/native.rs | 2 +- lib/api/src/sys/externals/function.rs | 2 +- lib/api/src/sys/mod.rs | 2 +- lib/api/src/sys/native.rs | 2 +- lib/api/src/sys/store.rs | 2 +- lib/types/src/lib.rs | 2 +- lib/types/src/trapcode.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 1194d58576c..e8c2c5af950 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -418,7 +418,7 @@ impl Function { let result = { let mut r; - //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 loop { r = js_sys::Reflect::apply( &self.handle.get(store.as_store_ref().objects()).function, diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 96c85ea174c..baa41641fbf 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -77,7 +77,7 @@ pub mod vm { } pub use wasmer_types::is_wasm; -//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index 915f1efa0b9..661034ac972 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -76,7 +76,7 @@ macro_rules! impl_native_traits { .collect(); let results = { let mut r; - //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 loop { r = self.handle.get(store.as_store_ref().objects()).function.apply( &JsValue::UNDEFINED, diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 0b053215d73..3c3d383113a 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -446,7 +446,7 @@ impl Function { // Call the trampoline. let result = { let mut r; - //TODO: This loop is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 loop { let vm_function = self.handle.get(store.as_store_ref().objects()); r = unsafe { diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 71ba2efa0d3..3989ec10f36 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -40,7 +40,7 @@ pub use wasmer_compiler::{ pub use wasmer_compiler::{Features, FrameInfo, LinkError, RuntimeError, Tunables}; pub use wasmer_derive::ValueType; pub use wasmer_types::is_wasm; -//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ CpuFeature, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, OnCalledAction, TableType, Target, Type, diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index b4f535c41c4..90fd7c14b36 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -187,7 +187,7 @@ macro_rules! impl_native_traits { }; let store_mut = store.as_store_mut(); if let Some(callback) = store_mut.inner.on_called.take() { - //TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 + // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 match callback(store_mut) { Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } Ok(wasmer_types::OnCalledAction::Finish) => { break; } diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index ff6975f475d..032b6c60d23 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -327,7 +327,7 @@ impl<'a> StoreMut<'a> { Self { inner: &mut *raw } } - //TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 + // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 /// Sets the unwind callback which will be invoked when the call finishes pub fn on_called(&mut self, callback: F) where diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index e42c0d3dbc6..1c84890dcc1 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -105,7 +105,7 @@ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; pub use crate::memory::MemoryStyle; pub use crate::table::TableStyle; -//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use crate::trapcode::{OnCalledAction, TrapCode}; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 5b0145d41c4..75c4e27d27b 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -120,7 +120,7 @@ impl FromStr for TrapCode { } } -//TODO: OnCalledAction is needed for async. It will be refactor with https://github.com/wasmerio/wasmer/issues/3451 +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 /// After the stack is unwound via asyncify what /// should the call loop do next #[derive(Debug)] From b126849c34e1621cd81db73f3a622f17ab3fc880 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 5 Jan 2023 09:57:29 +0100 Subject: [PATCH 53/56] Added one more TODO, about the imported/exported memory hack on js --- lib/api/src/js/module.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index b58aab358ad..5c55cdc800a 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -255,6 +255,14 @@ impl Module { InstantiationError::DifferentStores, ))); } + // TODO: refactor this if possible, after the WASIX merge. + // The imported/exported memory does not have the correct properties + // (incorrect size and shared flag) hence when using shared memory its + // failing - the only way to fix it is to resolve the import and use the + // correct memory properties. this regression issue was only found + // in WASIX on the browser as the other areas don't mind that they don't match up + // sharrattj/dash should be able to reproduce this. + let imports_object = js_sys::Object::new(); let mut import_externs: Vec = vec![]; for import_type in self.imports() { From 60d1fcf46ff3158caba8d757cd83716417b4781f Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 5 Jan 2023 10:25:12 +0100 Subject: [PATCH 54/56] Fixed linter --- lib/api/src/js/module.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 5c55cdc800a..e216d0109e6 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -256,10 +256,10 @@ impl Module { ))); } // TODO: refactor this if possible, after the WASIX merge. - // The imported/exported memory does not have the correct properties - // (incorrect size and shared flag) hence when using shared memory its - // failing - the only way to fix it is to resolve the import and use the - // correct memory properties. this regression issue was only found + // The imported/exported memory does not have the correct properties + // (incorrect size and shared flag) hence when using shared memory its + // failing - the only way to fix it is to resolve the import and use the + // correct memory properties. this regression issue was only found // in WASIX on the browser as the other areas don't mind that they don't match up // sharrattj/dash should be able to reproduce this. From e73f195aacc936e329c237fb96ba52a7555fc531 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 16 Jan 2023 13:56:44 +0100 Subject: [PATCH 55/56] Fixed error message in test-integration-cli-ci --- tests/integration/cli/tests/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 27abc3771f3..fba20733e7b 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -552,7 +552,7 @@ fn run_wasi_works_non_existent() -> anyhow::Result<()> { assert_eq!( stderr_lines, - vec!["error: invalid package name, could not find file does/not/exist".to_string()] + vec!["error: Could not find local file does/not/exist".to_string()] ); Ok(()) From e9edb7e8e6ccf07475a1ebf11ad79e3d57f68633 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Tue, 17 Jan 2023 00:20:36 +0100 Subject: [PATCH 56/56] chore; Add some explanatory comments on allow(unused_unsafe) annotations --- lib/api/src/js/externals/function.rs | 1 + lib/api/src/js/externals/memory.rs | 1 + lib/api/src/js/imports.rs | 5 +++++ lib/api/src/js/instance.rs | 1 + lib/api/src/js/module.rs | 7 +++++++ 5 files changed, 15 insertions(+) diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index e8c2c5af950..378e07e0885 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -399,6 +399,7 @@ impl Function { store: &mut impl AsStoreMut, params: &[Value], ) -> Result, RuntimeError> { + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] let params: Vec<_> = unsafe { params diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 44ff19fc08a..58da006b201 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -92,6 +92,7 @@ impl Memory { pub(crate) fn new_internal(ty: MemoryType) -> Result { let descriptor = js_sys::Object::new(); + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs index 80235e2cc43..de877fe0e57 100644 --- a/lib/api/src/js/imports.rs +++ b/lib/api/src/js/imports.rs @@ -171,12 +171,14 @@ impl Imports { for (ns, exports) in namespaces.into_iter() { let import_namespace = js_sys::Object::new(); for (name, ext) in exports { + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(store)) .expect("Error while setting into the js namespace object"); } } + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) @@ -238,6 +240,7 @@ impl Imports { } impl AsJs for Imports { + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { let imports_object = js_sys::Object::new(); @@ -245,6 +248,8 @@ impl AsJs for Imports { let val = unsafe { js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap() }; if !val.is_undefined() { // If the namespace is already set + + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { js_sys::Reflect::set( diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index 63633ed3008..255240c5e82 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -118,6 +118,7 @@ impl Instance { .map(|export_type| { let name = export_type.name(); let extern_type = export_type.ty().clone(); + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] let js_export = unsafe { js_sys::Reflect::get(&instance_exports, &name.into()) diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index c5963410a87..7b22ffaa0b1 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -232,6 +232,7 @@ impl Module { /// validation of the Module. pub fn validate(_store: &impl AsStoreRef, binary: &[u8]) -> Result<(), CompileError> { let js_bytes = unsafe { Uint8Array::view(binary) }; + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { match WebAssembly::validate(&js_bytes.into()) { @@ -267,6 +268,7 @@ impl Module { let mut import_externs: Vec = vec![]; for import_type in self.imports() { let resolved_import = imports.get_export(import_type.module(), import_type.name()); + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_variables)] if let wasmer_types::ExternType::Memory(mem_ty) = import_type.ty() { if resolved_import.is_some() { @@ -282,6 +284,7 @@ impl Module { ); } } + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { if let Some(import) = resolved_import { @@ -468,6 +471,7 @@ impl Module { .iter() .enumerate() .map(move |(i, val)| { + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] unsafe { let module = Reflect::get(val.as_ref(), &"module".into()) @@ -530,6 +534,7 @@ impl Module { return Err("The exports length must match the type hints lenght".to_owned()); } for (i, val) in exports.iter().enumerate() { + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] let kind = unsafe { Reflect::get(val.as_ref(), &"kind".into()) @@ -582,6 +587,7 @@ impl Module { .iter() .enumerate() .map(move |(i, val)| { + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] let field = unsafe { Reflect::get(val.as_ref(), &"name".into()) @@ -589,6 +595,7 @@ impl Module { .as_string() .unwrap() }; + // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] let kind = unsafe { Reflect::get(val.as_ref(), &"kind".into())