From 2d213296c9ece80492fd624350bd3f921b2af9dc Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 17 Feb 2022 15:03:28 +0200 Subject: [PATCH] Add a concept of an Executable Executable is a deserialized but not loaded compiled code. This allows us to implement a mechanism by which we copy/create WebAssembly module data fewer times before it becomes an instance. Of particular interest, it can help to avoid allocating the tree of maps and vectors that are used to represent the module when an Artifact has already been compiled. --- Cargo.lock | 308 +------- Cargo.toml | 15 +- Makefile | 71 -- benches/many_functions.rs | 28 +- benches/static_and_dynamic_functions.rs | 22 +- examples/compiler_singlepass.rs | 2 +- examples/tunables_limit_memory.rs | 2 - lib/api/Cargo.toml | 1 - lib/api/src/lib.rs | 10 +- lib/api/src/sys/env.rs | 2 +- lib/api/src/sys/exports.rs | 31 +- lib/api/src/sys/externals/function.rs | 89 +-- lib/api/src/sys/externals/global.rs | 7 +- lib/api/src/sys/externals/memory.rs | 8 +- lib/api/src/sys/externals/mod.rs | 7 +- lib/api/src/sys/externals/table.rs | 7 +- lib/api/src/sys/import_object.rs | 4 +- lib/api/src/sys/instance.rs | 115 ++- lib/api/src/sys/mod.rs | 12 +- lib/api/src/sys/module.rs | 274 +------- lib/api/src/sys/native.rs | 8 +- lib/api/src/sys/store.rs | 41 +- lib/api/src/sys/tunables.rs | 8 +- lib/api/src/sys/types.rs | 12 +- lib/api/tests/sys_export.rs | 127 ++-- lib/api/tests/sys_externals.rs | 5 +- lib/api/tests/sys_instance.rs | 2 +- lib/api/tests/sys_module.rs | 172 +---- lib/api/tests/sys_reference_types.rs | 236 ++++--- lib/cache/Cargo.toml | 31 - lib/cache/README.md | 29 - lib/cache/benches/bench_filesystem_cache.rs | 95 --- lib/cache/src/cache.rs | 24 - lib/cache/src/filesystem.rs | 115 --- lib/cache/src/hash.rs | 74 -- lib/cache/src/lib.rs | 31 - lib/cli/Cargo.toml | 4 - lib/cli/src/commands/compile.rs | 1 + lib/cli/src/commands/run.rs | 91 +-- lib/compiler-cranelift/Cargo.toml | 1 - lib/compiler-cranelift/src/compiler.rs | 8 +- lib/compiler-cranelift/src/config.rs | 5 +- lib/compiler-cranelift/src/func_environ.rs | 2 +- lib/compiler-llvm/Cargo.toml | 1 - lib/compiler-llvm/src/compiler.rs | 6 - lib/compiler-llvm/src/config.rs | 5 +- lib/compiler-llvm/src/translator/code.rs | 3 +- .../src/translator/intrinsics.rs | 2 +- lib/compiler-singlepass/Cargo.toml | 3 +- lib/compiler-singlepass/src/codegen_x64.rs | 20 +- lib/compiler-singlepass/src/compiler.rs | 40 +- lib/compiler-singlepass/src/config.rs | 7 +- lib/compiler/Cargo.toml | 6 +- lib/compiler/src/address_map.rs | 14 +- lib/compiler/src/compiler.rs | 6 +- lib/compiler/src/error.rs | 7 + lib/compiler/src/function.rs | 64 +- lib/compiler/src/jump_table.rs | 11 +- lib/compiler/src/lib.rs | 8 +- lib/compiler/src/module.rs | 9 +- lib/compiler/src/relocation.rs | 20 +- lib/compiler/src/section.rs | 65 +- lib/compiler/src/sourceloc.rs | 9 +- lib/compiler/src/target.rs | 5 +- lib/compiler/src/translator/environ.rs | 39 +- lib/compiler/src/translator/middleware.rs | 3 +- lib/compiler/src/translator/sections.rs | 14 +- lib/compiler/src/trap.rs | 8 +- lib/compiler/src/unwind.rs | 39 +- lib/derive/src/lib.rs | 68 +- lib/engine-universal/Cargo.toml | 10 +- lib/engine-universal/src/artifact.rs | 495 ++++--------- lib/engine-universal/src/code_memory.rs | 30 +- lib/engine-universal/src/engine.rs | 512 +++++++++++--- lib/engine-universal/src/executable.rs | 246 +++++++ lib/engine-universal/src/lib.rs | 4 +- lib/engine-universal/src/link.rs | 50 +- lib/engine-universal/src/serialize.rs | 104 --- lib/engine-universal/src/unwind/dummy.rs | 2 - lib/engine-universal/src/unwind/systemv.rs | 16 +- .../src/unwind/windows_x64.rs | 20 - lib/engine/Cargo.toml | 1 - lib/engine/src/artifact.rs | 224 ------ lib/engine/src/engine.rs | 67 +- lib/engine/src/error.rs | 16 +- lib/engine/src/executable.rs | 57 ++ lib/engine/src/export.rs | 167 ----- lib/engine/src/lib.rs | 17 +- lib/engine/src/resolver.rs | 410 ++++------- lib/engine/src/trap/frame_info.rs | 86 +-- lib/engine/src/trap/mod.rs | 5 +- lib/engine/src/tunables.rs | 129 ---- lib/types/Cargo.toml | 6 +- lib/types/src/archives.rs | 6 + lib/types/src/entity/boxed_slice.rs | 17 - lib/types/src/entity/primary_map.rs | 49 +- lib/types/src/entity/secondary_map.rs | 40 +- lib/types/src/features.rs | 27 +- lib/types/src/indexes.rs | 213 ++---- lib/types/src/initializers.rs | 56 +- lib/types/src/lib.rs | 10 +- lib/types/src/module.rs | 395 ++++------- lib/types/src/types.rs | 256 +++---- lib/types/src/units.rs | 12 +- lib/vm/Cargo.toml | 5 +- lib/vm/src/artifact.rs | 64 ++ lib/vm/src/export.rs | 15 +- lib/vm/src/func_data_registry.rs | 25 +- lib/vm/src/global.rs | 5 +- lib/vm/src/imports.rs | 27 + lib/vm/src/instance/allocator.rs | 10 +- lib/vm/src/instance/mod.rs | 634 ++++++----------- lib/vm/src/instance/ref.rs | 26 +- lib/vm/src/lib.rs | 70 +- lib/vm/src/libcalls.rs | 55 +- lib/vm/src/memory.rs | 45 +- lib/vm/src/mmap.rs | 9 - lib/vm/src/resolver.rs | 332 +++++++++ lib/vm/src/sig_registry.rs | 73 +- lib/vm/src/table.rs | 35 +- lib/vm/src/trap/mod.rs | 4 +- lib/vm/src/trap/trapcode.rs | 43 +- lib/vm/src/trap/traphandlers.rs | 665 +----------------- lib/vm/src/tunables.rs | 53 ++ lib/vm/src/vmcontext.rs | 170 ++--- lib/vm/src/vmoffsets.rs | 69 +- tests/compilers/compilation.rs | 40 +- tests/compilers/deterministic.rs | 15 +- tests/compilers/fast_gas_metering.rs | 21 +- tests/compilers/imports.rs | 200 +++--- tests/compilers/issues.rs | 5 +- tests/compilers/middlewares.rs | 15 +- tests/compilers/native_functions.rs | 21 +- tests/compilers/serialize.rs | 137 ++-- tests/compilers/stack_limiter.rs | 18 +- tests/compilers/traps.rs | 36 +- tests/lib/compiler-test-derive/src/lib.rs | 2 +- tests/lib/compiler-test-derive/src/tests.rs | 12 +- tests/lib/engine-dummy/Cargo.toml | 30 - tests/lib/engine-dummy/README.md | 10 - tests/lib/engine-dummy/src/artifact.rs | 274 -------- tests/lib/engine-dummy/src/engine.rs | 134 ---- tests/lib/engine-dummy/src/lib.rs | 5 - tests/lib/wast/src/lib.rs | 4 +- tests/lib/wast/src/wast.rs | 62 +- 145 files changed, 3538 insertions(+), 6311 deletions(-) delete mode 100644 lib/cache/Cargo.toml delete mode 100644 lib/cache/README.md delete mode 100644 lib/cache/benches/bench_filesystem_cache.rs delete mode 100644 lib/cache/src/cache.rs delete mode 100644 lib/cache/src/filesystem.rs delete mode 100644 lib/cache/src/hash.rs delete mode 100644 lib/cache/src/lib.rs create mode 100644 lib/engine-universal/src/executable.rs delete mode 100644 lib/engine-universal/src/serialize.rs delete mode 100644 lib/engine/src/artifact.rs create mode 100644 lib/engine/src/executable.rs delete mode 100644 lib/engine/src/tunables.rs create mode 100644 lib/vm/src/artifact.rs create mode 100644 lib/vm/src/resolver.rs create mode 100644 lib/vm/src/tunables.rs delete mode 100644 tests/lib/engine-dummy/Cargo.toml delete mode 100644 tests/lib/engine-dummy/README.md delete mode 100644 tests/lib/engine-dummy/src/artifact.rs delete mode 100644 tests/lib/engine-dummy/src/engine.rs delete mode 100644 tests/lib/engine-dummy/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 62b7ef1618..d3b13e4e9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,18 +67,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - [[package]] name = "atty" version = "0.2.14" @@ -111,35 +99,12 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "blake3" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526c210b4520e416420759af363083471656e819a75e831b8d2c9d5a584f2413" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "digest", -] - [[package]] name = "bstr" version = "0.2.17" @@ -194,12 +159,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytesize" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" - [[package]] name = "cast" version = "0.2.7" @@ -227,35 +186,9 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term", - "atty", "bitflags", - "strsim 0.8.0", "textwrap", "unicode-width", - "vec_map", -] - -[[package]] -name = "colored" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", ] [[package]] @@ -291,12 +224,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "cranelift-bforest" version = "0.76.0" @@ -491,7 +418,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim", "syn", ] @@ -523,15 +450,6 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -553,12 +471,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "distance" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240" - [[package]] name = "dynasm" version = "1.2.0" @@ -618,16 +530,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -[[package]] -name = "fern" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065" -dependencies = [ - "colored 1.9.3", - "log", -] - [[package]] name = "filetime" version = "0.2.15" @@ -646,16 +548,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getopts" version = "0.2.21" @@ -724,12 +616,12 @@ dependencies = [ ] [[package]] -name = "heck" -version = "0.3.3" +name = "hashbrown" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" dependencies = [ - "unicode-segmentation", + "ahash 0.7.6", ] [[package]] @@ -741,12 +633,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "ident_case" version = "1.0.1" @@ -883,27 +769,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "loupe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" -dependencies = [ - "indexmap", - "loupe-derive", - "rustversion", -] - -[[package]] -name = "loupe-derive" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "mach" version = "0.3.2" @@ -1312,21 +1177,21 @@ dependencies = [ [[package]] name = "rend" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1033f6fe7ce48c8333e5412891b933e85d6a3a09728c4883240edf64e7a6f11a" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" -version = "0.7.25" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c74fc6ea0317f1ba207eef55f5401b3180237625211866703183977b57dd45" +checksum = "2cdcf5caf69bcc87b1e3f5427b4f21a32fdd53c2847687bdf9861abb1cdaa0d8" dependencies = [ "bytecheck", - "hashbrown 0.11.2", + "hashbrown 0.12.0", "ptr_meta", "rend", "rkyv_derive", @@ -1335,9 +1200,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.25" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f677bd46008257bfdf28d051a59a72b75c2714754be3f20bcb4cbd835d2723" +checksum = "a6cf557da1f81b8c7e889c59c9c3abaf6978f7feb156b9579e4f8bf6d7a2bada" dependencies = [ "proc-macro2", "quote", @@ -1527,42 +1392,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "structopt" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "syn" version = "1.0.82" @@ -1615,24 +1450,24 @@ dependencies = [ ] [[package]] -name = "test-env-log" +name = "test-generator" +version = "0.1.0" +dependencies = [ + "anyhow", + "target-lexicon", +] + +[[package]] +name = "test-log" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877189d680101869f65ef94168105d6c188b3a143c13a2d42cf8a09c4c704f8a" +checksum = "eb78caec569a40f42c078c798c0e35b922d9054ec28e166f0d6ac447563d91a4" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "test-generator" -version = "0.1.0" -dependencies = [ - "anyhow", - "target-lexicon", -] - [[package]] name = "tester" version = "0.9.0" @@ -1765,24 +1600,12 @@ dependencies = [ "toml", ] -[[package]] -name = "typenum" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" - [[package]] name = "ucd-trie" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - [[package]] name = "unicode-width" version = "0.1.9" @@ -1795,12 +1618,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.3" @@ -1914,48 +1731,6 @@ dependencies = [ "wasmprinter", ] -[[package]] -name = "wasmer-cache" -version = "2.1.0" -dependencies = [ - "blake3", - "criterion", - "hex", - "rand", - "tempfile", - "thiserror", - "wasmer-compiler-singlepass-near", - "wasmer-engine-universal-near", - "wasmer-near", -] - -[[package]] -name = "wasmer-cli" -version = "2.1.0" -dependencies = [ - "anyhow", - "atty", - "bytesize", - "cfg-if", - "colored 2.0.0", - "distance", - "fern", - "log", - "structopt", - "tempfile", - "wasmer-cache", - "wasmer-compiler-cranelift", - "wasmer-compiler-llvm", - "wasmer-compiler-near", - "wasmer-compiler-singlepass-near", - "wasmer-engine-near", - "wasmer-engine-universal-near", - "wasmer-near", - "wasmer-types-near", - "wasmer-vm-near", - "wasmer-wast", -] - [[package]] name = "wasmer-compiler-cranelift" version = "2.1.0" @@ -1966,7 +1741,6 @@ dependencies = [ "gimli 0.25.0", "hashbrown 0.11.2", "lazy_static", - "loupe", "more-asserts", "rayon", "smallvec", @@ -1987,7 +1761,6 @@ dependencies = [ "itertools", "lazy_static", "libc", - "loupe", "object", "rayon", "regex", @@ -2006,7 +1779,6 @@ version = "2.2.2" dependencies = [ "enumset", "hashbrown 0.11.2", - "loupe", "rkyv", "serde", "serde_bytes", @@ -2027,7 +1799,6 @@ dependencies = [ "dynasmrt", "hashbrown 0.11.2", "lazy_static", - "loupe", "memoffset", "more-asserts", "rayon", @@ -2050,21 +1821,6 @@ dependencies = [ "wasmer-near", ] -[[package]] -name = "wasmer-engine-dummy" -version = "2.1.0" -dependencies = [ - "bincode", - "enumset", - "loupe", - "serde", - "serde_bytes", - "wasmer-compiler-near", - "wasmer-engine-near", - "wasmer-types-near", - "wasmer-vm-near", -] - [[package]] name = "wasmer-engine-near" version = "2.2.2" @@ -2072,7 +1828,6 @@ dependencies = [ "backtrace", "enumset", "lazy_static", - "loupe", "memmap2", "more-asserts", "rustc-demangle", @@ -2092,9 +1847,9 @@ dependencies = [ "cfg-if", "enumset", "leb128", - "loupe", "region", "rkyv", + "thiserror", "wasmer-compiler-near", "wasmer-engine-near", "wasmer-types-near", @@ -2102,14 +1857,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wasmer-integration-tests-cli" -version = "2.1.0" -dependencies = [ - "anyhow", - "tempfile", -] - [[package]] name = "wasmer-integration-tests-ios" version = "2.1.0" @@ -2121,7 +1868,6 @@ dependencies = [ "anyhow", "cfg-if", "indexmap", - "loupe", "more-asserts", "target-lexicon", "tempfile", @@ -2144,7 +1890,6 @@ name = "wasmer-types-near" version = "2.2.2" dependencies = [ "indexmap", - "loupe", "rkyv", "serde", "thiserror", @@ -2159,7 +1904,6 @@ dependencies = [ "cfg-if", "indexmap", "libc", - "loupe", "memoffset", "more-asserts", "region", @@ -2193,26 +1937,24 @@ dependencies = [ "criterion", "glob", "lazy_static", - "loupe", "rustc_version", "serial_test", "tempfile", - "test-env-log", "test-generator", + "test-log", "tracing", "tracing-subscriber", - "wasmer-cache", "wasmer-compiler-cranelift", "wasmer-compiler-llvm", "wasmer-compiler-near", "wasmer-compiler-singlepass-near", - "wasmer-engine-dummy", "wasmer-engine-near", "wasmer-engine-universal-near", "wasmer-near", "wasmer-types-near", "wasmer-vm-near", "wasmer-wast", + "wat", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 09db451e0a..f969072f27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ wasmer-compiler-llvm = { version = "2.0.0", path = "lib/compiler-llvm", optional wasmer-engine = { version = "=2.2.2", path = "lib/engine", package = "wasmer-engine-near" } wasmer-engine-universal = { version = "=2.2.2", path = "lib/engine-universal", optional = true, package = "wasmer-engine-universal-near" } wasmer-wast = { version = "2.0.0", path = "tests/lib/wast", optional = true } -wasmer-cache = { version = "2.0.0", path = "lib/cache", optional = true } wasmer-types = { version = "=2.2.2", path = "lib/types", package = "wasmer-types-near" } wasmer-vm = { version = "=2.2.2", path = "lib/vm", package = "wasmer-vm-near" } @@ -27,8 +26,6 @@ cfg-if = "1.0" [workspace] members = [ "lib/api", - "lib/cache", - "lib/cli", "lib/compiler", "lib/compiler-cranelift", "lib/compiler-singlepass", @@ -40,7 +37,6 @@ members = [ "lib/types", "tests/lib/wast", "tests/lib/compiler-test-derive", - "tests/integration/cli", "tests/integration/ios", "fuzz", ] @@ -58,33 +54,30 @@ anyhow = "1.0" criterion = "0.3" lazy_static = "1.4" serial_test = "0.5" -wasmer-engine-dummy = { path = "tests/lib/engine-dummy" } compiler-test-derive = { path = "tests/lib/compiler-test-derive" } tempfile = "3.1" -loupe = "0.1" # For logging tests using the `RUST_LOG=debug` when testing -test-env-log = { version = "0.2", default-features = false, features = ["trace"] } +test-log = { version = "0.2", default-features = false, features = ["trace"] } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] } +wat = "1.0" + [features] # Don't add the compiler features in default, please add them on the Makefile # since we might want to autoconfigure them depending on the availability on the host. default = [ - "wat", + "wasmer/wat", "wast", "universal", "singlepass", - "cache", ] engine = [] universal = [ "wasmer-engine-universal", "engine", ] -cache = ["wasmer-cache"] wast = ["wasmer-wast"] -wat = ["wasmer/wat"] compiler = [ "wasmer/compiler", "wasmer-compiler/translator", diff --git a/Makefile b/Makefile index 8e3072508d..a5f53ec2c7 100644 --- a/Makefile +++ b/Makefile @@ -344,16 +344,6 @@ endif # ##### -# Not really "all", just the default target that builds enough so make -# install will go through. -all: build-wasmer - -build-wasmer: - cargo build --release --manifest-path lib/cli/Cargo.toml $(compiler_features) --bin wasmer - -build-wasmer-debug: - cargo build --manifest-path lib/cli/Cargo.toml $(compiler_features) --features "debug" --bin wasmer - bench: cargo bench $(compiler_features) @@ -369,9 +359,6 @@ bench: # incremental = false # codegen-units = 1 # rpath = false -build-wasmer-headless-minimal: RUSTFLAGS += -C panic=abort -build-wasmer-headless-minimal: - RUSTFLAGS="${RUSTFLAGS}" xargo build --target $(HOST_TARGET) --release --manifest-path=lib/cli/Cargo.toml --no-default-features --features headless-minimal --bin wasmer-headless ifeq ($(IS_DARWIN), 1) strip -u target/$(HOST_TARGET)/release/wasmer-headless else ifeq ($(IS_WINDOWS), 1) @@ -380,21 +367,6 @@ else strip --strip-unneeded target/$(HOST_TARGET)/release/wasmer-headless endif -WAPM_VERSION = v0.5.1 -get-wapm: - [ -d "wapm-cli" ] || git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git - -build-wapm: get-wapm -ifeq ($(IS_DARWIN), 1) - # We build it without bundling sqlite, as is included by default in macos - cargo build --release --manifest-path wapm-cli/Cargo.toml --no-default-features --features "packagesigning telemetry update-notifications" -else - cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" -endif - -build-docs: - cargo doc --release $(compiler_features) --document-private-items --no-deps --workspace --exclude wasmer-c-api - ##### # # Testing. @@ -410,7 +382,6 @@ test-packages: cargo test --all --release $(exclude_tests) cargo test --manifest-path lib/compiler-cranelift/Cargo.toml --release --no-default-features --features=std cargo test --manifest-path lib/compiler-singlepass/Cargo.toml --release --no-default-features --features=std - cargo test --manifest-path lib/cli/Cargo.toml $(compiler_features) --release ##### # @@ -459,53 +430,12 @@ test-integration-ios: # ##### -package-wapm: - mkdir -p "package/bin" -ifneq (, $(filter 1, $(IS_DARWIN) $(IS_LINUX))) - if [ -d "wapm-cli" ]; then \ - cp wapm-cli/$(TARGET_DIR)/wapm package/bin/ ;\ - echo -e "#!/bin/bash\nwapm execute \"\$$@\"" > package/bin/wax ;\ - chmod +x package/bin/wax ;\ - fi -else - if [ -d "wapm-cli" ]; then \ - cp wapm-cli/$(TARGET_DIR)/wapm package/bin/ ;\ - fi -ifeq ($(IS_DARWIN), 1) - codesign -s - package/bin/wapm || true -endif -endif - -package-minimal-headless-wasmer: -ifeq ($(IS_WINDOWS), 1) - if [ -f "target/$(HOST_TARGET)/release/wasmer-headless.exe" ]; then \ - cp target/$(HOST_TARGET)/release/wasmer-headless.exe package/bin ;\ - fi -else - if [ -f "target/$(HOST_TARGET)/release/wasmer-headless" ]; then \ - cp target/$(HOST_TARGET)/release/wasmer-headless package/bin ;\ - fi -endif - -package-wasmer: - mkdir -p "package/bin" -ifeq ($(IS_WINDOWS), 1) - cp $(TARGET_DIR)/wasmer.exe package/bin/ -else - cp $(TARGET_DIR)/wasmer package/bin/ -ifeq ($(IS_DARWIN), 1) - codesign -s - package/bin/wasmer || true -endif -endif - package-docs: build-docs mkdir -p "package/docs/crates" cp -R target/doc/ package/docs/crates echo '' > package/docs/index.html echo '' > package/docs/crates/index.html -package: package-wapm package-wasmer package-minimal-headless-wasmer - distribution: package cp LICENSE package/LICENSE cp ATTRIBUTIONS.md package/ATTRIBUTIONS @@ -558,7 +488,6 @@ update-testsuite: lint-packages: RUSTFLAGS += -D dead-code -D nonstandard-style -D unused-imports -D unused-mut -D unused-variables -D unused-unsafe -D unreachable-patterns -D bad-style -D improper-ctypes -D unused-allocation -D unused-comparisons -D while-true -D unconditional-recursion -D bare-trait-objects # TODO: add `-D missing-docs` # TODO: add `-D function_item_references` (not available on Rust 1.47, try when upgrading) lint-packages: RUSTFLAGS="${RUSTFLAGS}" cargo clippy --all $(exclude_tests) - RUSTFLAGS="${RUSTFLAGS}" cargo clippy --manifest-path lib/cli/Cargo.toml $(compiler_features) RUSTFLAGS="${RUSTFLAGS}" cargo clippy --manifest-path fuzz/Cargo.toml $(compiler_features) lint-formatting: diff --git a/benches/many_functions.rs b/benches/many_functions.rs index 4540cd9407..4c16ac7ffe 100644 --- a/benches/many_functions.rs +++ b/benches/many_functions.rs @@ -1,5 +1,6 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use wasmer::*; +use wasmer_engine_universal::UniversalExecutableRef; fn call_many_functions(n: usize) -> String { let fndefs = (0..n) @@ -28,17 +29,18 @@ fn nops(c: &mut Criterion) { }) }); drop(compile); + let module = Module::new(&store, &wat).unwrap(); let imports = imports! {}; let instance = Instance::new(&module, &imports).unwrap(); let mut get_main = c.benchmark_group("get_main"); get_main.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { - let _: &Function = instance.exports.get("main").unwrap(); + let _: Function = instance.lookup_function("main").unwrap(); }) }); drop(get_main); - let main: &Function = instance.exports.get("main").unwrap(); + let main: Function = instance.lookup_function("main").unwrap(); let mut call_main = c.benchmark_group("call_main"); call_main.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { @@ -47,13 +49,33 @@ fn nops(c: &mut Criterion) { }); drop(call_main); - let single: &Function = instance.exports.get("single").unwrap(); + let single: Function = instance.lookup_function("single").unwrap(); let mut call_single = c.benchmark_group("call_single"); call_single.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { b.iter(|| { black_box(single.call(&[]).unwrap()); }) }); + drop(call_single); + + let mut serialize = c.benchmark_group("serialize"); + let wasm = wat::parse_bytes(wat.as_ref()).unwrap(); + let executable = store.engine().compile(&wasm, store.tunables()).unwrap(); + serialize.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter(|| { + black_box(executable.serialize().unwrap()); + }) + }); + drop(serialize); + + let serialized = executable.serialize().unwrap(); + let mut deserialize = c.benchmark_group("deserialize"); + deserialize.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter(|| unsafe { + let deserialized = UniversalExecutableRef::deserialize(&serialized).unwrap(); + black_box(store.engine().load(&deserialized).unwrap()); + }) + }); } } diff --git a/benches/static_and_dynamic_functions.rs b/benches/static_and_dynamic_functions.rs index 462bac6d5e..b33d0a573e 100644 --- a/benches/static_and_dynamic_functions.rs +++ b/benches/static_and_dynamic_functions.rs @@ -12,7 +12,7 @@ static BASIC_WAT: &str = r#"(module i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32) (i32.add - (i32.add + (i32.add (i32.add (i32.add (i32.add (local.get 0) (local.get 1)) (i32.add (local.get 2) (local.get 3))) (i32.add (i32.add (local.get 4) (local.get 5)) @@ -39,7 +39,7 @@ pub fn run_basic_static_function(store: &Store, compiler_name: &str, c: &mut Cri }, }; let instance = Instance::new(&module, &import_object).unwrap(); - let dyn_f: &Function = instance.exports.get("add").unwrap(); + let dyn_f = instance.lookup_function("add").unwrap(); let f: NativeFunc<(i32, i32), i32> = dyn_f.native().unwrap(); c.bench_function(&format!("basic static func {}", compiler_name), |b| { @@ -49,7 +49,7 @@ pub fn run_basic_static_function(store: &Store, compiler_name: &str, c: &mut Cri }) }); - let dyn_f_many: &Function = instance.exports.get("add20").unwrap(); + let dyn_f_many = instance.lookup_function("add20").unwrap(); let f_many: NativeFunc< ( i32, @@ -101,7 +101,7 @@ pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Cr }; let instance = Instance::new(&module, &import_object).unwrap(); - let dyn_f: &Function = instance.exports.get("add").unwrap(); + let dyn_f = instance.lookup_function("add").unwrap(); c.bench_function(&format!("basic dynfunc {}", compiler_name), |b| { b.iter(|| { let dyn_result = black_box(dyn_f.call(&[Val::I32(4), Val::I32(6)]).unwrap()); @@ -109,7 +109,7 @@ pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Cr }) }); - let dyn_f_many: &Function = instance.exports.get("add20").unwrap(); + let dyn_f_many = instance.lookup_function("add20").unwrap(); c.bench_function( &format!("basic dynfunc with many args {}", compiler_name), |b| { @@ -150,21 +150,21 @@ fn run_static_benchmarks(_c: &mut Criterion) { #[cfg(feature = "llvm")] { let store = Store::new(&Universal::new(wasmer_compiler_llvm::LLVM::new()).engine()); - run_basic_static_function(&store, "llvm", c); + run_basic_static_function(&store, "llvm", _c); } #[cfg(feature = "cranelift")] { let store = Store::new(&Universal::new(wasmer_compiler_cranelift::Cranelift::new()).engine()); - run_basic_static_function(&store, "cranelift", c); + run_basic_static_function(&store, "cranelift", _c); } #[cfg(feature = "singlepass")] { let store = Store::new(&Universal::new(wasmer_compiler_singlepass::Singlepass::new()).engine()); - run_basic_static_function(&store, "singlepass", c); + run_basic_static_function(&store, "singlepass", _c); } } @@ -172,21 +172,21 @@ fn run_dynamic_benchmarks(_c: &mut Criterion) { #[cfg(feature = "llvm")] { let store = Store::new(&Universal::new(wasmer_compiler_llvm::LLVM::new()).engine()); - run_basic_dynamic_function(&store, "llvm", c); + run_basic_dynamic_function(&store, "llvm", _c); } #[cfg(feature = "cranelift")] { let store = Store::new(&Universal::new(wasmer_compiler_cranelift::Cranelift::new()).engine()); - run_basic_dynamic_function(&store, "cranelift", c); + run_basic_dynamic_function(&store, "cranelift", _c); } #[cfg(feature = "singlepass")] { let store = Store::new(&Universal::new(wasmer_compiler_singlepass::Singlepass::new()).engine()); - run_basic_dynamic_function(&store, "singlepass", c); + run_basic_dynamic_function(&store, "singlepass", _c); } } diff --git a/examples/compiler_singlepass.rs b/examples/compiler_singlepass.rs index 845b9db302..a8f087dc19 100644 --- a/examples/compiler_singlepass.rs +++ b/examples/compiler_singlepass.rs @@ -46,7 +46,7 @@ fn main() -> Result<(), Box> { // Let's instantiate the Wasm module. let instance = Instance::new(&module, &import_object)?; - let sum = instance.exports.get_function("sum")?; + let sum = instance.lookup_function("sum").expect("function lookup"); println!("Calling `sum` function..."); // Let's call the `sum` exported function. The parameters are a diff --git a/examples/tunables_limit_memory.rs b/examples/tunables_limit_memory.rs index 690d671613..28d31450b5 100644 --- a/examples/tunables_limit_memory.rs +++ b/examples/tunables_limit_memory.rs @@ -1,7 +1,6 @@ use std::ptr::NonNull; use std::sync::Arc; -use loupe::MemoryUsage; use wasmer::{ imports, vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition}, @@ -15,7 +14,6 @@ use wasmer_engine_universal::Universal; /// /// After adjusting the memory limits, it delegates all other logic /// to the base tunables. -#[derive(MemoryUsage)] pub struct LimitingTunables { /// The maximum a linear memory is allowed to be (in Wasm pages, 64 KiB each). /// Since Wasmer ensures there is only none or one memory, this is practically diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 5ea01ab48a..a7cadfa8d1 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -32,7 +32,6 @@ wasmer-derive = { path = "../derive", version = "=2.2.2", package = "wasmer-deri wasmer-engine = { path = "../engine", version = "=2.2.2", package = "wasmer-engine-near" } wasmer-types = { path = "../types", version = "=2.2.2", package = "wasmer-types-near" } target-lexicon = { version = "0.12.2", default-features = false } -loupe = "0.1" # - Optional dependencies for `sys`. wasmer-compiler-singlepass = { path = "../compiler-singlepass", package = "wasmer-compiler-singlepass-near", version = "=2.2.2", optional = true} wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "2.1.0", optional = true } diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index fb5e2d0c06..ff4809b8af 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -39,7 +39,7 @@ //! written with its WAT format (textual format): //! //! ```rust -//! use wasmer::{Store, Module, Instance, Value, imports}; +//! use wasmer::{Store, Module, Instance, Value, Export, imports}; //! //! fn main() -> anyhow::Result<()> { //! let module_wat = r#" @@ -57,7 +57,7 @@ //! let import_object = imports! {}; //! let instance = Instance::new(&module, &import_object)?; //! -//! let add_one = instance.exports.get_function("add_one")?; +//! let add_one = instance.lookup_function("add_one").unwrap(); //! let result = add_one.call(&[Value::I32(42)])?; //! assert_eq!(result[0], Value::I32(43)); //! @@ -172,9 +172,9 @@ //! ``` //! # use wasmer::{imports, Instance, Function, Memory, NativeFunc}; //! # fn exports_example(instance: &Instance) -> anyhow::Result<()> { -//! let memory = instance.exports.get_memory("memory")?; -//! let memory: &Memory = instance.exports.get("some_other_memory")?; -//! let add: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?; +//! let memory = instance.lookup("memory").unwrap(); +//! let memory = instance.lookup("some_other_memory").unwrap(); +//! let add: NativeFunc<(i32, i32), i32> = instance.get_native_function("add").unwrap(); //! let result = add.call(5, 37)?; //! assert_eq!(result, 42); //! # Ok(()) diff --git a/lib/api/src/sys/env.rs b/lib/api/src/sys/env.rs index 8f947b5444..dc5e0e3341 100644 --- a/lib/api/src/sys/env.rs +++ b/lib/api/src/sys/env.rs @@ -74,7 +74,7 @@ impl From for HostEnvInitError { /// /// impl WasmerEnv for MyEnv { /// fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { -/// let memory: Memory = instance.exports.get_with_generics_weak("memory").unwrap(); +/// let memory: Memory = instance.get_with_generics_weak("memory").unwrap(); /// self.memory.initialize(memory.clone()); /// Ok(()) /// } diff --git a/lib/api/src/sys/exports.rs b/lib/api/src/sys/exports.rs index bee9c7bd4d..79f69cd859 100644 --- a/lib/api/src/sys/exports.rs +++ b/lib/api/src/sys/exports.rs @@ -3,12 +3,11 @@ use crate::sys::import_object::LikeNamespace; use crate::sys::native::NativeFunc; use crate::sys::WasmTypeList; use indexmap::IndexMap; -use loupe::MemoryUsage; use std::fmt; use std::iter::{ExactSizeIterator, FromIterator}; use std::sync::Arc; use thiserror::Error; -use wasmer_engine::Export; +use wasmer_vm::Export; /// The `ExportError` can happen when trying to get a specific /// export [`Extern`] from the [`Instance`] exports. @@ -30,8 +29,8 @@ use wasmer_engine::Export; /// # let import_object = imports! {}; /// # let instance = Instance::new(&module, &import_object).unwrap(); /// # -/// // This results with an error: `ExportError::IncompatibleType`. -/// let export = instance.exports.get_function("glob").unwrap(); +/// // This results in an error. +/// let export = instance.lookup_function("glob").unwrap(); /// ``` /// /// ## Missing export @@ -45,7 +44,7 @@ use wasmer_engine::Export; /// # let instance = Instance::new(&module, &import_object).unwrap(); /// # /// // This results with an error: `ExportError::Missing`. -/// let export = instance.exports.get_function("unknown").unwrap(); +/// let export = instance.lookup("unknown").unwrap(); /// ``` #[derive(Error, Debug)] pub enum ExportError { @@ -62,7 +61,7 @@ pub enum ExportError { /// the types of instances. /// /// TODO: add examples of using exports -#[derive(Clone, Default, MemoryUsage)] +#[derive(Clone, Default)] pub struct Exports { map: Arc>, } @@ -112,30 +111,30 @@ impl Exports { /// /// If you want to get an export dynamically handling manually /// type checking manually, please use `get_extern`. - pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> { + pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result { match self.map.get(name) { None => Err(ExportError::Missing(name.to_string())), - Some(extern_) => T::get_self_from_extern(extern_), + Some(extern_) => T::get_self_from_extern(extern_.clone()), } } /// Get an export as a `Global`. - pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> { + pub fn get_global(&self, name: &str) -> Result { self.get(name) } /// Get an export as a `Memory`. - pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> { + pub fn get_memory(&self, name: &str) -> Result { self.get(name) } /// Get an export as a `Table`. - pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> { + pub fn get_table(&self, name: &str) -> Result { self.get(name) } /// Get an export as a `Func`. - pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> { + pub fn get_function(&self, name: &str) -> Result { self.get(name) } @@ -162,7 +161,7 @@ impl Exports { { match self.map.get(name) { None => Err(ExportError::Missing(name.to_string())), - Some(extern_) => T::get_self_from_extern_with_generics(extern_), + Some(extern_) => T::get_self_from_extern_with_generics(extern_.clone()), } } @@ -310,7 +309,7 @@ pub trait Exportable<'a>: Sized { /// from an [`Instance`] by name. /// /// [`Instance`]: crate::Instance - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>; + fn get_self_from_extern(_extern: Extern) -> Result; /// Convert the extern internally to hold a weak reference to the `InstanceRef`. /// This is useful for preventing cycles, for example for data stored in a @@ -323,7 +322,7 @@ pub trait Exportable<'a>: Sized { /// as well. pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized { /// Get an export with the given generics. - fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result; + fn get_self_from_extern_with_generics(_extern: Extern) -> Result; /// Convert the extern internally to hold a weak reference to the `InstanceRef`. /// This is useful for preventing cycles, for example for data stored in a /// type implementing `WasmerEnv`. @@ -333,7 +332,7 @@ pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Si /// We implement it for all concrete [`Exportable`] types (that are `Clone`) /// with empty `Args` and `Rets`. impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T { - fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result { + fn get_self_from_extern_with_generics(_extern: Extern) -> Result { T::get_self_from_extern(_extern).map(|i| i.clone()) } diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 3146c82385..be750369eb 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -8,16 +8,15 @@ use crate::sys::RuntimeError; use crate::sys::WasmerEnv; pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; -use loupe::MemoryUsage; use std::cmp::max; use std::ffi::c_void; use std::fmt; use std::sync::Arc; -use wasmer_engine::{Export, ExportFunction, ExportFunctionMetadata}; use wasmer_vm::{ - raise_user_trap, resume_panic, wasmer_call_trampoline, ImportInitializerFuncPtr, - VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody, - VMFunctionEnvironment, VMFunctionKind, VMTrampoline, + raise_user_trap, resume_panic, wasmer_call_trampoline, Export, ExportFunction, + ExportFunctionMetadata, ImportInitializerFuncPtr, VMCallerCheckedAnyfunc, + VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment, + VMFunctionKind, VMTrampoline, }; /// A WebAssembly `function` instance. @@ -37,7 +36,7 @@ use wasmer_vm::{ /// with native functions. Attempting to create a native `Function` with one will /// result in a panic. /// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840) -#[derive(PartialEq, MemoryUsage)] +#[derive(PartialEq)] pub struct Function { pub(crate) store: Store, pub(crate) exported: ExportFunction, @@ -229,6 +228,10 @@ impl Function { // generated dynamic trampoline. let address = std::ptr::null() as *const VMFunctionBody; let vmctx = VMFunctionEnvironment { host_env }; + let signature = store + .engine() + // TODO(0-copy): + .register_signature((&ty).into()); Self { store: store.clone(), @@ -238,7 +241,7 @@ impl Function { address, kind: VMFunctionKind::Dynamic, vmctx, - signature: ty, + signature, call_trampoline: None, instance_ref: None, }, @@ -278,7 +281,10 @@ impl Function { let vmctx = VMFunctionEnvironment { host_env: std::ptr::null_mut() as *mut _, }; - let signature = function.ty(); + let signature = store + .engine() + // TODO(0-copy): + .register_signature((&function.ty()).into()); Self { store: store.clone(), @@ -338,8 +344,7 @@ impl Function { build_export_function_metadata::(env, Env::init_with_instance); let vmctx = VMFunctionEnvironment { host_env }; - let signature = function.ty(); - + let signature = store.engine().register_signature((&function.ty()).into()); Self { store: store.clone(), exported: ExportFunction { @@ -373,8 +378,11 @@ impl Function { /// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]); /// assert_eq!(f.ty().results(), vec![Type::I32]); /// ``` - pub fn ty(&self) -> &FunctionType { - &self.exported.vm_function.signature + pub fn ty(&self) -> FunctionType { + self.store + .engine() + .lookup_signature(self.exported.vm_function.signature) + .expect("Could not resolve VMSharedFunctionIndex! Mixing engines?") } /// Returns the [`Store`] where the `Function` belongs. @@ -431,7 +439,6 @@ impl Function { // Call the trampoline. if let Err(error) = unsafe { wasmer_call_trampoline( - &self.store, self.exported.vm_function.vmctx, trampoline, self.exported.vm_function.address, @@ -517,7 +524,7 @@ impl Function { /// # let import_object = imports! {}; /// # let instance = Instance::new(&module, &import_object).unwrap(); /// # - /// let sum = instance.exports.get_function("sum").unwrap(); + /// let sum = instance.lookup_function("sum").unwrap(); /// /// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]); /// ``` @@ -553,10 +560,9 @@ impl Function { pub(crate) fn vm_funcref(&self) -> VMFuncRef { let engine = self.store.engine(); - let vmsignature = engine.register_signature(&self.exported.vm_function.signature); engine.register_function_metadata(VMCallerCheckedAnyfunc { func_ptr: self.exported.vm_function.address, - type_index: vmsignature, + type_index: self.exported.vm_function.signature, vmctx: self.exported.vm_function.vmctx, }) } @@ -581,7 +587,7 @@ impl Function { /// # let import_object = imports! {}; /// # let instance = Instance::new(&module, &import_object).unwrap(); /// # - /// let sum = instance.exports.get_function("sum").unwrap(); + /// let sum = instance.lookup_function("sum").unwrap(); /// let sum_native = sum.native::<(i32, i32), i32>().unwrap(); /// /// assert_eq!(sum_native.call(1, 2).unwrap(), 3); @@ -607,7 +613,7 @@ impl Function { /// # let import_object = imports! {}; /// # let instance = Instance::new(&module, &import_object).unwrap(); /// # - /// let sum = instance.exports.get_function("sum").unwrap(); + /// let sum = instance.lookup_function("sum").unwrap(); /// /// // This results in an error: `RuntimeError` /// let sum_native = sum.native::<(i64, i64), i32>().unwrap(); @@ -631,7 +637,7 @@ impl Function { /// # let import_object = imports! {}; /// # let instance = Instance::new(&module, &import_object).unwrap(); /// # - /// let sum = instance.exports.get_function("sum").unwrap(); + /// let sum = instance.lookup_function("sum").unwrap(); /// /// // This results in an error: `RuntimeError` /// let sum_native = sum.native::<(i32, i32), i64>().unwrap(); @@ -641,32 +647,27 @@ impl Function { Args: WasmTypeList, Rets: WasmTypeList, { + let engine = self.store().engine(); + let signature = engine + .lookup_signature(self.exported.vm_function.signature) + .expect("Could not resolve VMSharedSignatureIndex! Wrong engine?"); // type check - { - let expected = self.exported.vm_function.signature.params(); - let given = Args::wasm_types(); - - if expected != given { - return Err(RuntimeError::new(format!( - "given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)", - given, - expected, - ))); - } + let expected = signature.params(); + let given = Args::wasm_types(); + if expected != given { + return Err(RuntimeError::new(format!( + "types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)", + given, expected, + ))); } - - { - let expected = self.exported.vm_function.signature.results(); - let given = Rets::wasm_types(); - - if expected != given { - // todo: error result types don't match - return Err(RuntimeError::new(format!( - "given types (`{:?}`) for the function results don't match the actual types (`{:?}`)", - given, - expected, - ))); - } + let expected = signature.results(); + let given = Rets::wasm_types(); + if expected != given { + // todo: error result types don't match + return Err(RuntimeError::new(format!( + "types (`{:?}`) for the function results don't match the actual types (`{:?}`)", + given, expected, + ))); } Ok(NativeFunc::new(self.store.clone(), self.exported.clone())) @@ -695,7 +696,7 @@ impl<'a> Exportable<'a> for Function { self.exported.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern(_extern: Extern) -> Result { match _extern { Extern::Function(func) => Ok(func), _ => Err(ExportError::IncompatibleType), diff --git a/lib/api/src/sys/externals/global.rs b/lib/api/src/sys/externals/global.rs index 776153af5c..2128361236 100644 --- a/lib/api/src/sys/externals/global.rs +++ b/lib/api/src/sys/externals/global.rs @@ -5,11 +5,9 @@ use crate::sys::types::Val; use crate::sys::GlobalType; use crate::sys::Mutability; use crate::sys::RuntimeError; -use loupe::MemoryUsage; use std::fmt; use std::sync::Arc; -use wasmer_engine::Export; -use wasmer_vm::{Global as RuntimeGlobal, VMGlobal}; +use wasmer_vm::{Export, Global as RuntimeGlobal, VMGlobal}; /// A WebAssembly `global` instance. /// @@ -17,7 +15,6 @@ use wasmer_vm::{Global as RuntimeGlobal, VMGlobal}; /// It consists of an individual value and a flag indicating whether it is mutable. /// /// Spec: -#[derive(MemoryUsage)] pub struct Global { store: Store, vm_global: VMGlobal, @@ -249,7 +246,7 @@ impl<'a> Exportable<'a> for Global { self.vm_global.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern(_extern: Extern) -> Result { match _extern { Extern::Global(global) => Ok(global), _ => Err(ExportError::IncompatibleType), diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 2403c212fe..270addb97b 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -2,13 +2,11 @@ use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; use crate::sys::store::Store; use crate::sys::{MemoryType, MemoryView}; -use loupe::MemoryUsage; use std::convert::TryInto; use std::slice; use std::sync::Arc; -use wasmer_engine::Export; use wasmer_types::{Pages, ValueType}; -use wasmer_vm::{MemoryError, VMMemory}; +use wasmer_vm::{Export, MemoryError, VMMemory}; /// A WebAssembly `memory` instance. /// @@ -24,7 +22,7 @@ use wasmer_vm::{MemoryError, VMMemory}; /// mutable from both host and WebAssembly. /// /// Spec: -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] pub struct Memory { store: Store, vm_memory: VMMemory, @@ -287,7 +285,7 @@ impl<'a> Exportable<'a> for Memory { self.vm_memory.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern(_extern: Extern) -> Result { match _extern { Extern::Memory(memory) => Ok(memory), _ => Err(ExportError::IncompatibleType), diff --git a/lib/api/src/sys/externals/mod.rs b/lib/api/src/sys/externals/mod.rs index 029e4e4206..e43919cb19 100644 --- a/lib/api/src/sys/externals/mod.rs +++ b/lib/api/src/sys/externals/mod.rs @@ -14,15 +14,14 @@ pub use self::table::Table; use crate::sys::exports::{ExportError, Exportable}; use crate::sys::store::{Store, StoreObject}; use crate::sys::ExternType; -use loupe::MemoryUsage; use std::fmt; -use wasmer_engine::Export; +use wasmer_vm::Export; /// An `Extern` is the runtime representation of an entity that /// can be imported or exported. /// /// Spec: -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub enum Extern { /// A external [`Function`]. Function(Function), @@ -66,7 +65,7 @@ impl<'a> Exportable<'a> for Extern { } } - fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> { + fn get_self_from_extern(_extern: Self) -> Result { // Since this is already an extern, we can just return it. Ok(_extern) } diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs index fd981ac4be..cb7d4753b5 100644 --- a/lib/api/src/sys/externals/table.rs +++ b/lib/api/src/sys/externals/table.rs @@ -4,10 +4,8 @@ use crate::sys::store::Store; use crate::sys::types::{Val, ValFuncRef}; use crate::sys::RuntimeError; use crate::sys::TableType; -use loupe::MemoryUsage; use std::sync::Arc; -use wasmer_engine::Export; -use wasmer_vm::{Table as RuntimeTable, TableElement, VMTable}; +use wasmer_vm::{Export, Table as RuntimeTable, TableElement, VMTable}; /// A WebAssembly `table` instance. /// @@ -18,7 +16,6 @@ use wasmer_vm::{Table as RuntimeTable, TableElement, VMTable}; /// mutable from both host and WebAssembly. /// /// Spec: -#[derive(MemoryUsage)] pub struct Table { store: Store, vm_table: VMTable, @@ -177,7 +174,7 @@ impl<'a> Exportable<'a> for Table { self.vm_table.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern(_extern: Extern) -> Result { match _extern { Extern::Table(table) => Ok(table), _ => Err(ExportError::IncompatibleType), diff --git a/lib/api/src/sys/import_object.rs b/lib/api/src/sys/import_object.rs index d98f220809..2568b01a29 100644 --- a/lib/api/src/sys/import_object.rs +++ b/lib/api/src/sys/import_object.rs @@ -7,7 +7,7 @@ use std::collections::VecDeque; use std::collections::{hash_map::Entry, HashMap}; use std::fmt; use std::sync::{Arc, Mutex}; -use wasmer_engine::{Export, NamedResolver}; +use wasmer_vm::{Export, NamedResolver}; /// The `LikeNamespace` trait represents objects that act as a namespace for imports. /// For example, an `Instance` or `Namespace` could be @@ -264,8 +264,8 @@ macro_rules! import_namespace { mod test { use super::*; use crate::sys::{Global, Store, Val}; - use wasmer_engine::ChainableNamedResolver; use wasmer_types::Type; + use wasmer_vm::ChainableNamedResolver; #[test] fn chaining_works() { diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index 3c5871e34a..33393f353a 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -1,15 +1,14 @@ -use crate::sys::exports::Exports; -use crate::sys::externals::Extern; use crate::sys::module::Module; use crate::sys::store::Store; use crate::sys::{HostEnvInitError, LinkError, RuntimeError}; -use loupe::MemoryUsage; +use crate::{ExportError, NativeFunc, WasmTypeList}; use std::fmt; use std::sync::{Arc, Mutex}; use thiserror::Error; -use wasmer_engine::Resolver; -use wasmer_types::{InstanceConfig, NamedFunction}; -use wasmer_vm::{InstanceHandle, VMContext}; +use wasmer_types::InstanceConfig; +use wasmer_vm::{InstanceHandle, Resolver, VMContext}; + +use super::exports::ExportableWithGenerics; /// A WebAssembly Instance is a stateful, executable /// instance of a WebAssembly [`Module`]. @@ -19,12 +18,10 @@ use wasmer_vm::{InstanceHandle, VMContext}; /// interacting with WebAssembly. /// /// Spec: -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub struct Instance { handle: Arc>, module: Module, - /// The exports for an instance. - pub exports: Exports, } #[cfg(test)] @@ -55,8 +52,12 @@ pub enum InstantiationError { #[error(transparent)] Link(LinkError), + /// Could not instantiate the artifact. + #[error("could not instantiate the artifact: {0}")] + Instantiation(Box), + /// A runtime error occured while invoking the start function - #[error(transparent)] + #[error("could not invoke the start function: {0}")] Start(RuntimeError), /// The module was compiled with a CPU feature that is not available on @@ -75,6 +76,9 @@ impl From for InstantiationError { wasmer_engine::InstantiationError::Link(e) => Self::Link(e), wasmer_engine::InstantiationError::Start(e) => Self::Start(e), wasmer_engine::InstantiationError::CpuFeature(e) => Self::CpuFeature(e), + wasmer_engine::InstantiationError::CreateInstance(e) => { + Self::Instantiation(Box::new(e).into()) + } } } } @@ -119,10 +123,7 @@ impl Instance { /// Those are, as defined by the spec: /// * Link errors that happen when plugging the imports into the instance /// * Runtime errors that happen when running the module `start` function. - pub fn new( - module: &Module, - resolver: &(dyn Resolver + Send + Sync), - ) -> Result { + pub fn new(module: &Module, resolver: &dyn Resolver) -> Result { Instance::new_with_config(module, InstanceConfig::default(), resolver) } @@ -140,22 +141,10 @@ impl Instance { )); } } - let store = module.store(); let handle = module.instantiate(resolver, config)?; - let exports = module - .exports() - .map(|export| { - let name = export.name().to_string(); - let export = handle.lookup(&name).expect("export"); - let extern_ = Extern::from_vm_export(store, export.into()); - (name, extern_) - }) - .collect::(); - let instance = Self { handle: Arc::new(Mutex::new(handle)), module: module.clone(), - exports, }; // # Safety @@ -167,11 +156,10 @@ impl Instance { // correct error type returned by `WasmerEnv::init_with_instance` as a generic // parameter. unsafe { - instance - .handle - .lock() - .unwrap() - .initialize_host_envs::(&instance as *const _ as *const _)?; + wasmer_vm::initialize_host_envs::( + &*instance.handle, + &instance as *const _ as *const _, + )?; } Ok(instance) @@ -187,9 +175,64 @@ impl Instance { self.module.store() } - /// Returns list of named functions in instance. - pub fn named_functions(&self) -> Vec { - self.handle.lock().unwrap().named_functions() + /// Lookup an exported entity by its name. + pub fn lookup(&self, field: &str) -> Option { + let vmextern = self.handle.lock().unwrap().lookup(field)?; + Some(vmextern.into()) + } + + /// Lookup an exported function by its name. + pub fn lookup_function(&self, field: &str) -> Option { + if let crate::Export::Function(f) = self.lookup(field)? { + Some(crate::Function::from_vm_export(self.store(), f)) + } else { + None + } + } + + /// Get an export as a `NativeFunc`. + pub fn get_native_function( + &self, + name: &str, + ) -> Result, ExportError> + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + match self.lookup(name) { + Some(crate::Export::Function(f)) => crate::Function::from_vm_export(self.store(), f) + .native() + .map_err(|_| ExportError::IncompatibleType), + Some(_) => Err(ExportError::IncompatibleType), + None => Err(ExportError::Missing("not found".into())), + } + } + + /// Hack to get this working with nativefunc too + pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result + where + Args: WasmTypeList, + Rets: WasmTypeList, + T: ExportableWithGenerics<'a, Args, Rets>, + { + let export = self + .lookup(name) + .ok_or_else(|| ExportError::Missing(name.to_string()))?; + let ext = crate::Extern::from_vm_export(self.store(), export); + T::get_self_from_extern_with_generics(ext) + } + + /// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally. + /// This is useful for passing data into `WasmerEnv`, for example. + pub fn get_with_generics_weak<'a, T, Args, Rets>(&'a self, name: &str) -> Result + where + Args: WasmTypeList, + Rets: WasmTypeList, + T: ExportableWithGenerics<'a, Args, Rets>, + { + let mut out: T = self.get_with_generics(name)?; + out.into_weak_instance_ref(); + Ok(out) } #[doc(hidden)] @@ -200,8 +243,6 @@ impl Instance { impl fmt::Debug for Instance { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Instance") - .field("exports", &self.exports) - .finish() + f.debug_struct("Instance").finish() } } diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 157a26a4a6..a82ac46505 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -41,8 +41,8 @@ pub use crate::sys::ptr::{Array, Item, WasmPtr}; pub use crate::sys::store::{Store, StoreObject}; pub use crate::sys::tunables::BaseTunables; pub use crate::sys::types::{ - ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, - TableType, Val, ValType, + ExportType, ExternType, FunctionType, GlobalType, MemoryType, Mutability, TableType, Val, + ValType, }; pub use crate::sys::types::{Val as Value, ValType as Type}; pub use crate::sys::utils::is_wasm; @@ -55,16 +55,16 @@ pub use wasmer_compiler::{ pub use wasmer_compiler::{ CompileError, CpuFeature, Features, ParseCpuFeatureError, Target, WasmError, WasmResult, }; -pub use wasmer_engine::{ - ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver, - NamedResolverChain, Resolver, RuntimeError, SerializeError, Tunables, -}; +pub use wasmer_engine::{DeserializeError, Engine, FrameInfo, LinkError, RuntimeError}; #[cfg(feature = "experimental-reference-types-extern-ref")] pub use wasmer_types::ExternRef; pub use wasmer_types::{ Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; +pub use wasmer_vm::{ + ChainableNamedResolver, Export, NamedResolver, NamedResolverChain, Resolver, Tunables, +}; // TODO: should those be moved into wasmer::vm as well? pub use wasmer_vm::{raise_user_trap, MemoryError}; diff --git a/lib/api/src/sys/module.rs b/lib/api/src/sys/module.rs index 30859d7340..3142044c8e 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/sys/module.rs @@ -1,7 +1,5 @@ use crate::sys::store::Store; -use crate::sys::types::{ExportType, ImportType}; use crate::sys::InstantiationError; -use loupe::MemoryUsage; use std::fmt; use std::io; use std::path::Path; @@ -10,9 +8,9 @@ use thiserror::Error; use wasmer_compiler::CompileError; #[cfg(feature = "wat")] use wasmer_compiler::WasmError; -use wasmer_engine::{is_wasm_pc, Artifact, DeserializeError, Resolver, SerializeError}; -use wasmer_types::{ExportsIterator, ImportsIterator, InstanceConfig, ModuleInfo}; -use wasmer_vm::{init_traps, InstanceHandle}; +use wasmer_engine::RuntimeError; +use wasmer_types::InstanceConfig; +use wasmer_vm::{Artifact, InstanceHandle, Resolver}; #[derive(Error, Debug)] pub enum IoCompileError { @@ -32,7 +30,7 @@ pub enum IoCompileError { /// /// Cloning a module is cheap: it does a shallow copy of the compiled /// contents rather than a deep copy. -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub struct Module { store: Store, artifact: Arc, @@ -114,13 +112,10 @@ impl Module { /// Creates a new WebAssembly module from a file path. pub fn from_file(store: &Store, file: impl AsRef) -> Result { let file_ref = file.as_ref(); - let canonical = file_ref.canonicalize()?; let wasm_bytes = std::fs::read(file_ref)?; - let mut module = Self::new(store, &wasm_bytes)?; + let module = Self::new(store, &wasm_bytes)?; // Set the module name to the absolute path of the filename. // This is useful for debugging the stack traces. - let filename = canonical.as_path().to_str().unwrap(); - module.set_name(filename); Ok(module) } @@ -160,103 +155,13 @@ impl Module { } fn compile(store: &Store, binary: &[u8]) -> Result { - let artifact = store.engine().compile(binary, store.tunables())?; + let executable = store.engine().compile(binary, store.tunables())?; + let artifact = store.engine().load(&*executable)?; Ok(Self::from_artifact(store, artifact)) } - /// Serializes a module into a binary representation that the `Engine` - /// can later process via [`Module::deserialize`]. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; - /// let serialized = module.serialize()?; - /// # Ok(()) - /// # } - /// ``` - pub fn serialize(&self) -> Result, SerializeError> { - self.artifact.serialize() - } - - /// Serializes a module into a file that the `Engine` - /// can later process via [`Module::deserialize_from_file`]. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; - /// module.serialize_to_file("path/to/foo.so")?; - /// # Ok(()) - /// # } - /// ``` - pub fn serialize_to_file(&self, path: impl AsRef) -> Result<(), SerializeError> { - self.artifact.serialize_to_file(path.as_ref()) - } - - /// Deserializes a serialized Module binary into a `Module`. - /// > Note: the module has to be serialized before with the `serialize` method. - /// - /// # Safety - /// - /// This function is inherently **unsafe** as the provided bytes: - /// 1. Are going to be deserialized directly into Rust objects. - /// 2. Contains the function assembly bodies and, if intercepted, - /// a malicious actor could inject code into executable - /// memory. - /// - /// And as such, the `deserialize` method is unsafe. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::deserialize(&store, serialized_data)?; - /// # Ok(()) - /// # } - /// ``` - pub unsafe fn deserialize(store: &Store, bytes: &[u8]) -> Result { - let artifact = store.engine().deserialize(bytes)?; - Ok(Self::from_artifact(store, artifact)) - } - - /// Deserializes a a serialized Module located in a `Path` into a `Module`. - /// > Note: the module has to be serialized before with the `serialize` method. - /// - /// # Safety - /// - /// Please check [`Module::deserialize`]. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # let store = Store::default(); - /// # fn main() -> anyhow::Result<()> { - /// let module = Module::deserialize_from_file(&store, path)?; - /// # Ok(()) - /// # } - /// ``` - pub unsafe fn deserialize_from_file( - store: &Store, - path: impl AsRef, - ) -> Result { - let artifact = store.engine().deserialize_from_file(path.as_ref())?; - Ok(Self::from_artifact(store, artifact)) - } - - fn from_artifact(store: &Store, artifact: Arc) -> Self { - if !artifact.features().signal_less { - init_traps(is_wasm_pc); - } + /// Make a Module from Artifact... + pub fn from_artifact(store: &Store, artifact: Arc) -> Self { Self { store: store.clone(), artifact, @@ -269,173 +174,36 @@ impl Module { config: InstanceConfig, ) -> Result { unsafe { - let instance_handle = self.artifact.instantiate( - self.store.tunables(), - resolver, - Box::new((self.store.clone(), self.artifact.clone())), - config, - )?; + let instance_handle = Arc::clone(&self.artifact) + .instantiate( + self.store.tunables(), + resolver, + Box::new((self.store.clone(), Arc::clone(&self.artifact))), + config, + ) + .map_err(InstantiationError::Instantiation)?; // After the instance handle is created, we need to initialize // the data, call the start function and so. However, if any // of this steps traps, we still need to keep the instance alive // as some of the Instance elements may have placed in other // instance tables. - self.artifact - .finish_instantiation(&self.store, &instance_handle)?; + instance_handle + .finish_instantiation() + .map_err(|t| InstantiationError::Start(RuntimeError::from_trap(t)))?; Ok(instance_handle) } } - /// Returns the name of the current module. - /// - /// This name is normally set in the WebAssembly bytecode by some - /// compilers, but can be also overwritten using the [`Module::set_name`] method. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let wat = "(module $moduleName)"; - /// let module = Module::new(&store, wat)?; - /// assert_eq!(module.name(), Some("moduleName")); - /// # Ok(()) - /// # } - /// ``` - pub fn name(&self) -> Option<&str> { - self.artifact.module_ref().name.as_deref() - } - - /// Sets the name of the current module. - /// This is normally useful for stacktraces and debugging. - /// - /// It will return `true` if the module name was changed successfully, - /// and return `false` otherwise (in case the module is already - /// instantiated). - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let wat = "(module)"; - /// let mut module = Module::new(&store, wat)?; - /// assert_eq!(module.name(), None); - /// module.set_name("foo"); - /// assert_eq!(module.name(), Some("foo")); - /// # Ok(()) - /// # } - /// ``` - pub fn set_name(&mut self, name: &str) -> bool { - Arc::get_mut(&mut self.artifact) - .and_then(|artifact| artifact.module_mut()) - .map(|mut module_info| { - module_info.name = Some(name.to_string()); - true - }) - .unwrap_or(false) - } - - /// Returns an iterator over the imported types in the Module. - /// - /// The order of the imports is guaranteed to be the same as in the - /// WebAssembly bytecode. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let wat = r#"(module - /// (import "host" "func1" (func)) - /// (import "host" "func2" (func)) - /// )"#; - /// let module = Module::new(&store, wat)?; - /// for import in module.imports() { - /// assert_eq!(import.module(), "host"); - /// assert!(import.name().contains("func")); - /// import.ty(); - /// } - /// # Ok(()) - /// # } - /// ``` - pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { - self.artifact.module_ref().imports() - } - - /// Returns an iterator over the exported types in the Module. - /// - /// The order of the exports is guaranteed to be the same as in the - /// WebAssembly bytecode. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let wat = r#"(module - /// (func (export "namedfunc")) - /// (memory (export "namedmemory") 1) - /// )"#; - /// let module = Module::new(&store, wat)?; - /// for export_ in module.exports() { - /// assert!(export_.name().contains("named")); - /// export_.ty(); - /// } - /// # Ok(()) - /// # } - /// ``` - pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { - self.artifact.module_ref().exports() - } - - /// 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 { - self.artifact.module_ref().custom_sections(name) - } - /// Returns the [`Store`] where the `Instance` belongs. pub fn store(&self) -> &Store { &self.store } - - /// The ABI of the ModuleInfo is very unstable, we refactor it very often. - /// This function is public because in some cases it can be useful to get some - /// extra information from the module. - /// - /// However, the usage is highly discouraged. - #[doc(hidden)] - pub fn info(&self) -> &ModuleInfo { - &self.artifact.module_ref() - } - - /// Gets the [`Artifact`] used internally by the Module. - /// - /// This API is hidden because it's not necessarily stable; - /// this functionality is required for some core functionality though, like - /// the object file engine. - #[doc(hidden)] - pub fn artifact(&self) -> &Arc { - &self.artifact - } } impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Module") - .field("name", &self.name()) - .finish() + f.debug_struct("Module").finish() } } diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index 7c3f8705be..baac369e9c 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -12,9 +12,10 @@ use std::marker::PhantomData; use crate::sys::externals::function::{DynamicFunction, VMDynamicFunction}; use crate::sys::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList}; use std::panic::{catch_unwind, AssertUnwindSafe}; -use wasmer_engine::ExportFunction; use wasmer_types::NativeWasmType; -use wasmer_vm::{VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind}; +use wasmer_vm::{ + ExportFunction, VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, +}; /// A WebAssembly function that can be called natively /// (using the Native ABI). @@ -155,7 +156,6 @@ macro_rules! impl_native_traits { }; unsafe { wasmer_vm::wasmer_call_trampoline( - &self.store, self.vmctx(), trampoline, self.address(), @@ -228,7 +228,7 @@ macro_rules! impl_native_traits { $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, { - fn get_self_from_extern_with_generics(_extern: &crate::sys::externals::Extern) -> Result { + fn get_self_from_extern_with_generics(_extern: crate::sys::externals::Extern) -> Result { use crate::sys::exports::Exportable; crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::sys::exports::ExportError::IncompatibleType) } diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index 8f611d4f32..ccc09a266d 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -1,12 +1,10 @@ use crate::sys::tunables::BaseTunables; -use loupe::MemoryUsage; -use std::any::Any; use std::fmt; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; #[cfg(all(feature = "compiler", feature = "engine"))] use wasmer_compiler::CompilerConfig; -use wasmer_engine::{is_wasm_pc, Engine, Tunables}; -use wasmer_vm::{init_traps, TrapHandler, TrapHandlerFn}; +use wasmer_engine::Engine; +use wasmer_vm::Tunables; /// The store represents all global state that can be manipulated by /// WebAssembly programs. It consists of the runtime representation @@ -18,12 +16,10 @@ use wasmer_vm::{init_traps, TrapHandler, TrapHandlerFn}; /// [`Tunables`] (that are used to create the memories, tables and globals). /// /// Spec: -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub struct Store { engine: Arc, tunables: Arc, - #[loupe(skip)] - trap_handler: Arc>>>, } impl Store { @@ -35,26 +31,14 @@ impl Store { Self::new_with_tunables(engine, BaseTunables::for_target(engine.target())) } - /// Set the trap handler in this store. - pub fn set_trap_handler(&self, handler: Option>) { - let mut m = self.trap_handler.write().unwrap(); - *m = handler; - } - /// Creates a new `Store` with a specific [`Engine`] and [`Tunables`]. pub fn new_with_tunables(engine: &E, tunables: impl Tunables + Send + Sync + 'static) -> Self where E: Engine + ?Sized, { - // Make sure the signal handlers are installed. - // This is required for handling traps. - if engine.use_signals() { - init_traps(is_wasm_pc); - } Self { engine: engine.cloned(), tunables: Arc::new(tunables), - trap_handler: Arc::new(RwLock::new(None)), } } @@ -82,23 +66,6 @@ impl PartialEq for Store { } } -unsafe impl TrapHandler for Store { - #[inline] - fn as_any(&self) -> &dyn Any { - self - } - - fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool { - if let Some(handler) = *&self.trap_handler.read().unwrap().as_ref() { - call(handler) - } else { - false - } - } -} - -// This is required to be able to set the trap_handler in the -// Store. unsafe impl Send for Store {} unsafe impl Sync for Store {} diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 4bdb657240..58d9137ee9 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -1,14 +1,12 @@ use crate::sys::{MemoryType, Pages, TableType}; -use loupe::MemoryUsage; use std::ptr::NonNull; use std::sync::Arc; use target_lexicon::PointerWidth; use wasmer_compiler::Target; -use wasmer_engine::Tunables; use wasmer_vm::MemoryError; use wasmer_vm::{ - LinearMemory, LinearTable, Memory, MemoryStyle, Table, TableStyle, VMMemoryDefinition, - VMTableDefinition, + LinearMemory, LinearTable, Memory, MemoryStyle, Table, TableStyle, Tunables, + VMMemoryDefinition, VMTableDefinition, }; /// Tunable parameters for WebAssembly compilation. @@ -19,7 +17,7 @@ use wasmer_vm::{ /// implementation or use composition to wrap your Tunables around /// this one. The later approach is demonstrated in the /// tunables-limit-memory example. -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub struct BaseTunables { /// For static heaps, the size in wasm pages of the heap protected by bounds checking. pub static_memory_bound: Pages, diff --git a/lib/api/src/sys/types.rs b/lib/api/src/sys/types.rs index ef291e9889..57549a7879 100644 --- a/lib/api/src/sys/types.rs +++ b/lib/api/src/sys/types.rs @@ -3,8 +3,8 @@ use crate::sys::store::{Store, StoreObject}; use crate::sys::RuntimeError; use wasmer_types::Value; pub use wasmer_types::{ - ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, - TableType, Type as ValType, + ExportType, ExternType, FunctionType, GlobalType, MemoryType, Mutability, TableType, + Type as ValType, }; use wasmer_vm::VMFuncRef; @@ -66,17 +66,13 @@ impl ValFuncRef for Val { let anyfunc: *const wasmer_vm::VMCallerCheckedAnyfunc = *func_ref; &*anyfunc }; - let signature = store - .engine() - .lookup_signature(item.type_index) - .expect("Signature not found in store"); - let export = wasmer_engine::ExportFunction { + let export = wasmer_vm::ExportFunction { // TODO: // figure out if we ever need a value here: need testing with complicated import patterns metadata: None, vm_function: wasmer_vm::VMFunction { address: item.func_ptr, - signature, + signature: item.type_index, // TODO: review this comment (unclear if it's still correct): // All functions in tables are already Static (as dynamic functions // are converted to use the trampolines with static signatures). diff --git a/lib/api/tests/sys_export.rs b/lib/api/tests/sys_export.rs index 9975a8580e..fd950fe56e 100644 --- a/lib/api/tests/sys_export.rs +++ b/lib/api/tests/sys_export.rs @@ -2,7 +2,7 @@ mod sys { use anyhow::Result; use wasmer::*; - use wasmer_vm::WeakOrStrongInstanceRef; + use wasmer_vm::{VMGlobal, VMMemory, VMTable, WeakOrStrongInstanceRef}; const MEM_WAT: &str = " (module @@ -45,37 +45,28 @@ mod sys { ) "; - fn is_memory_instance_ref_strong(memory: &Memory) -> Option { + fn is_memory_instance_ref_strong(memory: &VMMemory) -> Option { // This is safe because we're calling it from a test to test the internals - unsafe { - memory - .get_vm_memory() - .instance_ref - .as_ref() - .map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_))) - } + memory + .instance_ref + .as_ref() + .map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_))) } - fn is_table_instance_ref_strong(table: &Table) -> Option { + fn is_table_instance_ref_strong(table: &VMTable) -> Option { // This is safe because we're calling it from a test to test the internals - unsafe { - table - .get_vm_table() - .instance_ref - .as_ref() - .map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_))) - } + table + .instance_ref + .as_ref() + .map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_))) } - fn is_global_instance_ref_strong(global: &Global) -> Option { + fn is_global_instance_ref_strong(global: &VMGlobal) -> Option { // This is safe because we're calling it from a test to test the internals - unsafe { - global - .get_vm_global() - .instance_ref - .as_ref() - .map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_))) - } + global + .instance_ref + .as_ref() + .map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_))) } fn is_function_instance_ref_strong(f: &Function) -> Option { @@ -112,12 +103,21 @@ mod sys { memory: LazyInit, } - let host_fn = |env: &MemEnv| { + let host_fn = |env: &MemEnv| unsafe { let mem = env.memory_ref().unwrap(); - assert_eq!(is_memory_instance_ref_strong(&mem), Some(false)); + assert_eq!( + is_memory_instance_ref_strong(&mem.get_vm_memory()), + Some(false) + ); let mem_clone = mem.clone(); - assert_eq!(is_memory_instance_ref_strong(&mem_clone), Some(true)); - assert_eq!(is_memory_instance_ref_strong(&mem), Some(false)); + assert_eq!( + is_memory_instance_ref_strong(&mem_clone.get_vm_memory()), + Some(true) + ); + assert_eq!( + is_memory_instance_ref_strong(&mem.get_vm_memory()), + Some(false) + ); }; let f: NativeFunc<(), ()> = { @@ -135,11 +135,14 @@ mod sys { )?; { - let mem = instance.exports.get_memory("memory")?; - assert_eq!(is_memory_instance_ref_strong(&mem), Some(true)); + if let Some(Export::Memory(mem)) = instance.lookup("memory") { + assert_eq!(is_memory_instance_ref_strong(&mem), Some(true)); + } else { + panic!("not a memory"); + } } - let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + let f: NativeFunc<(), ()> = instance.get_native_function("call_host_fn").unwrap(); f.call()?; f }; @@ -156,12 +159,21 @@ mod sys { global: LazyInit, } - let host_fn = |env: &GlobalEnv| { + let host_fn = |env: &GlobalEnv| unsafe { let global = env.global_ref().unwrap(); - assert_eq!(is_global_instance_ref_strong(&global), Some(false)); + assert_eq!( + is_global_instance_ref_strong(&global.get_vm_global()), + Some(false) + ); let global_clone = global.clone(); - assert_eq!(is_global_instance_ref_strong(&global_clone), Some(true)); - assert_eq!(is_global_instance_ref_strong(&global), Some(false)); + assert_eq!( + is_global_instance_ref_strong(&global_clone.get_vm_global()), + Some(true) + ); + assert_eq!( + is_global_instance_ref_strong(&global.get_vm_global()), + Some(false) + ); }; let f: NativeFunc<(), ()> = { @@ -179,11 +191,14 @@ mod sys { )?; { - let global = instance.exports.get_global("global")?; - assert_eq!(is_global_instance_ref_strong(&global), Some(true)); + if let Some(Export::Global(global)) = instance.lookup("global") { + assert_eq!(is_global_instance_ref_strong(&global), Some(true)); + } else { + panic!("not a global"); + } } - let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + let f: NativeFunc<(), ()> = instance.get_native_function("call_host_fn").unwrap(); f.call()?; f }; @@ -200,12 +215,21 @@ mod sys { table: LazyInit, } - let host_fn = |env: &TableEnv| { + let host_fn = |env: &TableEnv| unsafe { let table = env.table_ref().unwrap(); - assert_eq!(is_table_instance_ref_strong(&table), Some(false)); + assert_eq!( + is_table_instance_ref_strong(&table.get_vm_table()), + Some(false) + ); let table_clone = table.clone(); - assert_eq!(is_table_instance_ref_strong(&table_clone), Some(true)); - assert_eq!(is_table_instance_ref_strong(&table), Some(false)); + assert_eq!( + is_table_instance_ref_strong(&table_clone.get_vm_table()), + Some(true) + ); + assert_eq!( + is_table_instance_ref_strong(&table.get_vm_table()), + Some(false) + ); }; let f: NativeFunc<(), ()> = { @@ -223,11 +247,14 @@ mod sys { )?; { - let table = instance.exports.get_table("table")?; - assert_eq!(is_table_instance_ref_strong(&table), Some(true)); + if let Some(Export::Table(table)) = instance.lookup("table") { + assert_eq!(is_table_instance_ref_strong(&table), Some(true)); + } else { + panic!("not a table"); + } } - let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + let f: NativeFunc<(), ()> = instance.get_native_function("call_host_fn").unwrap(); f.call()?; f }; @@ -267,11 +294,11 @@ mod sys { )?; { - let function = instance.exports.get_function("call_host_fn")?; + let function = instance.lookup_function("call_host_fn").unwrap(); assert_eq!(is_function_instance_ref_strong(&function), Some(true)); } - let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + let f: NativeFunc<(), ()> = instance.get_native_function("call_host_fn").unwrap(); f.call()?; f }; @@ -321,14 +348,14 @@ mod sys { { let function: NativeFunc<(), ()> = - instance.exports.get_native_function("call_host_fn")?; + instance.get_native_function("call_host_fn").unwrap(); assert_eq!( is_native_function_instance_ref_strong(&function), Some(true) ); } - let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + let f: NativeFunc<(), ()> = instance.get_native_function("call_host_fn").unwrap(); f.call()?; f }; diff --git a/lib/api/tests/sys_externals.rs b/lib/api/tests/sys_externals.rs index 5600239040..6b9bb53bce 100644 --- a/lib/api/tests/sys_externals.rs +++ b/lib/api/tests/sys_externals.rs @@ -397,7 +397,7 @@ mod sys { let f = { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("sum")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("sum").unwrap(); assert_eq!(f.call(4, 5)?, 9); f @@ -424,8 +424,7 @@ mod sys { let f = { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_with_generics_weak("sum")?; - + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("sum").unwrap(); assert_eq!(f.call(4, 5)?, 9); f }; diff --git a/lib/api/tests/sys_instance.rs b/lib/api/tests/sys_instance.rs index b4d8f33fa7..7a94b7dffb 100644 --- a/lib/api/tests/sys_instance.rs +++ b/lib/api/tests/sys_instance.rs @@ -25,7 +25,7 @@ mod sys { let instance3 = instance.clone(); // The function is cloned to “break” the connection with `instance`. - let sum = instance.exports.get_function("sum")?.clone(); + let sum = instance.lookup_function("sum").unwrap().clone(); drop(instance); drop(instance2); diff --git a/lib/api/tests/sys_module.rs b/lib/api/tests/sys_module.rs index 2214b289ec..a34a0f1d11 100644 --- a/lib/api/tests/sys_module.rs +++ b/lib/api/tests/sys_module.rs @@ -3,162 +3,6 @@ mod sys { use anyhow::Result; use wasmer::*; - #[test] - fn module_get_name() -> Result<()> { - let store = Store::default(); - let wat = r#"(module)"#; - let module = Module::new(&store, wat)?; - assert_eq!(module.name(), None); - - Ok(()) - } - - #[test] - fn module_set_name() -> Result<()> { - let store = Store::default(); - let wat = r#"(module $name)"#; - let mut module = Module::new(&store, wat)?; - assert_eq!(module.name(), Some("name")); - - module.set_name("new_name"); - assert_eq!(module.name(), Some("new_name")); - - Ok(()) - } - - #[test] - fn imports() -> Result<()> { - let store = Store::default(); - let wat = r#"(module - (import "host" "func" (func)) - (import "host" "memory" (memory 1)) - (import "host" "table" (table 1 anyfunc)) - (import "host" "global" (global i32)) -)"#; - let module = Module::new(&store, wat)?; - assert_eq!( - module.imports().collect::>(), - vec![ - ImportType::new( - "host", - "func", - ExternType::Function(FunctionType::new(vec![], vec![])) - ), - ImportType::new( - "host", - "memory", - ExternType::Memory(MemoryType::new(Pages(1), None, false)) - ), - ImportType::new( - "host", - "table", - ExternType::Table(TableType::new(Type::FuncRef, 1, None)) - ), - ImportType::new( - "host", - "global", - ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)) - ) - ] - ); - - // Now we test the iterators - assert_eq!( - module.imports().functions().collect::>(), - vec![ImportType::new( - "host", - "func", - FunctionType::new(vec![], vec![]) - ),] - ); - assert_eq!( - module.imports().memories().collect::>(), - vec![ImportType::new( - "host", - "memory", - MemoryType::new(Pages(1), None, false) - ),] - ); - assert_eq!( - module.imports().tables().collect::>(), - vec![ImportType::new( - "host", - "table", - TableType::new(Type::FuncRef, 1, None) - ),] - ); - assert_eq!( - module.imports().globals().collect::>(), - vec![ImportType::new( - "host", - "global", - GlobalType::new(Type::I32, Mutability::Const) - ),] - ); - Ok(()) - } - - #[test] - fn exports() -> Result<()> { - let store = Store::default(); - let wat = r#"(module - (func (export "func") nop) - (memory (export "memory") 1) - (table (export "table") 1 funcref) - (global (export "global") i32 (i32.const 0)) -)"#; - let module = Module::new(&store, wat)?; - assert_eq!( - module.exports().collect::>(), - vec![ - ExportType::new( - "func", - ExternType::Function(FunctionType::new(vec![], vec![])) - ), - ExportType::new( - "memory", - ExternType::Memory(MemoryType::new(Pages(1), None, false)) - ), - ExportType::new( - "table", - ExternType::Table(TableType::new(Type::FuncRef, 1, None)) - ), - ExportType::new( - "global", - ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)) - ) - ] - ); - - // Now we test the iterators - assert_eq!( - module.exports().functions().collect::>(), - vec![ExportType::new("func", FunctionType::new(vec![], vec![])),] - ); - assert_eq!( - module.exports().memories().collect::>(), - vec![ExportType::new( - "memory", - MemoryType::new(Pages(1), None, false) - ),] - ); - assert_eq!( - module.exports().tables().collect::>(), - vec![ExportType::new( - "table", - TableType::new(Type::FuncRef, 1, None) - ),] - ); - assert_eq!( - module.exports().globals().collect::>(), - vec![ExportType::new( - "global", - GlobalType::new(Type::I32, Mutability::Const) - ),] - ); - Ok(()) - } - #[test] fn calling_host_functions_with_negative_values_works() -> Result<()> { let store = Store::default(); @@ -228,14 +72,14 @@ mod sys { }; let instance = Instance::new(&module, &imports)?; - let f1: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func1")?; - let f2: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func2")?; - let f3: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func3")?; - let f4: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func4")?; - let f5: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func5")?; - let f6: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func6")?; - let f7: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func7")?; - let f8: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func8")?; + let f1: NativeFunc<(), ()> = instance.get_native_function("call_host_func1")?; + let f2: NativeFunc<(), ()> = instance.get_native_function("call_host_func2")?; + let f3: NativeFunc<(), ()> = instance.get_native_function("call_host_func3")?; + let f4: NativeFunc<(), ()> = instance.get_native_function("call_host_func4")?; + let f5: NativeFunc<(), ()> = instance.get_native_function("call_host_func5")?; + let f6: NativeFunc<(), ()> = instance.get_native_function("call_host_func6")?; + let f7: NativeFunc<(), ()> = instance.get_native_function("call_host_func7")?; + let f8: NativeFunc<(), ()> = instance.get_native_function("call_host_func8")?; f1.call()?; f2.call()?; diff --git a/lib/api/tests/sys_reference_types.rs b/lib/api/tests/sys_reference_types.rs index f73a835a93..f1cfb1a980 100644 --- a/lib/api/tests/sys_reference_types.rs +++ b/lib/api/tests/sys_reference_types.rs @@ -22,17 +22,20 @@ mod sys { (call_indirect $table (type $ret_i32_ty) (i32.const 0))) )"#; let module = Module::new(&store, wat)?; + let func_ref_identity = Function::new( + &store, + FunctionType::new(vec![Type::FuncRef], vec![Type::FuncRef]), + |values| -> Result, _> { Ok(vec![values[0].clone()]) }, + ); let imports = imports! { "env" => { - "func_ref_identity" => Function::new(&store, FunctionType::new([Type::FuncRef], [Type::FuncRef]), |values| -> Result, _> { - Ok(vec![values[0].clone()]) - }) + "func_ref_identity" => func_ref_identity }, }; let instance = Instance::new(&module, &imports)?; - let f: &Function = instance.exports.get_function("run")?; + let f: Function = instance.lookup_function("run").unwrap(); let results = f.call(&[]).unwrap(); if let Value::FuncRef(fr) = &results[0] { assert!(fr.is_none()); @@ -48,7 +51,7 @@ mod sys { env.0.store(true, Ordering::SeqCst); 343 }); - let call_set_value: &Function = instance.exports.get_function("call_set_value")?; + let call_set_value: Function = instance.lookup_function("call_set_value").unwrap(); let results: Box<[Value]> = call_set_value.call(&[Value::FuncRef(Some(func_to_call))])?; assert!(env.0.load(Ordering::SeqCst)); assert_eq!(&*results, &[Value::I32(343)]); @@ -86,13 +89,14 @@ mod sys { Ok(vec![Value::I32(f.call(7, 9)?)]) } + let func_ref_call = Function::new( + &store, + FunctionType::new(vec![Type::FuncRef], vec![Type::I32]), + func_ref_call, + ); let imports = imports! { "env" => { - "func_ref_call" => Function::new( - &store, - FunctionType::new([Type::FuncRef], [Type::I32]), - func_ref_call - ), + "func_ref_call" => func_ref_call, // TODO(reftypes): this should work /* "func_ref_call_native" => Function::new_native(&store, |f: Function| -> Result { @@ -110,15 +114,15 @@ mod sys { } let sum_func = Function::new_native(&store, sum); - let call_func: &Function = instance.exports.get_function("call_func")?; + let call_func: Function = instance.lookup_function("call_func").unwrap(); let result = call_func.call(&[Value::FuncRef(Some(sum_func))])?; assert_eq!(result[0].unwrap_i32(), 16); } { let f: NativeFunc<(), i32> = instance - .exports - .get_native_function("call_host_func_with_wasm_func")?; + .get_native_function("call_host_func_with_wasm_func") + .unwrap(); let result = f.call()?; assert_eq!(result, 63); } @@ -147,39 +151,50 @@ mod sys { (call $get_new_extern_ref_native)) )"#; let module = Module::new(&store, wat)?; + let extern_ref_identity = Function::new( + &store, + FunctionType::new(vec![Type::ExternRef], vec![Type::ExternRef]), + |values| -> Result, _> { Ok(vec![values[0].clone()]) }, + ); + let extern_ref_identity_native = + Function::new_native(&store, |er: ExternRef| -> ExternRef { er }); + let get_new_extern_ref = Function::new( + &store, + FunctionType::new(vec![], vec![Type::ExternRef]), + |_| -> Result, _> { + let inner = [ + ("hello".to_string(), "world".to_string()), + ("color".to_string(), "orange".to_string()), + ] + .iter() + .cloned() + .collect::>(); + let new_extern_ref = ExternRef::new(inner); + Ok(vec![Value::ExternRef(new_extern_ref)]) + }, + ); + let get_new_extern_ref_native = Function::new_native(&store, || -> ExternRef { + let inner = [ + ("hello".to_string(), "world".to_string()), + ("color".to_string(), "orange".to_string()), + ] + .iter() + .cloned() + .collect::>(); + ExternRef::new(inner) + }); let imports = imports! { "env" => { - "extern_ref_identity" => Function::new(&store, FunctionType::new([Type::ExternRef], [Type::ExternRef]), |values| -> Result, _> { - Ok(vec![values[0].clone()]) - }), - "extern_ref_identity_native" => Function::new_native(&store, |er: ExternRef| -> ExternRef { - er - }), - "get_new_extern_ref" => Function::new(&store, FunctionType::new([], [Type::ExternRef]), |_| -> Result, _> { - let inner = - [("hello".to_string(), "world".to_string()), - ("color".to_string(), "orange".to_string())] - .iter() - .cloned() - .collect::>(); - let new_extern_ref = ExternRef::new(inner); - Ok(vec![Value::ExternRef(new_extern_ref)]) - }), - "get_new_extern_ref_native" => Function::new_native(&store, || -> ExternRef { - let inner = - [("hello".to_string(), "world".to_string()), - ("color".to_string(), "orange".to_string())] - .iter() - .cloned() - .collect::>(); - ExternRef::new(inner) - }) + "extern_ref_identity" => extern_ref_identity, + "extern_ref_identity_native" => extern_ref_identity_native, + "get_new_extern_ref" => get_new_extern_ref, + "get_new_extern_ref_native" => get_new_extern_ref_native, }, }; let instance = Instance::new(&module, &imports)?; for run in &["run", "run_native"] { - let f: &Function = instance.exports.get_function(run)?; + let f: Function = instance.lookup_function(run).unwrap(); let results = f.call(&[]).unwrap(); if let Value::ExternRef(er) = &results[0] { assert!(er.is_null()); @@ -187,13 +202,13 @@ mod sys { panic!("result is not an extern ref!"); } - let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(run)?; + let f: NativeFunc<(), ExternRef> = instance.get_native_function(run).unwrap(); let result: ExternRef = f.call()?; assert!(result.is_null()); } for get_hashmap in &["get_hashmap", "get_hashmap_native"] { - let f: &Function = instance.exports.get_function(get_hashmap)?; + let f: Function = instance.lookup_function(get_hashmap).unwrap(); let results = f.call(&[]).unwrap(); if let Value::ExternRef(er) = &results[0] { let inner: &HashMap = er.downcast().unwrap(); @@ -203,7 +218,7 @@ mod sys { panic!("result is not an extern ref!"); } - let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(get_hashmap)?; + let f: NativeFunc<(), ExternRef> = instance.get_native_function(get_hashmap).unwrap(); let result: ExternRef = f.call()?; let inner: &HashMap = result.downcast().unwrap(); @@ -226,7 +241,7 @@ mod sys { )"#; let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; - let f: NativeFunc = instance.exports.get_native_function("drop")?; + let f: NativeFunc = instance.get_native_function("drop").unwrap(); let er = ExternRef::new(3u32); f.call(er.clone())?; @@ -251,17 +266,23 @@ mod sys { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; { - let er_global: &Global = instance.exports.get_global("er_global")?; + let er_global = if let Some(Export::Global(g)) = instance.lookup("er_global") { + g.from + } else { + panic!("no global"); + }; - if let Value::ExternRef(er) = er_global.get() { + if let Value::ExternRef(er) = er_global.get(&store) { assert!(er.is_null()); } else { panic!("Did not find extern ref in the global"); } - er_global.set(Val::ExternRef(ExternRef::new(3u32)))?; + unsafe { + er_global.set(Val::ExternRef(ExternRef::new(3u32)))?; + } - if let Value::ExternRef(er) = er_global.get() { + if let Value::ExternRef(er) = er_global.get(&store) { assert_eq!(er.downcast::().unwrap(), &3); assert_eq!(er.strong_count(), 1); } else { @@ -270,9 +291,14 @@ mod sys { } { - let fr_global: &Global = instance.exports.get_global("fr_immutable_global")?; + let fr_global = if let Some(Export::Global(g)) = instance.lookup("fr_immutable_global") + { + g.from + } else { + panic!("no global"); + }; - if let Value::FuncRef(Some(f)) = fr_global.get() { + if let Value::FuncRef(Some(f)) = fr_global.get(&store) { let native_func: NativeFunc<(), u32> = f.native()?; assert_eq!(native_func.call()?, 73); } else { @@ -281,18 +307,24 @@ mod sys { } { - let fr_global: &Global = instance.exports.get_global("fr_global")?; + let fr_global = if let Some(Export::Global(g)) = instance.lookup("fr_global") { + g.from + } else { + panic!("no global"); + }; - if let Value::FuncRef(None) = fr_global.get() { + if let Value::FuncRef(None) = fr_global.get(&store) { } else { panic!("Did not find a null func ref in the global"); } let f = Function::new_native(&store, |arg1: i32, arg2: i32| -> i32 { arg1 + arg2 }); - fr_global.set(Val::FuncRef(Some(f)))?; + unsafe { + fr_global.set(Val::FuncRef(Some(f)))?; + } - if let Value::FuncRef(Some(f)) = fr_global.get() { + if let Value::FuncRef(Some(f)) = fr_global.get(&store) { let native: NativeFunc<(i32, i32), i32> = f.native()?; assert_eq!(native.call(5, 7)?, 12); } else { @@ -307,6 +339,8 @@ mod sys { #[test] #[cfg_attr(feature = "singlepass", ignore)] // singlepass does not support funcref args. fn extern_ref_ref_counting_table_basic() -> Result<()> { + use wasmer_vm::TableElement; + let store = Store::default(); let wat = r#"(module (global $global (export "global") (mut externref) (ref.null extern)) @@ -323,22 +357,27 @@ mod sys { let instance = Instance::new(&module, &imports! {})?; let f: NativeFunc<(ExternRef, i32), ExternRef> = - instance.exports.get_native_function("insert_into_table")?; + instance.get_native_function("insert_into_table").unwrap(); let er = ExternRef::new(3usize); let er = f.call(er, 1)?; assert_eq!(er.strong_count(), 2); - let table: &Table = instance.exports.get_table("table")?; + let table = if let Some(Export::Table(t)) = instance.lookup("table") { + t.from + } else { + panic!("no table"); + }; - { - let er2 = table.get(1).unwrap().externref().unwrap(); + if let TableElement::ExternRef(er2) = table.get(1).unwrap() { assert_eq!(er2.strong_count(), 3); } assert_eq!(er.strong_count(), 2); - table.set(1, Val::ExternRef(ExternRef::null()))?; + table + .set(1, TableElement::ExternRef(ExternRef::null())) + .unwrap(); assert_eq!(er.strong_count(), 1); @@ -360,18 +399,26 @@ mod sys { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; - let global: &Global = instance.exports.get_global("global")?; + let global = if let Some(Export::Global(g)) = instance.lookup("global") { + g.from + } else { + panic!("not a global"); + }; { let er = ExternRef::new(3usize); - global.set(Val::ExternRef(er.clone()))?; + unsafe { + global.set(Val::ExternRef(er.clone()))?; + } assert_eq!(er.strong_count(), 2); } let get_from_global: NativeFunc<(), ExternRef> = - instance.exports.get_native_function("get_from_global")?; + instance.get_native_function("get_from_global").unwrap(); let er = get_from_global.call()?; assert_eq!(er.strong_count(), 2); - global.set(Val::ExternRef(ExternRef::null()))?; + unsafe { + global.set(Val::ExternRef(ExternRef::null()))?; + } assert_eq!(er.strong_count(), 1); Ok(()) @@ -392,7 +439,7 @@ mod sys { let instance = Instance::new(&module, &imports! {})?; let pass_extern_ref: NativeFunc = - instance.exports.get_native_function("pass_extern_ref")?; + instance.get_native_function("pass_extern_ref").unwrap(); let er = ExternRef::new(3usize); assert_eq!(er.strong_count(), 1); @@ -408,6 +455,8 @@ mod sys { #[test] #[cfg_attr(feature = "singlepass", ignore)] // singlepass does not support funcref args. fn extern_ref_ref_counting_table_instructions() -> Result<()> { + use wasmer_vm::TableElement; + let store = Store::default(); let wat = r#"(module (table $table1 (export "table1") 2 12 externref) @@ -422,16 +471,19 @@ mod sys { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; - let grow_table_with_ref: NativeFunc<(ExternRef, i32), i32> = instance - .exports - .get_native_function("grow_table_with_ref")?; - let fill_table_with_ref: NativeFunc<(ExternRef, i32, i32), ()> = instance - .exports - .get_native_function("fill_table_with_ref")?; + let grow_table_with_ref: NativeFunc<(ExternRef, i32), i32> = + instance.get_native_function("grow_table_with_ref").unwrap(); + let fill_table_with_ref: NativeFunc<(ExternRef, i32, i32), ()> = + instance.get_native_function("fill_table_with_ref").unwrap(); let copy_into_table2: NativeFunc<(), ()> = - instance.exports.get_native_function("copy_into_table2")?; - let table1: &Table = instance.exports.get_table("table1")?; - let table2: &Table = instance.exports.get_table("table2")?; + instance.get_native_function("copy_into_table2").unwrap(); + let (table1, table2) = if let (Some(Export::Table(t1)), Some(Export::Table(t2))) = + (instance.lookup("table1"), instance.lookup("table2")) + { + (t1.from, t2.from) + } else { + panic!("can't get tables"); + }; let er1 = ExternRef::new(3usize); let er2 = ExternRef::new(5usize); @@ -450,9 +502,10 @@ mod sys { assert_eq!(er1.strong_count(), 9); for i in 2..10 { - let e = table1.get(i).unwrap().unwrap_externref(); - assert_eq!(*e.downcast::().unwrap(), 3); - assert_eq!(&e, &er1); + if let TableElement::ExternRef(e) = table1.get(i).unwrap() { + assert_eq!(*e.downcast::().unwrap(), 3); + assert_eq!(&e, &er1); + } } assert_eq!(er1.strong_count(), 9); } @@ -463,11 +516,11 @@ mod sys { } { - table2.set(0, Val::ExternRef(er3.clone()))?; - table2.set(1, Val::ExternRef(er3.clone()))?; - table2.set(2, Val::ExternRef(er3.clone()))?; - table2.set(3, Val::ExternRef(er3.clone()))?; - table2.set(4, Val::ExternRef(er3.clone()))?; + table2.set(0, TableElement::ExternRef(er3.clone())).unwrap(); + table2.set(1, TableElement::ExternRef(er3.clone())).unwrap(); + table2.set(2, TableElement::ExternRef(er3.clone())).unwrap(); + table2.set(3, TableElement::ExternRef(er3.clone())).unwrap(); + table2.set(4, TableElement::ExternRef(er3.clone())).unwrap(); assert_eq!(er3.strong_count(), 6); } @@ -477,22 +530,29 @@ mod sys { assert_eq!(er2.strong_count(), 5); assert_eq!(er1.strong_count(), 11); for i in 1..5 { - let e = table2.get(i).unwrap().unwrap_externref(); - let value = e.downcast::().unwrap(); - match i { - 0 | 1 => assert_eq!(*value, 5), - 4 => assert_eq!(*value, 7), - _ => assert_eq!(*value, 3), + if let TableElement::ExternRef(e) = table2.get(i).unwrap() { + let value = e.downcast::().unwrap(); + match i { + 0 | 1 => assert_eq!(*value, 5), + 4 => assert_eq!(*value, 7), + _ => assert_eq!(*value, 3), + } + } else { + panic!("not extern ref"); } } } { for i in 0..table1.size() { - table1.set(i, Val::ExternRef(ExternRef::null()))?; + table1 + .set(i, TableElement::ExternRef(ExternRef::null())) + .unwrap(); } for i in 0..table2.size() { - table2.set(i, Val::ExternRef(ExternRef::null()))?; + table2 + .set(i, TableElement::ExternRef(ExternRef::null())) + .unwrap(); } } diff --git a/lib/cache/Cargo.toml b/lib/cache/Cargo.toml deleted file mode 100644 index d182e9c290..0000000000 --- a/lib/cache/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "wasmer-cache" -version = "2.1.0" -description = "Cache system for Wasmer WebAssembly runtime" -categories = ["wasm", "caching"] -keywords = ["wasm", "webassembly", "cache"] -authors = ["Wasmer Engineering Team "] -repository = "https://github.com/wasmerio/wasmer" -license = "MIT" -readme = "README.md" -edition = "2018" - -[dependencies] -wasmer = { path = "../api", version = "=2.2.2", package = "wasmer-near", default-features = false, features = ["sys"] } -hex = "0.4" -thiserror = "1" -blake3 = "1.0" - -[dev-dependencies] -criterion = "0.3" -tempfile = "3" -rand = "0.8.3" -wasmer-compiler-singlepass = { path = "../compiler-singlepass", package = "wasmer-compiler-singlepass-near", version = "2.0.3" } -wasmer-engine-universal = { path = "../engine-universal", package = "wasmer-engine-universal-near", version = "2.0.3" } - -[features] -blake3-pure = ["blake3/pure"] - -[[bench]] -name = "bench_filesystem_cache" -harness = false diff --git a/lib/cache/README.md b/lib/cache/README.md deleted file mode 100644 index a80abac103..0000000000 --- a/lib/cache/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# `wasmer-cache` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) - -The `wasmer-cache` crate allows to cache WebAssembly modules (of kind -`wasmer::Module`) in your system, so that next uses of the module does -imply a compilation time. - -## Usage - -The `Cache` trait represents a generic cache for storing and loading -compiled WebAssembly modules. The `FileSystemCache` type implements -`Cache` to store cache on the file system. - -```rust -use wasmer::{DeserializeError, Module, SerializeError}; -use wasmer_cache::{Cache, FileSystemCache, Hash}; - -fn store_module(module: &Module, bytes: &[u8]) -> Result<(), SerializeError> { - // Create a new file system cache. - let mut fs_cache = FileSystemCache::new("some/directory/goes/here")?; - - // Compute a key for a given WebAssembly binary - let hash = Hash::generate(bytes); - - // Store a module into the cache given a key - fs_cache.store(hash, module.clone())?; - - Ok(()) -} -``` diff --git a/lib/cache/benches/bench_filesystem_cache.rs b/lib/cache/benches/bench_filesystem_cache.rs deleted file mode 100644 index f9adc74b3d..0000000000 --- a/lib/cache/benches/bench_filesystem_cache.rs +++ /dev/null @@ -1,95 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use rand::distributions::Alphanumeric; -use rand::{thread_rng, Rng}; -use tempfile::TempDir; -use wasmer::{Module, Store}; -use wasmer_cache::Cache; -use wasmer_cache::{FileSystemCache, Hash}; -use wasmer_compiler_singlepass::Singlepass; -use wasmer_engine_dylib::Dylib; -use wasmer_engine_universal::Universal; - -fn random_key() -> Hash { - Hash::new(rand::thread_rng().gen::<[u8; 32]>()) -} - -pub fn store_cache_universal(c: &mut Criterion) { - let tmp_dir = TempDir::new().unwrap(); - let mut fs_cache = FileSystemCache::new(tmp_dir.path()).unwrap(); - let compiler = Singlepass::default(); - let store = Store::new(&Universal::new(compiler).engine()); - let module = Module::new( - &store, - std::fs::read("../../lib/c-api/examples/assets/qjs.wasm").unwrap(), - ) - .unwrap(); - - c.bench_function("store universal module in filesystem cache", |b| { - b.iter(|| { - let key = random_key(); - fs_cache.store(key, &module).unwrap() - }) - }); -} - -pub fn load_cache_universal(c: &mut Criterion) { - let tmp_dir = TempDir::new().unwrap(); - let mut fs_cache = FileSystemCache::new(tmp_dir.path()).unwrap(); - let compiler = Singlepass::default(); - let store = Store::new(&Universal::new(compiler).engine()); - let module = Module::new( - &store, - std::fs::read("../../lib/c-api/examples/assets/qjs.wasm").unwrap(), - ) - .unwrap(); - let key = Hash::new([0u8; 32]); - fs_cache.store(key, &module).unwrap(); - - c.bench_function("load universal module in filesystem cache", |b| { - b.iter(|| unsafe { fs_cache.load(&store, key.clone()).unwrap() }) - }); -} - -pub fn store_cache_native(c: &mut Criterion) { - let tmp_dir = TempDir::new().unwrap(); - let mut fs_cache = FileSystemCache::new(tmp_dir.path()).unwrap(); - let compiler = Singlepass::default(); - let store = Store::new(&Native::new(compiler).engine()); - let module = Module::new( - &store, - std::fs::read("../../lib/c-api/examples/assets/qjs.wasm").unwrap(), - ) - .unwrap(); - - c.bench_function("store native module in filesystem cache", |b| { - b.iter(|| { - let key = random_key(); - fs_cache.store(key, &module).unwrap() - }) - }); -} - -pub fn load_cache_native(c: &mut Criterion) { - let tmp_dir = TempDir::new().unwrap(); - let mut fs_cache = FileSystemCache::new(tmp_dir.path()).unwrap(); - let compiler = Singlepass::default(); - let store = Store::new(&Native::new(compiler).engine()); - let module = Module::new( - &store, - std::fs::read("../../lib/c-api/examples/assets/qjs.wasm").unwrap(), - ) - .unwrap(); - let key = Hash::new([0u8; 32]); - fs_cache.store(key, &module).unwrap(); - - c.bench_function("load native module in filesystem cache", |b| { - b.iter(|| unsafe { fs_cache.load(&store, key.clone()).unwrap() }) - }); -} - -criterion_group! { - name = benches; - config = Criterion::default().sample_size(300); - targets = store_cache_universal, load_cache_universal, store_cache_native, load_cache_native -} -criterion_main!(benches); diff --git a/lib/cache/src/cache.rs b/lib/cache/src/cache.rs deleted file mode 100644 index 057238daba..0000000000 --- a/lib/cache/src/cache.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! The cache module provides the common data structures used by compiler backends to allow -//! serializing compiled wasm code to a binary format. The binary format can be persisted, -//! and loaded to allow skipping compilation and fast startup. - -use crate::hash::Hash; -use std::error::Error; -use wasmer::{Module, Store}; - -/// A generic cache for storing and loading compiled wasm modules. -pub trait Cache { - /// The serialization error for the implementation - type SerializeError: Error + Send + Sync; - /// The deserialization error for the implementation - type DeserializeError: Error + Send + Sync; - - /// Loads a module using the provided [`Store`] and [`Hash`]. - /// - /// # Safety - /// This function is unsafe as the cache store could be tampered with. - unsafe fn load(&self, store: &Store, key: Hash) -> Result; - - /// Store a [`Module`] into the cache with the given [`Hash`]. - fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError>; -} diff --git a/lib/cache/src/filesystem.rs b/lib/cache/src/filesystem.rs deleted file mode 100644 index cbd30c1cdb..0000000000 --- a/lib/cache/src/filesystem.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::cache::Cache; -use crate::hash::Hash; -use std::fs::{create_dir_all, File}; -use std::io::{self, Write}; -use std::path::PathBuf; -use wasmer::{DeserializeError, Module, SerializeError, Store}; - -/// Representation of a directory that contains compiled wasm artifacts. -/// -/// The `FileSystemCache` type implements the [`Cache`] trait, which allows it to be used -/// generically when some sort of cache is required. -/// -/// # Usage -/// -/// ``` -/// use wasmer::{DeserializeError, SerializeError}; -/// use wasmer_cache::{Cache, FileSystemCache, Hash}; -/// -/// # use wasmer::{Module}; -/// fn store_module(module: &Module, bytes: &[u8]) -> Result<(), SerializeError> { -/// // Create a new file system cache. -/// let mut fs_cache = FileSystemCache::new("some/directory/goes/here")?; -/// -/// // Compute a key for a given WebAssembly binary -/// let key = Hash::generate(bytes); -/// -/// // Store a module into the cache given a key -/// fs_cache.store(key, module)?; -/// -/// Ok(()) -/// } -/// ``` -pub struct FileSystemCache { - path: PathBuf, - ext: Option, -} - -impl FileSystemCache { - /// Construct a new `FileSystemCache` around the specified directory. - pub fn new>(path: P) -> io::Result { - let path: PathBuf = path.into(); - if path.exists() { - let metadata = path.metadata()?; - if metadata.is_dir() { - if !metadata.permissions().readonly() { - Ok(Self { path, ext: None }) - } else { - // This directory is readonly. - Err(io::Error::new( - io::ErrorKind::PermissionDenied, - format!("the supplied path is readonly: {}", path.display()), - )) - } - } else { - // This path points to a file. - Err(io::Error::new( - io::ErrorKind::PermissionDenied, - format!( - "the supplied path already points to a file: {}", - path.display() - ), - )) - } - } else { - // Create the directory and any parent directories if they don't yet exist. - let res = create_dir_all(&path); - if res.is_err() { - Err(io::Error::new( - io::ErrorKind::Other, - format!("failed to create cache directory: {}", path.display()), - )) - } else { - Ok(Self { path, ext: None }) - } - } - } - - /// Set the extension for this cached file. - /// - /// This is needed for loading native files from Windows, as otherwise - /// loading the library will fail (it requires a `.dll` extension) - pub fn set_cache_extension(&mut self, ext: Option) { - self.ext = ext.map(|ext| ext.to_string()); - } -} - -impl Cache for FileSystemCache { - type DeserializeError = DeserializeError; - type SerializeError = SerializeError; - - unsafe fn load(&self, store: &Store, key: Hash) -> Result { - let filename = if let Some(ref ext) = self.ext { - format!("{}.{}", key.to_string(), ext) - } else { - key.to_string() - }; - let path = self.path.join(filename); - Module::deserialize_from_file(&store, path) - } - - fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> { - let filename = if let Some(ref ext) = self.ext { - format!("{}.{}", key.to_string(), ext) - } else { - key.to_string() - }; - let path = self.path.join(filename); - let mut file = File::create(path)?; - - let buffer = module.serialize()?; - file.write_all(&buffer)?; - - Ok(()) - } -} diff --git a/lib/cache/src/hash.rs b/lib/cache/src/hash.rs deleted file mode 100644 index 8504413bf0..0000000000 --- a/lib/cache/src/hash.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::DeserializeError; -use std::str::FromStr; -use std::string::ToString; - -/// A hash used as a key when loading and storing modules in a -/// [`Cache`]. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -// Hash is made up of a 32 byte array -pub struct Hash([u8; 32]); - -impl Hash { - /// Creates a new instance from 32 raw bytes. - /// Does not perform any hashing. In order to create a hash from data, - /// use `Hash::generate`. - pub fn new(bytes: [u8; 32]) -> Self { - Self(bytes) - } - - /// Creates a new hash from a slice of bytes. - pub fn generate(bytes: &[u8]) -> Self { - let hash = blake3::hash(bytes); - Self::new(hash.into()) - } - - pub(crate) fn to_array(&self) -> [u8; 32] { - self.0 - } -} - -impl ToString for Hash { - /// Create the hexadecimal representation of the - /// stored hash. - fn to_string(&self) -> String { - hex::encode(&self.to_array()) - } -} - -impl FromStr for Hash { - type Err = DeserializeError; - /// Create hash from hexadecimal representation - fn from_str(s: &str) -> Result { - let bytes = hex::decode(s).map_err(|e| { - DeserializeError::Generic(format!( - "Could not decode prehashed key as hexadecimal: {}", - e - )) - })?; - if bytes.len() != 32 { - return Err(DeserializeError::Generic( - "Prehashed keys must deserialze into exactly 32 bytes".to_string(), - )); - } - use std::convert::TryInto; - Ok(Self(bytes[0..32].try_into().map_err(|e| { - DeserializeError::Generic(format!("Could not get first 32 bytes: {}", e)) - })?)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn hash_to_array_works() { - let original = [ - 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, - 0xEE, 0xFF, 0x12, 0x65, - ]; - let hash = Hash::new(original); - assert_eq!(hash.to_array(), original); - } -} diff --git a/lib/cache/src/lib.rs b/lib/cache/src/lib.rs deleted file mode 100644 index ffe1e9ce65..0000000000 --- a/lib/cache/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! The `wasmer-cache` crate provides the necessary abstractions -//! to cache WebAssembly Modules easily. - -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "std", deny(unstable_features))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] -#![cfg_attr( - feature = "cargo-clippy", - warn( - clippy::float_arithmetic, - clippy::mut_mut, - clippy::nonminimal_bool, - clippy::option_map_unwrap_or, - clippy::option_map_unwrap_or_else, - clippy::print_stdout, - clippy::unicode_not_nfc, - clippy::use_self - ) -)] - -mod cache; -mod filesystem; -mod hash; - -pub use crate::cache::Cache; -pub use crate::filesystem::FileSystemCache; -pub use crate::hash::Hash; - -// We re-export those for convinience of users -pub use wasmer::{DeserializeError, SerializeError}; diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 2447393d74..4b6a707285 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -33,7 +33,6 @@ wasmer-engine = { version = "2.0.3", path = "../engine", package = "wasmer-engin wasmer-engine-universal = { version = "2.0.3", path = "../engine-universal", package = "wasmer-engine-universal-near", optional = true } wasmer-vm = { version = "2.0.3", path = "../vm", package = "wasmer-vm-near" } wasmer-wast = { version = "2.0.0", path = "../../tests/lib/wast", optional = true } -wasmer-cache = { version = "2.0.0", path = "../cache", optional = true } wasmer-types = { version = "2.0.3", path = "../types", package = "wasmer-types-near" } atty = "0.2" colored = "2.0" @@ -56,15 +55,12 @@ default = [ "wat", "wast", "universal", - "cache", ] engine = [] universal = [ "wasmer-engine-universal", "engine", ] -cache = ["wasmer-cache"] -cache-blake3-pure = ["wasmer-cache/blake3-pure"] wast = ["wasmer-wast"] wat = ["wasmer/wat"] compiler = [ diff --git a/lib/cli/src/commands/compile.rs b/lib/cli/src/commands/compile.rs index 26b60ba863..76ada5ebbc 100644 --- a/lib/cli/src/commands/compile.rs +++ b/lib/cli/src/commands/compile.rs @@ -99,6 +99,7 @@ impl Compile { println!("Target: {}", target.triple()); let module = Module::from_file(&store, &self.path)?; + let _ = module.serialize_to_file(&self.output)?; eprintln!( "✔ File compiled successfully to `{}`.", diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index d3109ed011..7c04a8df1a 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -8,19 +8,12 @@ use anyhow::{anyhow, Context, Result}; use std::path::PathBuf; use std::str::FromStr; use wasmer::*; -#[cfg(feature = "cache")] -use wasmer_cache::{Cache, FileSystemCache, Hash}; use structopt::StructOpt; #[derive(Debug, StructOpt, Clone, Default)] /// The options for the `wasmer run` subcommand pub struct Run { - /// Disable the cache - #[cfg(feature = "cache")] - #[structopt(long = "disable-cache")] - disable_cache: bool, - /// File to run #[structopt(name = "FILE", parse(from_os_str))] path: PathBuf, @@ -35,21 +28,9 @@ pub struct Run { #[structopt(long = "command-name", hidden = true)] command_name: Option, - /// A prehashed string, used to speed up start times by avoiding hashing the - /// wasm module. If the specified hash is not found, Wasmer will hash the module - /// as if no `cache-key` argument was passed. - #[cfg(feature = "cache")] - #[structopt(long = "cache-key", hidden = true)] - cache_key: Option, - #[structopt(flatten)] store: StoreOptions, - /// Enable non-standard experimental IO devices - #[cfg(feature = "io-devices")] - #[structopt(long = "enable-io-devices")] - enable_experimental_io_devices: bool, - /// Enable debug output #[cfg(feature = "debug")] #[structopt(long = "debug", short = "d")] @@ -86,46 +67,6 @@ impl Run { fn inner_execute(&self) -> Result<()> { let module = self.get_module()?; - #[cfg(feature = "emscripten")] - { - use wasmer_emscripten::{ - generate_emscripten_env, is_emscripten_module, run_emscripten_instance, EmEnv, - EmscriptenGlobals, - }; - // TODO: refactor this - if is_emscripten_module(&module) { - if self.invoke.is_some() { - bail!("--invoke is not supported with emscripten modules"); - } - let mut emscripten_globals = EmscriptenGlobals::new(module.store(), &module) - .map_err(|e| anyhow!("{}", e))?; - let mut em_env = EmEnv::new(&emscripten_globals.data, Default::default()); - let import_object = - generate_emscripten_env(module.store(), &mut emscripten_globals, &mut em_env); - let mut instance = match Instance::new(&module, &import_object) { - Ok(instance) => instance, - Err(e) => { - let err: Result<(), _> = Err(e); - return err.with_context(|| "Can't instantiate emscripten module"); - } - }; - - run_emscripten_instance( - &mut instance, - &mut em_env, - &mut emscripten_globals, - if let Some(cn) = &self.command_name { - cn - } else { - self.path.to_str().unwrap() - }, - self.args.iter().map(|arg| arg.as_str()).collect(), - None, //run.em_entrypoint.clone(), - )?; - return Ok(()); - } - } - let instance = Instance::new(&module, &imports! {})?; // If this module exports an _initialize function, run that first. @@ -159,32 +100,22 @@ impl Run { fn get_module(&self) -> Result { let contents = std::fs::read(self.path.clone())?; - #[cfg(feature = "dylib")] - { - if wasmer_engine_dylib::DylibArtifact::is_deserializable(&contents) { - let engine = wasmer_engine_dylib::Dylib::headless().engine(); - let store = Store::new(&engine); - let module = unsafe { Module::deserialize_from_file(&store, &self.path)? }; - return Ok(module); - } - } #[cfg(feature = "universal")] { - if wasmer_engine_universal::UniversalArtifact::is_deserializable(&contents) { - let engine = wasmer_engine_universal::Universal::headless().engine(); - let store = Store::new(&engine); - let module = unsafe { Module::deserialize_from_file(&store, &self.path)? }; + use wasmer_engine_universal::{Universal, UniversalArtifact, UniversalExecutable}; + + if UniversalExecutable::verify_serialized(&contents) { + unsafe { + let executable = UniversalExecutable::archive_from_slice(&contents)?; + let engine = wasmer_engine_universal::Universal::headless().engine(); + let artifact = engine.load(&executable); + let store = Store::new(&engine); + let module = unsafe { Module::deserialize_from_file(&store, &self.path)? }; + } return Ok(module); } } let (store, engine_type, compiler_type) = self.store.get_store()?; - #[cfg(feature = "cache")] - let module_result: Result = if !self.disable_cache && contents.len() > 0x1000 { - self.get_module_from_cache(&store, &contents, &engine_type, &compiler_type) - } else { - Module::new(&store, &contents).map_err(|e| e.into()) - }; - #[cfg(not(feature = "cache"))] let module_result = Module::new(&store, &contents); let mut module = module_result.with_context(|| { @@ -418,7 +349,7 @@ impl Run { args, path: executable.into(), command_name: Some(original_executable), - store: store, + store, ..Self::default() }) } diff --git a/lib/compiler-cranelift/Cargo.toml b/lib/compiler-cranelift/Cargo.toml index 760f55cc0a..7ec00a7a88 100644 --- a/lib/compiler-cranelift/Cargo.toml +++ b/lib/compiler-cranelift/Cargo.toml @@ -24,7 +24,6 @@ rayon = "1.5" more-asserts = "0.2" gimli = { version = "0.25", optional = true } smallvec = "1.6" -loupe = "0.1" target-lexicon = { version = "0.12.2", default-features = false } [dev-dependencies] diff --git a/lib/compiler-cranelift/src/compiler.rs b/lib/compiler-cranelift/src/compiler.rs index 6ad6d44787..03f957fe47 100644 --- a/lib/compiler-cranelift/src/compiler.rs +++ b/lib/compiler-cranelift/src/compiler.rs @@ -18,7 +18,6 @@ use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::{binemit, Context}; #[cfg(feature = "unwind")] use gimli::write::{Address, EhFrame, FrameTable}; -use loupe::MemoryUsage; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use std::sync::Arc; use target_lexicon::{Architecture, OperatingSystem}; @@ -40,7 +39,6 @@ use wasmer_vm::libcalls::LibCall; /// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR, /// optimizing it and then translating to assembly. -#[derive(MemoryUsage)] pub struct CraneliftCompiler { config: Cranelift, } @@ -58,10 +56,6 @@ impl CraneliftCompiler { } impl Compiler for CraneliftCompiler { - fn use_signals(&self) -> bool { - true - } - /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares @@ -280,7 +274,7 @@ impl Compiler for CraneliftCompiler { .collect::>(); use wasmer_vm::VMOffsets; - let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes()); + let offsets = VMOffsets::new(frontend_config.pointer_bytes()); // dynamic function trampolines (only for imported functions) let dynamic_function_trampolines = module .imported_function_types() diff --git a/lib/compiler-cranelift/src/config.rs b/lib/compiler-cranelift/src/config.rs index 397b405931..513e00ca31 100644 --- a/lib/compiler-cranelift/src/config.rs +++ b/lib/compiler-cranelift/src/config.rs @@ -1,7 +1,6 @@ use crate::compiler::CraneliftCompiler; use cranelift_codegen::isa::{lookup, TargetIsa}; use cranelift_codegen::settings::{self, Configurable}; -use loupe::MemoryUsage; use std::sync::Arc; use wasmer_compiler::{ Architecture, Compiler, CompilerConfig, CpuFeature, ModuleMiddleware, Target, @@ -11,7 +10,7 @@ use wasmer_compiler::{ /// Possible optimization levels for the Cranelift codegen backend. #[non_exhaustive] -#[derive(Clone, Debug, MemoryUsage)] +#[derive(Clone, Debug)] pub enum CraneliftOptLevel { /// No optimizations performed, minimizes compilation time by disabling most /// optimizations. @@ -28,7 +27,7 @@ pub enum CraneliftOptLevel { /// /// This structure exposes a builder-like interface and is primarily /// consumed by `wasmer_engine::Engine::new`. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub struct Cranelift { enable_nan_canonicalization: bool, enable_verifier: bool, diff --git a/lib/compiler-cranelift/src/func_environ.rs b/lib/compiler-cranelift/src/func_environ.rs index 2cdab38e1f..7399553343 100644 --- a/lib/compiler-cranelift/src/func_environ.rs +++ b/lib/compiler-cranelift/src/func_environ.rs @@ -150,7 +150,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { table_fill_sig: None, externref_inc_sig: None, externref_dec_sig: None, - offsets: VMOffsets::new(target_config.pointer_bytes(), module), + offsets: VMOffsets::new(target_config.pointer_bytes()).with_module_info(module), memory_styles, table_styles, } diff --git a/lib/compiler-llvm/Cargo.toml b/lib/compiler-llvm/Cargo.toml index 32d5840010..7fee7e170d 100644 --- a/lib/compiler-llvm/Cargo.toml +++ b/lib/compiler-llvm/Cargo.toml @@ -22,7 +22,6 @@ libc = { version = "^0.2", default-features = false } byteorder = "1" itertools = "0.10" rayon = "1.5" -loupe = "0.1" [dependencies.inkwell] package = "inkwell" diff --git a/lib/compiler-llvm/src/compiler.rs b/lib/compiler-llvm/src/compiler.rs index f81da0c703..c615661258 100644 --- a/lib/compiler-llvm/src/compiler.rs +++ b/lib/compiler-llvm/src/compiler.rs @@ -7,7 +7,6 @@ use inkwell::memory_buffer::MemoryBuffer; use inkwell::module::{Linkage, Module}; use inkwell::targets::FileType; use inkwell::DLLStorageClass; -use loupe::MemoryUsage; use rayon::iter::ParallelBridge; use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use std::sync::Arc; @@ -24,7 +23,6 @@ use wasmer_types::{FunctionIndex, LocalFunctionIndex, SignatureIndex}; /// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR, /// optimizing it and then translating to assembly. -#[derive(MemoryUsage)] pub struct LLVMCompiler { config: LLVM, } @@ -189,10 +187,6 @@ impl LLVMCompiler { } impl Compiler for LLVMCompiler { - fn use_signals(&self) -> bool { - true - } - /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler-llvm/src/config.rs b/lib/compiler-llvm/src/config.rs index c6b6519726..3b00db5996 100644 --- a/lib/compiler-llvm/src/config.rs +++ b/lib/compiler-llvm/src/config.rs @@ -5,7 +5,6 @@ use inkwell::targets::{ }; pub use inkwell::OptimizationLevel as LLVMOptLevel; use itertools::Itertools; -use loupe::MemoryUsage; use std::fmt::Debug; use std::sync::Arc; use target_lexicon::Architecture; @@ -38,14 +37,12 @@ pub trait LLVMCallbacks: Debug + Send + Sync { fn obj_memory_buffer(&self, function: &CompiledKind, memory_buffer: &InkwellMemoryBuffer); } -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub struct LLVM { pub(crate) enable_nan_canonicalization: bool, pub(crate) enable_verifier: bool, - #[loupe(skip)] pub(crate) opt_level: LLVMOptLevel, is_pic: bool, - #[loupe(skip)] pub(crate) callbacks: Option>, /// The middleware chain. pub(crate) middlewares: Vec>, diff --git a/lib/compiler-llvm/src/translator/code.rs b/lib/compiler-llvm/src/translator/code.rs index 3d78b07bae..9fb1e7097e 100644 --- a/lib/compiler-llvm/src/translator/code.rs +++ b/lib/compiler-llvm/src/translator/code.rs @@ -92,7 +92,8 @@ impl FuncTranslator { .unwrap(); // TODO: pointer width - let offsets = VMOffsets::new(8, &wasm_module); + let offsets = VMOffsets::new(target_data.get_pointer_byte_size(None) as u8) + .with_module_info(&wasm_module); let intrinsics = Intrinsics::declare(&module, &self.ctx, &target_data); let (func_type, func_attrs) = self.abi diff --git a/lib/compiler-llvm/src/translator/intrinsics.rs b/lib/compiler-llvm/src/translator/intrinsics.rs index 48f176f45a..400fe5cef5 100644 --- a/lib/compiler-llvm/src/translator/intrinsics.rs +++ b/lib/compiler-llvm/src/translator/intrinsics.rs @@ -1126,7 +1126,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { cached_memory_size: HashMap::new(), // TODO: pointer width - offsets: VMOffsets::new(8, &wasm_module), + offsets: VMOffsets::new(8).with_module_info(&wasm_module), } } diff --git a/lib/compiler-singlepass/Cargo.toml b/lib/compiler-singlepass/Cargo.toml index 020835b6af..78ddd746e2 100644 --- a/lib/compiler-singlepass/Cargo.toml +++ b/lib/compiler-singlepass/Cargo.toml @@ -16,7 +16,7 @@ name = "wasmer_compiler_singlepass" [dependencies] wasmer-compiler = { path = "../compiler", package = "wasmer-compiler-near", version = "=2.2.2", features = ["translator"], default-features = false } -wasmer-vm = { path = "../vm", package = "wasmer-vm-near", version = "=2.2.2", features = ["avoid-tls-signals"] } +wasmer-vm = { path = "../vm", package = "wasmer-vm-near", version = "=2.2.2" } wasmer-types = { path = "../types", package = "wasmer-types-near", version = "=2.2.2", default-features = false, features = ["std"] } rayon = { version = "1.5", optional = true } hashbrown = { version = "0.11", optional = true } @@ -26,7 +26,6 @@ dynasmrt = "1.0" lazy_static = "1.4" byteorder = "1.3" smallvec = "1.6" -loupe = "0.1" memoffset = "0.6" [dev-dependencies] diff --git a/lib/compiler-singlepass/src/codegen_x64.rs b/lib/compiler-singlepass/src/codegen_x64.rs index 84ed158dff..76a66a2e6c 100644 --- a/lib/compiler-singlepass/src/codegen_x64.rs +++ b/lib/compiler-singlepass/src/codegen_x64.rs @@ -343,12 +343,11 @@ impl<'a> FuncGen<'a> { } else { let reloc_at = self.assembler.get_offset().0 + self.assembler.arch_mov64_imm_offset(); // Imported functions are called through trampolines placed as custom sections. - let reloc_target = if function_index < self.module.num_imported_functions { + let imports = self.module.import_counts.functions as usize; + let reloc_target = if function_index < imports { RelocationTarget::CustomSection(SectionIndex::new(function_index)) } else { - RelocationTarget::LocalFunc(LocalFunctionIndex::new( - function_index - self.module.num_imported_functions, - )) + RelocationTarget::LocalFunc(LocalFunctionIndex::new(function_index - imports)) }; self.relocations.push(Relocation { kind: RelocationKind::Abs8, @@ -1386,7 +1385,7 @@ impl<'a> FuncGen<'a> { let tmp_addr = self.machine.acquire_temp_gpr().unwrap(); // Reusing `tmp_addr` for temporary indirection here, since it's not used before the last reference to `{base,bound}_loc`. - let (base_loc, bound_loc) = if self.module.num_imported_memories != 0 { + let (base_loc, bound_loc) = if self.module.import_counts.memories != 0 { // Imported memories require one level of indirection. let offset = self .vmoffsets @@ -8848,30 +8847,31 @@ pub(crate) fn gen_import_call_trampoline( // Emits a tail call trampoline that loads the address of the target import function // from Ctx and jumps to it. - let offset = vmoffsets.vmctx_vmfunction_import(index); + let body_offset = vmoffsets.vmctx_vmfunction_import_body(index); + let vmctx_offset = vmoffsets.vmctx_vmfunction_import_vmctx(index); match calling_convention { CallingConvention::WindowsFastcall => { a.emit_mov( Size::S64, - Location::Memory(GPR::RCX, offset as i32), // function pointer + Location::Memory(GPR::RCX, body_offset as i32), // function pointer Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, - Location::Memory(GPR::RCX, offset as i32 + 8), // target vmctx + Location::Memory(GPR::RCX, vmctx_offset as i32), // target vmctx Location::GPR(GPR::RCX), ); } _ => { a.emit_mov( Size::S64, - Location::Memory(GPR::RDI, offset as i32), // function pointer + Location::Memory(GPR::RDI, body_offset as i32), // function pointer Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, - Location::Memory(GPR::RDI, offset as i32 + 8), // target vmctx + Location::Memory(GPR::RDI, vmctx_offset as i32), // target vmctx Location::GPR(GPR::RDI), ); } diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index ba595f605d..460d087e0d 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -7,7 +7,6 @@ use crate::codegen_x64::{ CodegenError, FuncGen, }; use crate::config::Singlepass; -use loupe::MemoryUsage; #[cfg(feature = "rayon")] use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use std::sync::Arc; @@ -25,7 +24,6 @@ use wasmer_vm::{TrapCode, VMOffsets}; /// A compiler that compiles a WebAssembly module with Singlepass. /// It does the compilation in one pass -#[derive(MemoryUsage)] pub struct SinglepassCompiler { config: Singlepass, } @@ -43,11 +41,6 @@ impl SinglepassCompiler { } impl Compiler for SinglepassCompiler { - /// If signal handlers are required. - fn use_signals(&self) -> bool { - false - } - /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares @@ -88,13 +81,20 @@ impl Compiler for SinglepassCompiler { }; let table_styles = &compile_info.table_styles; - let vmoffsets = VMOffsets::new(8, &compile_info.module); let module = &compile_info.module; - let import_trampolines: PrimaryMap = (0..module.num_imported_functions) - .map(FunctionIndex::new) - .collect::>() + let pointer_width = target + .triple() + .pointer_width() + .map_err(|()| { + CompileError::UnsupportedTarget("target with unknown pointer width".into()) + })? + .bytes(); + let vmoffsets = VMOffsets::new(pointer_width).with_module_info(&module); + let import_idxs = 0..module.import_counts.functions as usize; + let import_trampolines: PrimaryMap = import_idxs .into_par_iter_if_rayon() .map(|i| { + let i = FunctionIndex::new(i); gen_import_call_trampoline( &vmoffsets, i, @@ -203,17 +203,19 @@ trait IntoParIterIfRayon { fn into_par_iter_if_rayon(self) -> Self::Output; } -impl IntoParIterIfRayon for Vec { - #[cfg(not(feature = "rayon"))] - type Output = std::vec::IntoIter; - #[cfg(feature = "rayon")] - type Output = rayon::vec::IntoIter; +#[cfg(feature = "rayon")] +impl IntoParIterIfRayon for T { + type Output = ::Iter; + fn into_par_iter_if_rayon(self) -> Self::Output { + return self.into_par_iter(); + } +} +#[cfg(not(feature = "rayon"))] +impl IntoParIterIfRayon for T { + type Output = ::IntoIter; fn into_par_iter_if_rayon(self) -> Self::Output { - #[cfg(not(feature = "rayon"))] return self.into_iter(); - #[cfg(feature = "rayon")] - return self.into_par_iter(); } } diff --git a/lib/compiler-singlepass/src/config.rs b/lib/compiler-singlepass/src/config.rs index 09d43d8a53..3a6520a1c3 100644 --- a/lib/compiler-singlepass/src/config.rs +++ b/lib/compiler-singlepass/src/config.rs @@ -3,25 +3,24 @@ use crate::compiler::SinglepassCompiler; use crate::emitter_x64::Location; -use loupe::MemoryUsage; use smallvec::SmallVec; use std::sync::Arc; use wasmer_compiler::{Compiler, CompilerConfig, CpuFeature, ModuleMiddleware, Target}; use wasmer_types::{Features, FunctionType, Type}; -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub(crate) enum IntrinsicKind { Gas, } -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub(crate) struct Intrinsic { pub(crate) kind: IntrinsicKind, pub(crate) name: String, pub(crate) signature: FunctionType, } -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub struct Singlepass { pub(crate) enable_nan_canonicalization: bool, pub(crate) enable_stack_check: bool, diff --git a/lib/compiler/Cargo.toml b/lib/compiler/Cargo.toml index 59e09fb39f..d5b0ec1f08 100644 --- a/lib/compiler/Cargo.toml +++ b/lib/compiler/Cargo.toml @@ -24,11 +24,10 @@ serde = { version = "1.0", features = ["derive"], optional = true } thiserror = "1.0" serde_bytes = { version = "0.11", optional = true } smallvec = "1.6" -rkyv = { version = "0.7.20", optional = true } -loupe = "0.1" +rkyv = { version = "0.7.20" } [features] -default = ["std", "enable-serde", "enable-rkyv"] +default = ["std", "enable-serde" ] # This feature is for compiler implementors, it enables using `Compiler` and # `CompilerConfig`, as well as the included wasmparser. # Disable this feature if you just want a headless engine. @@ -36,7 +35,6 @@ translator = ["wasmparser"] std = ["wasmer-types/std"] core = ["hashbrown", "wasmer-types/core"] enable-serde = ["serde", "serde_bytes", "wasmer-types/enable-serde"] -enable-rkyv = ["rkyv", "wasmer-vm/enable-rkyv", "wasmer-types/enable-rkyv"] [badges] maintenance = { status = "experimental" } diff --git a/lib/compiler/src/address_map.rs b/lib/compiler/src/address_map.rs index 74db48dcba..73a56c99c2 100644 --- a/lib/compiler/src/address_map.rs +++ b/lib/compiler/src/address_map.rs @@ -3,19 +3,13 @@ use crate::lib::std::vec::Vec; use crate::sourceloc::SourceLoc; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; /// Single source location to generated address mapping. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] pub struct InstructionAddressMap { /// Original source location. pub srcloc: SourceLoc, @@ -29,11 +23,7 @@ pub struct InstructionAddressMap { /// Function and its instructions addresses mappings. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, Default, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)] pub struct FunctionAddressMap { /// Instructions maps. /// The array is sorted by the InstructionAddressMap::code_offset field. diff --git a/lib/compiler/src/compiler.rs b/lib/compiler/src/compiler.rs index 498386f6b5..6cc44f9cb2 100644 --- a/lib/compiler/src/compiler.rs +++ b/lib/compiler/src/compiler.rs @@ -11,7 +11,6 @@ use crate::translator::ModuleMiddleware; use crate::FunctionBodyData; use crate::ModuleTranslationState; use crate::SectionIndex; -use loupe::MemoryUsage; use wasmer_types::entity::PrimaryMap; use wasmer_types::{Features, FunctionIndex, LocalFunctionIndex, SignatureIndex}; use wasmparser::{Validator, WasmFeatures}; @@ -78,7 +77,7 @@ where } /// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. -pub trait Compiler: Send + MemoryUsage { +pub trait Compiler: Send { /// Validates a module. /// /// It returns the a succesful Result in case is valid, `CompileError` in case is not. @@ -120,9 +119,6 @@ pub trait Compiler: Send + MemoryUsage { function_body_inputs: PrimaryMap>, ) -> Result; - /// If signal handlers are required. - fn use_signals(&self) -> bool; - /// Compiles a module into a native object file. /// /// It returns the bytes as a `&[u8]` or a [`CompileError`]. diff --git a/lib/compiler/src/error.rs b/lib/compiler/src/error.rs index cf107ad636..2423d79fe0 100644 --- a/lib/compiler/src/error.rs +++ b/lib/compiler/src/error.rs @@ -40,6 +40,13 @@ pub enum CompileError { /// Insufficient resources available for execution. #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] Resource(String), + + /// Cannot downcast the engine to a specific type. + #[cfg_attr( + feature = "std", + error("cannot downcast the engine to a specific type") + )] + EngineDowncast, } impl From for CompileError { diff --git a/lib/compiler/src/function.rs b/lib/compiler/src/function.rs index fda91c69e0..77c9cb6626 100644 --- a/lib/compiler/src/function.rs +++ b/lib/compiler/src/function.rs @@ -7,9 +7,10 @@ use crate::lib::std::vec::Vec; use crate::section::{CustomSection, SectionIndex}; use crate::trap::TrapInformation; -use crate::{CompiledFunctionUnwindInfo, FunctionAddressMap, JumpTableOffsets, Relocation}; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] +use crate::{ + CompiledFunctionUnwindInfo, CompiledFunctionUnwindInfoRef, FunctionAddressMap, + JumpTableOffsets, Relocation, +}; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -21,11 +22,7 @@ use wasmer_types::{FunctionIndex, LocalFunctionIndex, SignatureIndex}; /// This structure is only used for reconstructing /// the frame information after a `Trap`. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, Default, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)] pub struct CompiledFunctionFrameInfo { /// The traps (in the function body). /// @@ -38,11 +35,7 @@ pub struct CompiledFunctionFrameInfo { /// The function body. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] pub struct FunctionBody { /// The function body bytes. #[cfg_attr(feature = "enable-serde", serde(with = "serde_bytes"))] @@ -52,17 +45,40 @@ pub struct FunctionBody { pub unwind_info: Option, } +/// See [`FunctionBody`]. +#[derive(Clone, Copy)] +pub struct FunctionBodyRef<'a> { + /// Function body bytes. + pub body: &'a [u8], + /// The function unwind info. + pub unwind_info: Option>, +} + +impl<'a> From<&'a FunctionBody> for FunctionBodyRef<'a> { + fn from(body: &'a FunctionBody) -> Self { + FunctionBodyRef { + body: &*body.body, + unwind_info: body.unwind_info.as_ref().map(Into::into), + } + } +} + +impl<'a> From<&'a ArchivedFunctionBody> for FunctionBodyRef<'a> { + fn from(body: &'a ArchivedFunctionBody) -> Self { + FunctionBodyRef { + body: &*body.body, + unwind_info: body.unwind_info.as_ref().map(Into::into), + } + } +} + /// The result of compiling a WebAssembly function. /// /// This structure only have the compiled information data /// (function bytecode body, relocations, traps, jump tables /// and unwind information). #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] pub struct CompiledFunction { /// The function body. pub body: FunctionBody, @@ -90,11 +106,7 @@ pub type CustomSections = PrimaryMap; /// In the future this structure may also hold other information useful /// for debugging. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, PartialEq, Eq, Clone, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone)] pub struct Dwarf { /// The section index in the [`Compilation`] that corresponds to the exception frames. /// [Learn @@ -111,11 +123,7 @@ impl Dwarf { /// Trampolines section used by ARM short jump (26bits) #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, PartialEq, Eq, Clone, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone)] pub struct TrampolinesSection { /// SectionIndex for the actual Trampolines code pub section_index: SectionIndex, diff --git a/lib/compiler/src/jump_table.rs b/lib/compiler/src/jump_table.rs index 5fc7551bb6..2e7d5705c9 100644 --- a/lib/compiler/src/jump_table.rs +++ b/lib/compiler/src/jump_table.rs @@ -5,8 +5,6 @@ //! [Learn more](https://en.wikipedia.org/wiki/Branch_table). use super::CodeOffset; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -17,14 +15,13 @@ use wasmer_types::entity::{entity_impl, SecondaryMap}; /// `JumpTable`s are used for indirect branching and are specialized for dense, /// 0-based jump offsets. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct JumpTable(u32); entity_impl!(JumpTable, "jt"); +entity_impl!(ArchivedJumpTable, "jt"); impl JumpTable { /// Create a new jump table reference from its number. diff --git a/lib/compiler/src/lib.rs b/lib/compiler/src/lib.rs index 27669f27c2..766e18b634 100644 --- a/lib/compiler/src/lib.rs +++ b/lib/compiler/src/lib.rs @@ -74,12 +74,14 @@ pub use crate::error::{ }; pub use crate::function::{ Compilation, CompiledFunction, CompiledFunctionFrameInfo, CustomSections, Dwarf, FunctionBody, - Functions, TrampolinesSection, + FunctionBodyRef, Functions, TrampolinesSection, }; pub use crate::jump_table::{JumpTable, JumpTableOffsets}; pub use crate::module::CompileModuleInfo; pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations}; -pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex}; +pub use crate::section::{ + CustomSection, CustomSectionProtection, CustomSectionRef, SectionBody, SectionIndex, +}; pub use crate::sourceloc::SourceLoc; pub use crate::target::{ Architecture, BinaryFormat, CallingConvention, CpuFeature, Endianness, OperatingSystem, @@ -92,7 +94,7 @@ pub use crate::translator::{ ModuleMiddlewareChain, ModuleTranslationState, }; pub use crate::trap::TrapInformation; -pub use crate::unwind::CompiledFunctionUnwindInfo; +pub use crate::unwind::{CompiledFunctionUnwindInfo, CompiledFunctionUnwindInfoRef}; pub use wasmer_types::Features; diff --git a/lib/compiler/src/module.rs b/lib/compiler/src/module.rs index 6e9c5ce279..ca0b77607b 100644 --- a/lib/compiler/src/module.rs +++ b/lib/compiler/src/module.rs @@ -1,6 +1,4 @@ use crate::lib::std::sync::Arc; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -13,12 +11,9 @@ use wasmer_vm::{MemoryStyle, TableStyle}; /// This differs from [`ModuleInfo`] because it have extra info only /// possible after translation (such as the features used for compiling, /// or the `MemoryStyle` and `TableStyle`). -#[derive(Debug, MemoryUsage, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] pub struct CompileModuleInfo { /// The features used for compiling the module pub features: Features, diff --git a/lib/compiler/src/relocation.rs b/lib/compiler/src/relocation.rs index 8e73d8c34a..208b09b439 100644 --- a/lib/compiler/src/relocation.rs +++ b/lib/compiler/src/relocation.rs @@ -13,8 +13,6 @@ use crate::lib::std::fmt; use crate::lib::std::vec::Vec; use crate::section::SectionIndex; use crate::{Addend, CodeOffset, JumpTable}; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -24,11 +22,7 @@ use wasmer_vm::libcalls::LibCall; /// Relocation kinds for every ISA. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Copy, Clone, Debug, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Copy, Clone, Debug, PartialEq, Eq)] pub enum RelocationKind { /// absolute 4-byte Abs4, @@ -92,11 +86,7 @@ impl fmt::Display for RelocationKind { /// A record of a relocation to perform. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] pub struct Relocation { /// The relocation kind. pub kind: RelocationKind, @@ -110,11 +100,7 @@ pub struct Relocation { /// Destination function. Can be either user function or some special one, like `memory.grow`. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq)] pub enum RelocationTarget { /// A relocation to a function defined locally in the wasm (not an imported one). LocalFunc(LocalFunctionIndex), diff --git a/lib/compiler/src/section.rs b/lib/compiler/src/section.rs index 1ac45eeda4..e7709485d7 100644 --- a/lib/compiler/src/section.rs +++ b/lib/compiler/src/section.rs @@ -7,8 +7,6 @@ use crate::lib::std::vec::Vec; use crate::Relocation; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -16,31 +14,20 @@ use wasmer_types::entity::entity_impl; /// Index type of a Section defined inside a WebAssembly `Compilation`. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub struct SectionIndex(u32); entity_impl!(SectionIndex); -#[cfg(feature = "enable-rkyv")] entity_impl!(ArchivedSectionIndex); /// Custom section Protection. /// /// Determines how a custom section may be used. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq)] pub enum CustomSectionProtection { /// A custom section with read permission. Read, @@ -54,11 +41,7 @@ pub enum CustomSectionProtection { /// This is used so compilers can store arbitrary information /// in the emitted module. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] pub struct CustomSection { /// Memory protection that applies to this section. pub protection: CustomSectionProtection, @@ -75,13 +58,41 @@ pub struct CustomSection { pub relocations: Vec, } +/// See [`CustomSection`]. +/// +/// Note that this does not reference the relocation data. +#[derive(Clone, Copy)] +pub struct CustomSectionRef<'a> { + /// See [`CustomSection::protection`]. + pub protection: CustomSectionProtection, + + /// See [`CustomSection::bytes`]. + pub bytes: &'a [u8], +} + +impl<'a> From<&'a CustomSection> for CustomSectionRef<'a> { + fn from(section: &'a CustomSection) -> Self { + CustomSectionRef { + protection: section.protection.clone(), + bytes: section.bytes.as_slice(), + } + } +} + +impl<'a> From<&'a ArchivedCustomSection> for CustomSectionRef<'a> { + fn from(section: &'a ArchivedCustomSection) -> Self { + CustomSectionRef { + protection: Result::<_, std::convert::Infallible>::unwrap( + rkyv::Deserialize::deserialize(§ion.protection, &mut rkyv::Infallible), + ), + bytes: §ion.bytes.0[..], + } + } +} + /// The bytes in the section. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, Default, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)] pub struct SectionBody(#[cfg_attr(feature = "enable-serde", serde(with = "serde_bytes"))] Vec); impl SectionBody { diff --git a/lib/compiler/src/sourceloc.rs b/lib/compiler/src/sourceloc.rs index 5874cd4f4d..7117639871 100644 --- a/lib/compiler/src/sourceloc.rs +++ b/lib/compiler/src/sourceloc.rs @@ -8,8 +8,6 @@ //! and tracing errors. use crate::lib::std::fmt; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -23,12 +21,9 @@ use serde::{Deserialize, Serialize}; derive(Serialize, Deserialize), serde(transparent) )] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] #[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, MemoryUsage)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SourceLoc(u32); impl SourceLoc { diff --git a/lib/compiler/src/target.rs b/lib/compiler/src/target.rs index 12b2b06f1c..9d18d79e87 100644 --- a/lib/compiler/src/target.rs +++ b/lib/compiler/src/target.rs @@ -3,7 +3,6 @@ use crate::error::ParseCpuFeatureError; use crate::lib::std::str::FromStr; use crate::lib::std::string::{String, ToString}; use enumset::{EnumSet, EnumSetType}; -use loupe::MemoryUsage; pub use target_lexicon::{ Architecture, BinaryFormat, CallingConvention, Endianness, OperatingSystem, PointerWidth, Triple, @@ -161,11 +160,9 @@ impl ToString for CpuFeature { /// This is the target that we will use for compiling /// the WebAssembly ModuleInfo, and then run it. -#[derive(Clone, Debug, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Target { - #[loupe(skip)] triple: Triple, - #[loupe(skip)] cpu_features: EnumSet, } diff --git a/lib/compiler/src/translator/environ.rs b/lib/compiler/src/translator/environ.rs index 73829bad89..9a56a3247d 100644 --- a/lib/compiler/src/translator/environ.rs +++ b/lib/compiler/src/translator/environ.rs @@ -14,8 +14,8 @@ use wasmer_types::FunctionType; use wasmer_types::{ CustomSectionIndex, DataIndex, DataInitializer, DataInitializerLocation, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, GlobalType, ImportIndex, - LocalFunctionIndex, MemoryIndex, MemoryType, ModuleInfo, SignatureIndex, TableIndex, - TableInitializer, TableType, + LocalFunctionIndex, MemoryIndex, MemoryType, ModuleInfo, OwnedTableInitializer, SignatureIndex, + TableIndex, TableType, }; /// Contains function data: bytecode and its offset in the module. @@ -136,18 +136,16 @@ impl<'data> ModuleEnvironment<'data> { ) -> WasmResult<()> { debug_assert_eq!( self.module.functions.len(), - self.module.num_imported_functions, + self.module.import_counts.functions as usize, "Imported functions must be declared first" ); self.declare_import( - ImportIndex::Function(FunctionIndex::from_u32( - self.module.num_imported_functions as _, - )), + ImportIndex::Function(FunctionIndex::from_u32(self.module.import_counts.functions)), module, field, )?; self.module.functions.push(sig_index); - self.module.num_imported_functions += 1; + self.module.import_counts.functions += 1; Ok(()) } @@ -159,16 +157,16 @@ impl<'data> ModuleEnvironment<'data> { ) -> WasmResult<()> { debug_assert_eq!( self.module.tables.len(), - self.module.num_imported_tables, + self.module.import_counts.tables as usize, "Imported tables must be declared first" ); self.declare_import( - ImportIndex::Table(TableIndex::from_u32(self.module.num_imported_tables as _)), + ImportIndex::Table(TableIndex::from_u32(self.module.import_counts.tables)), module, field, )?; self.module.tables.push(table); - self.module.num_imported_tables += 1; + self.module.import_counts.tables += 1; Ok(()) } @@ -180,18 +178,16 @@ impl<'data> ModuleEnvironment<'data> { ) -> WasmResult<()> { debug_assert_eq!( self.module.memories.len(), - self.module.num_imported_memories, + self.module.import_counts.memories as usize, "Imported memories must be declared first" ); self.declare_import( - ImportIndex::Memory(MemoryIndex::from_u32( - self.module.num_imported_memories as _, - )), + ImportIndex::Memory(MemoryIndex::from_u32(self.module.import_counts.memories)), module, field, )?; self.module.memories.push(memory); - self.module.num_imported_memories += 1; + self.module.import_counts.memories += 1; Ok(()) } @@ -203,16 +199,16 @@ impl<'data> ModuleEnvironment<'data> { ) -> WasmResult<()> { debug_assert_eq!( self.module.globals.len(), - self.module.num_imported_globals, + self.module.import_counts.globals as usize, "Imported globals must be declared first" ); self.declare_import( - ImportIndex::Global(GlobalIndex::from_u32(self.module.num_imported_globals as _)), + ImportIndex::Global(GlobalIndex::from_u32(self.module.import_counts.globals)), module, field, )?; self.module.globals.push(global); - self.module.num_imported_globals += 1; + self.module.import_counts.globals += 1; Ok(()) } @@ -337,7 +333,7 @@ impl<'data> ModuleEnvironment<'data> { offset: usize, elements: Box<[FunctionIndex]>, ) -> WasmResult<()> { - self.module.table_initializers.push(TableInitializer { + self.module.table_initializers.push(OwnedTableInitializer { table_index, base, offset, @@ -397,9 +393,8 @@ impl<'data> ModuleEnvironment<'data> { Ok(()) } - pub(crate) fn reserve_passive_data(&mut self, count: u32) -> WasmResult<()> { - let count = usize::try_from(count).unwrap(); - self.module.passive_data.reserve(count); + pub(crate) fn reserve_passive_data(&mut self, _count: u32) -> WasmResult<()> { + // TODO(0-copy): consider finding a more appropriate data structure for this? Ok(()) } diff --git a/lib/compiler/src/translator/middleware.rs b/lib/compiler/src/translator/middleware.rs index 957cf93264..b12541980f 100644 --- a/lib/compiler/src/translator/middleware.rs +++ b/lib/compiler/src/translator/middleware.rs @@ -1,7 +1,6 @@ //! The middleware parses the function binary bytecodes and transform them //! with the chosen functions. -use loupe::MemoryUsage; use smallvec::SmallVec; use std::collections::VecDeque; use std::fmt::Debug; @@ -13,7 +12,7 @@ use crate::error::{MiddlewareError, WasmResult}; use crate::translator::environ::FunctionBinaryReader; /// A shared builder for function middlewares. -pub trait ModuleMiddleware: Debug + Send + Sync + MemoryUsage { +pub trait ModuleMiddleware: Debug + Send + Sync { /// Generates a `FunctionMiddleware` for a given function. /// /// Here we generate a separate object for each function instead of executing directly on per-function operators, diff --git a/lib/compiler/src/translator/sections.rs b/lib/compiler/src/translator/sections.rs index faac992543..9759c2d510 100644 --- a/lib/compiler/src/translator/sections.rs +++ b/lib/compiler/src/translator/sections.rs @@ -22,7 +22,7 @@ use wasmer_types::entity::packed_option::ReservedValue; use wasmer_types::entity::EntityRef; use wasmer_types::{ DataIndex, ElemIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalInit, GlobalType, - MemoryIndex, MemoryType, Pages, SignatureIndex, TableIndex, TableType, Type, V128, + MemoryIndex, MemoryType, Mutability, Pages, SignatureIndex, TableIndex, TableType, Type, V128, }; use wasmparser::{ self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind, @@ -131,7 +131,11 @@ pub fn parse_import_section<'data>( environ.declare_global_import( GlobalType { ty: wptype_to_type(ty.content_type).unwrap(), - mutability: ty.mutable.into(), + mutability: if ty.mutable { + Mutability::Var + } else { + Mutability::Const + }, }, module_name, field_name.unwrap_or_default(), @@ -257,7 +261,11 @@ pub fn parse_global_section( }; let global = GlobalType { ty: wptype_to_type(content_type).unwrap(), - mutability: mutable.into(), + mutability: if mutable { + Mutability::Var + } else { + Mutability::Const + }, }; environ.declare_global(global, initializer)?; } diff --git a/lib/compiler/src/trap.rs b/lib/compiler/src/trap.rs index 2d016c1312..b2545bbc70 100644 --- a/lib/compiler/src/trap.rs +++ b/lib/compiler/src/trap.rs @@ -1,6 +1,4 @@ use crate::CodeOffset; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -8,11 +6,7 @@ use wasmer_vm::TrapCode; /// Information about trap. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Clone, Debug, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Clone, Debug, PartialEq, Eq)] pub struct TrapInformation { /// The offset of the trapping instruction in native code. It is relative to the beginning of the function. pub code_offset: CodeOffset, diff --git a/lib/compiler/src/unwind.rs b/lib/compiler/src/unwind.rs index ec344a9c15..6d41e5be77 100644 --- a/lib/compiler/src/unwind.rs +++ b/lib/compiler/src/unwind.rs @@ -6,8 +6,6 @@ //! //! [Learn more](https://en.wikipedia.org/wiki/Call_stack). use crate::lib::std::vec::Vec; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -20,11 +18,7 @@ use serde::{Deserialize, Serialize}; /// /// [unwind info]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2019 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive(Debug, Clone, PartialEq, Eq, MemoryUsage)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] pub enum CompiledFunctionUnwindInfo { /// Windows UNWIND_INFO. WindowsX64(Vec), @@ -32,3 +26,34 @@ pub enum CompiledFunctionUnwindInfo { /// The unwind info is added to the Dwarf section in `Compilation`. Dwarf, } + +/// See [`CompiledFunctionUnwindInfo`]. +#[derive(Clone, Copy)] +pub enum CompiledFunctionUnwindInfoRef<'a> { + /// Windows UNWIND_INFO. + WindowsX64(&'a [u8]), + /// Unwind info is added to the Dwarf section in `Compilation`. + Dwarf, +} + +impl<'a> From<&'a CompiledFunctionUnwindInfo> for CompiledFunctionUnwindInfoRef<'a> { + fn from(uw: &'a CompiledFunctionUnwindInfo) -> Self { + match uw { + CompiledFunctionUnwindInfo::WindowsX64(d) => { + CompiledFunctionUnwindInfoRef::WindowsX64(d) + } + CompiledFunctionUnwindInfo::Dwarf => CompiledFunctionUnwindInfoRef::Dwarf, + } + } +} + +impl<'a> From<&'a ArchivedCompiledFunctionUnwindInfo> for CompiledFunctionUnwindInfoRef<'a> { + fn from(uw: &'a ArchivedCompiledFunctionUnwindInfo) -> Self { + match uw { + ArchivedCompiledFunctionUnwindInfo::WindowsX64(d) => { + CompiledFunctionUnwindInfoRef::WindowsX64(d) + } + ArchivedCompiledFunctionUnwindInfo::Dwarf => CompiledFunctionUnwindInfoRef::Dwarf, + } + } +} diff --git a/lib/derive/src/lib.rs b/lib/derive/src/lib.rs index 63422408c1..445315df12 100644 --- a/lib/derive/src/lib.rs +++ b/lib/derive/src/lib.rs @@ -136,12 +136,12 @@ fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) { identifier.unwrap_or_else(|| LitStr::new(&name_str, name.span())); let mut access_expr = quote_spanned! { f.span() => - instance.exports.get_with_generics_weak::<#inner_type, _, _>(#item_name) + instance.get_with_generics_weak::<#inner_type, _, _>(#item_name) }; for alias in aliases { access_expr = quote_spanned! { f.span()=> - #access_expr .or_else(|_| instance.exports.get_with_generics_weak::<#inner_type, _, _>(#alias)) + #access_expr .or_else(|_| instance.get_with_generics_weak::<#inner_type, _, _>(#alias)) }; } if optional { @@ -159,43 +159,41 @@ fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) { self.#name.initialize(#name); } } - } else { - if let Some(identifier) = identifier { - let mut access_expr = quote_spanned! { - f.span() => - instance.exports.get_with_generics_weak::<#inner_type, _, _>(#identifier) + } else if let Some(identifier) = identifier { + let mut access_expr = quote_spanned! { + f.span() => + instance.get_with_generics_weak::<#inner_type, _, _>(#identifier) + }; + for alias in aliases { + access_expr = quote_spanned! { + f.span()=> + #access_expr .or_else(|_| instance.get_with_generics_weak::<#inner_type, _, _>(#alias)) }; - for alias in aliases { - access_expr = quote_spanned! { - f.span()=> - #access_expr .or_else(|_| instance.exports.get_with_generics_weak::<#inner_type, _, _>(#alias)) - }; - } - let local_var = - Ident::new(&format!("field_{}", field_num), identifier.span()); - if optional { - quote_spanned! { - f.span()=> - match #access_expr { - Ok(#local_var) => { - self.#field_idx.initialize(#local_var); - }, - Err(_) => (), - } - } - } else { - quote_spanned! { - f.span()=> - let #local_var: #inner_type = #access_expr?; - self.#field_idx.initialize(#local_var); - } + } + let local_var = + Ident::new(&format!("field_{}", field_num), identifier.span()); + if optional { + quote_spanned! { + f.span()=> + match #access_expr { + Ok(#local_var) => { + self.#field_idx.initialize(#local_var); + }, + Err(_) => (), + } } } else { - abort!( - span, - "Expected `name` field on export attribute because field does not have a name. For example: `#[wasmer(export(name = \"wasm_ident\"))]`.", - ); + quote_spanned! { + f.span()=> + let #local_var: #inner_type = #access_expr?; + self.#field_idx.initialize(#local_var); + } } + } else { + abort!( + span, + "Expected `name` field on export attribute because field does not have a name. For example: `#[wasmer(export(name = \"wasm_ident\"))]`.", + ); }; finish.push(finish_tokens); diff --git a/lib/engine-universal/Cargo.toml b/lib/engine-universal/Cargo.toml index d037662734..e687afdb00 100644 --- a/lib/engine-universal/Cargo.toml +++ b/lib/engine-universal/Cargo.toml @@ -14,17 +14,17 @@ edition = "2018" name = "wasmer_engine_universal" [dependencies] -wasmer-types = { path = "../types", version = "=2.2.2", package = "wasmer-types-near", features = ["enable-rkyv"] } -wasmer-compiler = { path = "../compiler", version = "=2.2.2", package = "wasmer-compiler-near", features = ["translator", "enable-rkyv"] } -wasmer-vm = { path = "../vm", version = "=2.2.2", package = "wasmer-vm-near", features = ["enable-rkyv"] } +wasmer-types = { path = "../types", version = "=2.2.2", package = "wasmer-types-near" } +wasmer-compiler = { path = "../compiler", version = "=2.2.2", package = "wasmer-compiler-near", features = ["translator"] } +wasmer-vm = { path = "../vm", version = "=2.2.2", package = "wasmer-vm-near" } wasmer-engine = { path = "../engine", package = "wasmer-engine-near", version = "=2.2.2" } # flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" } region = "3.0" cfg-if = "1.0" leb128 = "0.2" -rkyv = "0.7.20" -loupe = "0.1" +rkyv = "0.7.31" enumset = "1.0" +thiserror = "1" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winnt", "impl-default"] } diff --git a/lib/engine-universal/src/artifact.rs b/lib/engine-universal/src/artifact.rs index 2a21194710..9e03cf9daf 100644 --- a/lib/engine-universal/src/artifact.rs +++ b/lib/engine-universal/src/artifact.rs @@ -1,402 +1,189 @@ //! Define `UniversalArtifact` to allow compiling and instantiating to be //! done as separate steps. -use crate::engine::{UniversalEngine, UniversalEngineInner}; -use crate::link::link_module; -#[cfg(feature = "compiler")] -use crate::serialize::SerializableCompilation; -use crate::serialize::SerializableModule; -use enumset::EnumSet; -use loupe::MemoryUsage; -use std::sync::{Arc, Mutex}; -use wasmer_compiler::{CompileError, CpuFeature, Features, Triple}; -#[cfg(feature = "compiler")] -use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment, ModuleMiddlewareChain}; -use wasmer_engine::{ - register_frame_info, Artifact, DeserializeError, FunctionExtent, GlobalFrameInfoRegistration, - SerializeError, -}; -#[cfg(feature = "compiler")] -use wasmer_engine::{Engine, Tunables}; -use wasmer_types::entity::{BoxedSlice, PrimaryMap}; +use std::collections::BTreeMap; +use std::convert::TryFrom; +use std::sync::Arc; +use wasmer_engine::{InstantiationError, RuntimeError}; +use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ - FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer, - SignatureIndex, TableIndex, + DataIndex, ElemIndex, FunctionIndex, GlobalInit, GlobalType, ImportCounts, LocalFunctionIndex, + LocalGlobalIndex, MemoryType, OwnedDataInitializer, OwnedTableInitializer, SignatureIndex, + TableType, }; use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex, - VMTrampoline, + Artifact, FunctionBodyPtr, FunctionExtent, InstanceHandle, MemoryStyle, Resolver, TableStyle, + Tunables, VMImport, VMImportType, VMLocalFunction, VMOffsets, VMSharedSignatureIndex, }; -const SERIALIZED_METADATA_LENGTH_OFFSET: usize = 22; -const SERIALIZED_METADATA_CONTENT_OFFSET: usize = 32; - -/// A compiled wasm module, ready to be instantiated. -#[derive(MemoryUsage)] +/// A compiled wasm module, containing everything necessary for instantiation. pub struct UniversalArtifact { - serializable: SerializableModule, - finished_functions: BoxedSlice, - #[loupe(skip)] - finished_function_call_trampolines: BoxedSlice, - finished_dynamic_function_trampolines: BoxedSlice, - signatures: BoxedSlice, - func_data_registry: Arc, - frame_info_registration: Mutex>, - finished_function_lengths: BoxedSlice, + // TODO: figure out how to allocate fewer distinct structures onto heap. Maybe have an arena…? + pub(crate) engine: crate::UniversalEngine, + pub(crate) import_counts: ImportCounts, + pub(crate) start_function: Option, + pub(crate) vmoffsets: VMOffsets, + pub(crate) imports: Vec, + pub(crate) dynamic_function_trampolines: BoxedSlice, + pub(crate) functions: BoxedSlice, + pub(crate) exports: BTreeMap, + pub(crate) signatures: BoxedSlice, + pub(crate) local_memories: Vec<(MemoryType, MemoryStyle)>, + pub(crate) data_segments: Vec, + pub(crate) passive_data: BTreeMap>, + pub(crate) local_tables: Vec<(TableType, TableStyle)>, + pub(crate) element_segments: Vec, + // TODO: does this need to be a BTreeMap? Can it be a plain vector? + pub(crate) passive_elements: BTreeMap>, + pub(crate) local_globals: Vec<(GlobalType, GlobalInit)>, } impl UniversalArtifact { - const MAGIC_HEADER: &'static [u8; 22] = b"\0wasmer-universal\0\0\0\0\0"; - - /// Check if the provided bytes look like a serialized `UniversalArtifact`. - pub fn is_deserializable(bytes: &[u8]) -> bool { - bytes.starts_with(Self::MAGIC_HEADER) - } - - /// Compile a data buffer into a `UniversalArtifact`, which may then be instantiated. - #[cfg(feature = "compiler")] - pub fn new( - engine: &UniversalEngine, - data: &[u8], - tunables: &dyn Tunables, - ) -> Result { - let environ = ModuleEnvironment::new(); - let mut inner_engine = engine.inner_mut(); - let features = inner_engine.features(); - - let translation = environ.translate(data).map_err(CompileError::Wasm)?; - - let compiler = inner_engine.compiler()?; - - // We try to apply the middleware first - let mut module = translation.module; - let middlewares = compiler.get_middlewares(); - middlewares.apply_on_module_info(&mut module); - - let memory_styles: PrimaryMap = module - .memories - .values() - .map(|memory_type| tunables.memory_style(memory_type)) - .collect(); - let table_styles: PrimaryMap = module - .tables - .values() - .map(|table_type| tunables.table_style(table_type)) - .collect(); - - let mut compile_info = CompileModuleInfo { - module: Arc::new(module), - features: features.clone(), - memory_styles, - table_styles, - }; - - // Ensure that we pass information about signals in module metadata. - compile_info.features.signal_less(!compiler.use_signals()); - - // Compile the Module - let compilation = compiler.compile_module( - &engine.target(), - &compile_info, - // SAFETY: Calling `unwrap` is correct since - // `environ.translate()` above will write some data into - // `module_translation_state`. - translation.module_translation_state.as_ref().unwrap(), - translation.function_body_inputs, - )?; - let function_call_trampolines = compilation.get_function_call_trampolines(); - let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); - - let data_initializers = translation - .data_initializers - .iter() - .map(OwnedDataInitializer::new) - .collect::>() - .into_boxed_slice(); - - let frame_infos = compilation.get_frame_info(); - - let serializable_compilation = SerializableCompilation { - function_bodies: compilation.get_function_bodies(), - function_relocations: compilation.get_relocations(), - function_jt_offsets: compilation.get_jt_offsets(), - function_frame_info: frame_infos, - function_call_trampolines, - dynamic_function_trampolines, - custom_sections: compilation.get_custom_sections(), - custom_section_relocations: compilation.get_custom_section_relocations(), - debug: compilation.get_debug(), - trampolines: compilation.get_trampolines(), - }; - let serializable = SerializableModule { - compilation: serializable_compilation, - compile_info, - data_initializers, - cpu_features: engine.target().cpu_features().as_u64(), - }; - Self::from_parts(&mut inner_engine, serializable) - } - - /// Compile a data buffer into a `UniversalArtifact`, which may then be instantiated. - #[cfg(not(feature = "compiler"))] - pub fn new(_engine: &UniversalEngine, _data: &[u8]) -> Result { - Err(CompileError::Codegen( - "Compilation is not enabled in the engine".to_string(), - )) - } - - /// Deserialize a UniversalArtifact - /// - /// # Safety - /// This function is unsafe because rkyv reads directly without validating - /// the data. - pub unsafe fn deserialize( - universal: &UniversalEngine, - bytes: &[u8], - ) -> Result { - if !Self::is_deserializable(bytes) { - return Err(DeserializeError::Incompatible( - "The provided bytes are not wasmer-universal".to_string(), - )); - } - - let mut inner_bytes = &bytes[SERIALIZED_METADATA_LENGTH_OFFSET..]; - - let metadata_len = leb128::read::unsigned(&mut inner_bytes).map_err(|_e| { - DeserializeError::CorruptedBinary("Can't read metadata size".to_string()) - })?; - let metadata_slice: &[u8] = std::slice::from_raw_parts( - &bytes[SERIALIZED_METADATA_CONTENT_OFFSET] as *const u8, - metadata_len as usize, - ); - - let serializable = SerializableModule::deserialize(metadata_slice)?; - Self::from_parts(&mut universal.inner_mut(), serializable) - .map_err(DeserializeError::Compiler) - } - - /// Construct a `UniversalArtifact` from component parts. - pub fn from_parts( - inner_engine: &mut UniversalEngineInner, - serializable: SerializableModule, - ) -> Result { - let ( - finished_functions, - finished_function_call_trampolines, - finished_dynamic_function_trampolines, - custom_sections, - ) = inner_engine.allocate( - &serializable.compile_info.module, - &serializable.compilation.function_bodies, - &serializable.compilation.function_call_trampolines, - &serializable.compilation.dynamic_function_trampolines, - &serializable.compilation.custom_sections, - )?; - - link_module( - &serializable.compile_info.module, - &finished_functions, - &serializable.compilation.function_jt_offsets, - serializable.compilation.function_relocations.clone(), - &custom_sections, - &serializable.compilation.custom_section_relocations, - &serializable.compilation.trampolines, - ); - - // Compute indices into the shared signature table. - let signatures = { - let signature_registry = inner_engine.signatures(); - serializable - .compile_info - .module - .signatures - .values() - .map(|sig| signature_registry.register(sig)) - .collect::>() - }; - - let eh_frame = match &serializable.compilation.debug { - Some(debug) => { - let eh_frame_section_size = serializable.compilation.custom_sections - [debug.eh_frame] - .bytes - .len(); - let eh_frame_section_pointer = custom_sections[debug.eh_frame]; - Some(unsafe { - std::slice::from_raw_parts(*eh_frame_section_pointer, eh_frame_section_size) - }) - } - None => None, - }; - - // Make all code compiled thus far executable. - inner_engine.publish_compiled_code(); - - inner_engine.publish_eh_frame(eh_frame)?; - - let finished_function_lengths = finished_functions - .values() - .map(|extent| extent.length) - .collect::>() - .into_boxed_slice(); - let finished_functions = finished_functions - .values() - .map(|extent| extent.ptr) - .collect::>() - .into_boxed_slice(); - let finished_function_call_trampolines = - finished_function_call_trampolines.into_boxed_slice(); - let finished_dynamic_function_trampolines = - finished_dynamic_function_trampolines.into_boxed_slice(); - let signatures = signatures.into_boxed_slice(); - let func_data_registry = inner_engine.func_data().clone(); - - Ok(Self { - serializable, - finished_functions, - finished_function_call_trampolines, - finished_dynamic_function_trampolines, - signatures, - frame_info_registration: Mutex::new(None), - finished_function_lengths, - func_data_registry, + /// Return the extents of the specified local function. + pub fn function_extent(&self, index: LocalFunctionIndex) -> Option { + let func = self.functions.get(index)?; + Some(FunctionExtent { + address: func.body, + length: usize::try_from(func.length).unwrap(), }) } - /// Get the default extension when serializing this artifact - pub fn get_default_extension(_triple: &Triple) -> &'static str { - // `.wasmu` is the default extension for all the triples. It - // stands for “Wasm Universal”. - "wasmu" + /// Return the engine instance this artifact is loaded into. + pub fn engine(&self) -> &crate::UniversalEngine { + &self.engine } } impl Artifact for UniversalArtifact { - fn module(&self) -> Arc { - self.serializable.compile_info.module.clone() - } - - fn module_ref(&self) -> &ModuleInfo { - &self.serializable.compile_info.module - } - - fn module_mut(&mut self) -> Option<&mut ModuleInfo> { - Arc::get_mut(&mut self.serializable.compile_info.module) - } - - fn register_frame_info(&self) { - let mut info = self.frame_info_registration.lock().unwrap(); + unsafe fn instantiate( + self: Arc, + tunables: &dyn Tunables, + resolver: &dyn Resolver, + host_state: Box, + config: wasmer_types::InstanceConfig, + ) -> Result> { + let (imports, import_function_envs) = { + let mut imports = wasmer_engine::resolve_imports( + &self.engine, + resolver, + &self.import_counts, + &self.imports, + &self.dynamic_function_trampolines, + ) + .map_err(InstantiationError::Link)?; + + // Get the `WasmerEnv::init_with_instance` function pointers and the pointers + // to the envs to call it on. + let import_function_envs = imports.get_imported_function_envs(); + + (imports, import_function_envs) + }; - if info.is_some() { - return; + let (allocator, memory_definition_locations, table_definition_locations) = + wasmer_vm::InstanceAllocator::new(self.vmoffsets.clone()); + + // Memories + let mut memories: PrimaryMap = + PrimaryMap::with_capacity(self.local_memories.len()); + for (idx, (ty, style)) in (self.import_counts.memories..).zip(self.local_memories.iter()) { + let memory = tunables + .create_vm_memory(&ty, &style, memory_definition_locations[idx as usize]) + .map_err(|e| { + InstantiationError::Link(wasmer_engine::LinkError::Resource(format!( + "Failed to create memory: {}", + e + ))) + })?; + memories.push(memory); } - let finished_function_extents = self - .finished_functions - .values() - .copied() - .zip(self.finished_function_lengths.values().copied()) - .map(|(ptr, length)| FunctionExtent { ptr, length }) - .collect::>() - .into_boxed_slice(); - - let frame_infos = &self.serializable.compilation.function_frame_info; - *info = register_frame_info( - self.serializable.compile_info.module.clone(), - &finished_function_extents, - frame_infos.clone(), - ); - } + // Tables + let mut tables: PrimaryMap = + PrimaryMap::with_capacity(self.local_tables.len()); + for (idx, (ty, style)) in (self.import_counts.tables..).zip(self.local_tables.iter()) { + let table = tunables + .create_vm_table(ty, style, table_definition_locations[idx as usize]) + .map_err(|e| InstantiationError::Link(wasmer_engine::LinkError::Resource(e)))?; + tables.push(table); + } - fn features(&self) -> &Features { - &self.serializable.compile_info.features - } + // Globals + let mut globals = + PrimaryMap::::with_capacity(self.local_globals.len()); + for (ty, _) in self.local_globals.iter() { + globals.push(Arc::new(wasmer_vm::Global::new(*ty))); + } - fn cpu_features(&self) -> EnumSet { - EnumSet::from_u64(self.serializable.cpu_features) + let passive_data = self.passive_data.clone(); + Ok(InstanceHandle::new( + self, + allocator, + memories.into_boxed_slice(), + tables.into_boxed_slice(), + globals.into_boxed_slice(), + imports, + passive_data, + host_state, + import_function_envs, + config, + ) + .map_err(|t| InstantiationError::CreateInstance(RuntimeError::from_trap(t)))?) } - fn data_initializers(&self) -> &[OwnedDataInitializer] { - &*self.serializable.data_initializers + fn offsets(&self) -> &wasmer_vm::VMOffsets { + &self.vmoffsets } - fn memory_styles(&self) -> &PrimaryMap { - &self.serializable.compile_info.memory_styles + fn import_counts(&self) -> &ImportCounts { + &self.import_counts } - fn table_styles(&self) -> &PrimaryMap { - &self.serializable.compile_info.table_styles + fn functions(&self) -> &BoxedSlice { + &self.functions } - fn finished_functions(&self) -> &BoxedSlice { - &self.finished_functions + fn passive_elements(&self) -> &BTreeMap> { + &self.passive_elements } - fn finished_functions_lengths(&self) -> &BoxedSlice { - &self.finished_function_lengths + fn element_segments(&self) -> &[OwnedTableInitializer] { + &self.element_segments[..] } - fn finished_function_call_trampolines(&self) -> &BoxedSlice { - &self.finished_function_call_trampolines + fn data_segments(&self) -> &[OwnedDataInitializer] { + &self.data_segments[..] } - fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice { - &self.finished_dynamic_function_trampolines + fn globals(&self) -> &[(GlobalType, GlobalInit)] { + &self.local_globals[..] } - fn signatures(&self) -> &BoxedSlice { - &self.signatures + fn start_function(&self) -> Option { + self.start_function } - fn func_data_registry(&self) -> &FuncDataRegistry { - &self.func_data_registry + fn export_field(&self, name: &str) -> Option { + self.exports.get(name).cloned() } - fn serialize(&self) -> Result, SerializeError> { - // Prepend the header. - let mut serialized = Self::MAGIC_HEADER.to_vec(); - - serialized.resize(SERIALIZED_METADATA_CONTENT_OFFSET, 0); - let mut writable_leb = &mut serialized[SERIALIZED_METADATA_LENGTH_OFFSET..]; - let serialized_data = self.serializable.serialize()?; - let length = serialized_data.len(); - leb128::write::unsigned(&mut writable_leb, length as u64).expect("Should write number"); - let offset = pad_and_extend::(&mut serialized, &serialized_data); - assert_eq!(offset, SERIALIZED_METADATA_CONTENT_OFFSET); - - Ok(serialized) + fn signatures(&self) -> &[wasmer_vm::VMSharedSignatureIndex] { + self.signatures.values().as_slice() } -} -/// It pads the data with the desired alignment -pub fn pad_and_extend(prev_data: &mut Vec, data: &[u8]) -> usize { - let align = std::mem::align_of::(); - - let mut offset = prev_data.len(); - if offset & (align - 1) != 0 { - offset += align - (offset & (align - 1)); - prev_data.resize(offset, 0); - } - prev_data.extend(data); - offset -} - -#[cfg(test)] -mod tests { - use super::pad_and_extend; - - #[test] - fn test_pad_and_extend() { - let mut data: Vec = vec![]; - let offset = pad_and_extend::(&mut data, &[1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(offset, 0); - let offset = pad_and_extend::(&mut data, &[2, 0, 0, 0]); - assert_eq!(offset, 8); - let offset = pad_and_extend::(&mut data, &[3, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(offset, 16); - assert_eq!( - data, - &[1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] - ); + fn function_signature(&self, index: FunctionIndex) -> Option { + match self.import_counts().local_function_index(index) { + Ok(local) => Some(self.functions[local].signature), + Err(import) => self + .imports + .iter() + .filter_map(|im| { + if let VMImportType::Function(idx) = im.ty { + Some(idx) + } else { + None + } + }) + .nth(import.index()), + } } } diff --git a/lib/engine-universal/src/code_memory.rs b/lib/engine-universal/src/code_memory.rs index 1498a23ea1..148f69eb18 100644 --- a/lib/engine-universal/src/code_memory.rs +++ b/lib/engine-universal/src/code_memory.rs @@ -3,8 +3,7 @@ //! Memory management for executable code. use crate::unwind::UnwindRegistry; -use loupe::MemoryUsage; -use wasmer_compiler::{CompiledFunctionUnwindInfo, CustomSection, FunctionBody}; +use wasmer_compiler::{CompiledFunctionUnwindInfoRef, CustomSectionRef, FunctionBodyRef}; use wasmer_vm::{Mmap, VMFunctionBody}; /// The optimal alignment for functions. @@ -19,7 +18,6 @@ const ARCH_FUNCTION_ALIGNMENT: usize = 16; const DATA_SECTION_ALIGNMENT: usize = 64; /// Memory manager for executable code. -#[derive(MemoryUsage)] pub struct CodeMemory { unwind_registry: UnwindRegistry, mmap: Mmap, @@ -44,9 +42,9 @@ impl CodeMemory { /// Allocate a single contiguous block of memory for the functions and custom sections, and copy the data in place. pub fn allocate( &mut self, - functions: &[&FunctionBody], - executable_sections: &[&CustomSection], - data_sections: &[&CustomSection], + functions: &[FunctionBodyRef<'_>], + executable_sections: &[CustomSectionRef<'_>], + data_sections: &[CustomSectionRef<'_>], ) -> Result<(Vec<&mut [VMFunctionBody]>, Vec<&mut [u8]>, Vec<&mut [u8]>), String> { let mut function_result = vec![]; let mut data_section_result = vec![]; @@ -67,7 +65,7 @@ impl CodeMemory { let total_len = round_up( functions.iter().fold(0, |acc, func| { round_up( - acc + Self::function_allocation_size(func), + acc + Self::function_allocation_size(*func), ARCH_FUNCTION_ALIGNMENT, ) }) + executable_sections.iter().fold(0, |acc, exec| { @@ -89,14 +87,14 @@ impl CodeMemory { let mut buf = self.mmap.as_mut_slice(); for func in functions { let len = round_up( - Self::function_allocation_size(func), + Self::function_allocation_size(*func), ARCH_FUNCTION_ALIGNMENT, ); let (func_buf, next_buf) = buf.split_at_mut(len); buf = next_buf; bytes += len; - let vmfunc = Self::copy_function(&mut self.unwind_registry, func, func_buf); + let vmfunc = Self::copy_function(&mut self.unwind_registry, *func, func_buf); assert_eq!(vmfunc.as_ptr() as usize % ARCH_FUNCTION_ALIGNMENT, 0); function_result.push(vmfunc); } @@ -107,7 +105,7 @@ impl CodeMemory { let (s, next_buf) = buf.split_at_mut(len); buf = next_buf; bytes += len; - s[..section.len()].copy_from_slice(section.as_slice()); + s[..section.len()].copy_from_slice(*section); executable_section_result.push(s); } @@ -125,7 +123,7 @@ impl CodeMemory { let len = round_up(section.len(), DATA_SECTION_ALIGNMENT); let (s, next_buf) = buf.split_at_mut(len); buf = next_buf; - s[..section.len()].copy_from_slice(section.as_slice()); + s[..section.len()].copy_from_slice(*section); data_section_result.push(s); } } @@ -154,9 +152,9 @@ impl CodeMemory { } /// Calculates the allocation size of the given compiled function. - fn function_allocation_size(func: &FunctionBody) -> usize { + fn function_allocation_size(func: FunctionBodyRef<'_>) -> usize { match &func.unwind_info { - Some(CompiledFunctionUnwindInfo::WindowsX64(info)) => { + Some(CompiledFunctionUnwindInfoRef::WindowsX64(info)) => { // Windows unwind information is required to be emitted into code memory // This is because it must be a positive relative offset from the start of the memory // Account for necessary unwind information alignment padding (32-bit alignment) @@ -171,7 +169,7 @@ impl CodeMemory { /// This will also add the function to the current function table. fn copy_function<'a>( registry: &mut UnwindRegistry, - func: &FunctionBody, + func: FunctionBodyRef<'_>, buf: &'a mut [u8], ) -> &'a mut [VMFunctionBody] { assert_eq!(buf.as_ptr() as usize % ARCH_FUNCTION_ALIGNMENT, 0); @@ -182,7 +180,7 @@ impl CodeMemory { body.copy_from_slice(&func.body); let vmfunc = Self::view_as_mut_vmfunc_slice(body); - if let Some(CompiledFunctionUnwindInfo::WindowsX64(info)) = &func.unwind_info { + if let Some(CompiledFunctionUnwindInfoRef::WindowsX64(info)) = &func.unwind_info { // Windows unwind information is written following the function body // Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary) let unwind_start = (func_len + 3) & !3; @@ -195,7 +193,7 @@ impl CodeMemory { if let Some(info) = &func.unwind_info { registry - .register(vmfunc.as_ptr() as usize, 0, func_len as u32, info) + .register(vmfunc.as_ptr() as usize, 0, func_len as u32, *info) .expect("failed to register unwind information"); } diff --git a/lib/engine-universal/src/engine.rs b/lib/engine-universal/src/engine.rs index a593cb032d..2dd575dd7a 100644 --- a/lib/engine-universal/src/engine.rs +++ b/lib/engine-universal/src/engine.rs @@ -1,25 +1,32 @@ //! Universal compilation. -use crate::{CodeMemory, UniversalArtifact}; -use loupe::MemoryUsage; +use crate::executable::{unrkyv, UniversalExecutableRef}; +use crate::{CodeMemory, UniversalArtifact, UniversalExecutable}; +use rkyv::de::deserializers::SharedDeserializeMap; +use std::collections::BTreeMap; +use std::convert::TryFrom; use std::sync::{Arc, Mutex}; #[cfg(feature = "compiler")] use wasmer_compiler::Compiler; use wasmer_compiler::{ - CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex, Target, + CompileError, CustomSectionProtection, CustomSectionRef, FunctionBodyRef, JumpTable, + ModuleMiddlewareChain, SectionIndex, Target, }; -use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, FunctionExtent, Tunables}; -use wasmer_types::entity::PrimaryMap; +use wasmer_engine::{Engine, EngineId}; +use wasmer_types::entity::{EntityRef, PrimaryMap}; use wasmer_types::{ - Features, FunctionIndex, FunctionType, LocalFunctionIndex, ModuleInfo, SignatureIndex, + DataInitializer, ExportIndex, Features, FunctionIndex, FunctionType, FunctionTypeRef, + GlobalInit, GlobalType, ImportCounts, ImportIndex, LocalFunctionIndex, LocalGlobalIndex, + MemoryIndex, SignatureIndex, TableIndex, }; use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMCallerCheckedAnyfunc, - VMFuncRef, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, Tunables, + VMCallerCheckedAnyfunc, VMFuncRef, VMFunctionBody, VMImportType, VMLocalFunction, VMOffsets, + VMSharedSignatureIndex, VMTrampoline, }; /// A WebAssembly `Universal` Engine. -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub struct UniversalEngine { inner: Arc>, /// The target for the compiler @@ -79,6 +86,341 @@ impl UniversalEngine { pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> { self.inner.lock().unwrap() } + + /// Compile a WebAssembly binary + #[cfg(feature = "compiler")] + pub fn compile_universal( + &self, + binary: &[u8], + tunables: &dyn Tunables, + ) -> Result { + let inner_engine = self.inner_mut(); + let features = inner_engine.features(); + let compiler = inner_engine.compiler()?; + let environ = wasmer_compiler::ModuleEnvironment::new(); + let translation = environ.translate(binary).map_err(CompileError::Wasm)?; + + // Apply the middleware first + let mut module = translation.module; + let middlewares = compiler.get_middlewares(); + middlewares.apply_on_module_info(&mut module); + + let memory_styles: PrimaryMap = module + .memories + .values() + .map(|memory_type| tunables.memory_style(memory_type)) + .collect(); + let table_styles: PrimaryMap = module + .tables + .values() + .map(|table_type| tunables.table_style(table_type)) + .collect(); + let compile_info = wasmer_compiler::CompileModuleInfo { + module: Arc::new(module), + features: features.clone(), + memory_styles, + table_styles, + }; + + // Compile the Module + let compilation = compiler.compile_module( + &self.target(), + &compile_info, + // SAFETY: Calling `unwrap` is correct since + // `environ.translate()` above will write some data into + // `module_translation_state`. + translation.module_translation_state.as_ref().unwrap(), + translation.function_body_inputs, + )?; + let function_call_trampolines = compilation.get_function_call_trampolines(); + let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); + let data_initializers = translation + .data_initializers + .iter() + .map(wasmer_types::OwnedDataInitializer::new) + .collect(); + + let frame_infos = compilation.get_frame_info(); + Ok(crate::UniversalExecutable { + function_bodies: compilation.get_function_bodies(), + function_relocations: compilation.get_relocations(), + function_jt_offsets: compilation.get_jt_offsets(), + function_frame_info: frame_infos, + function_call_trampolines, + dynamic_function_trampolines, + custom_sections: compilation.get_custom_sections(), + custom_section_relocations: compilation.get_custom_section_relocations(), + debug: compilation.get_debug(), + trampolines: compilation.get_trampolines(), + compile_info, + data_initializers, + cpu_features: self.target().cpu_features().as_u64(), + }) + } + + /// Load a [`UniversalExecutable`](crate::UniversalExecutable) with this engine. + pub fn load_universal_executable( + &self, + executable: &UniversalExecutable, + ) -> Result { + let info = &executable.compile_info; + let module = &info.module; + let local_memories = (module.import_counts.memories as usize..module.memories.len()) + .map(|idx| { + let idx = MemoryIndex::new(idx); + (module.memories[idx], info.memory_styles[idx].clone()) + }) + .collect(); + let local_tables = (module.import_counts.tables as usize..module.tables.len()) + .map(|idx| { + let idx = TableIndex::new(idx); + (module.tables[idx], info.table_styles[idx].clone()) + }) + .collect(); + let local_globals: Vec<(GlobalType, GlobalInit)> = module + .globals + .iter() + .skip(module.import_counts.globals as usize) + .enumerate() + .map(|(idx, (_, t))| { + let init = module.global_initializers[LocalGlobalIndex::new(idx)]; + (*t, init) + }) + .collect(); + let mut inner_engine = self.inner_mut(); + + let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into()); + let function_call_trampolines = &executable.function_call_trampolines; + let dynamic_function_trampolines = &executable.dynamic_function_trampolines; + let signatures = module + .signatures + .iter() + .map(|(_, sig)| inner_engine.signatures.register(sig.into())) + .collect::>() + .into_boxed_slice(); + let (functions, _, dynamic_trampolines, custom_sections) = inner_engine.allocate( + local_functions, + function_call_trampolines.iter().map(|(_, b)| b.into()), + dynamic_function_trampolines.iter().map(|(_, b)| b.into()), + executable.custom_sections.iter().map(|(_, s)| s.into()), + |idx: LocalFunctionIndex| { + let func_idx = module.import_counts.function_index(idx); + let sig_idx = module.functions[func_idx]; + (sig_idx, signatures[sig_idx]) + }, + )?; + let imports = module + .imports + .iter() + .map(|((module_name, field, idx), entity)| wasmer_vm::VMImport { + module: String::from(module_name), + field: String::from(field), + import_no: *idx, + ty: match entity { + ImportIndex::Function(i) => { + let sig_idx = module.functions[*i]; + VMImportType::Function(signatures[sig_idx]) + } + ImportIndex::Table(i) => VMImportType::Table(module.tables[*i]), + &ImportIndex::Memory(i) => { + let ty = module.memories[i]; + VMImportType::Memory(ty, info.memory_styles[i].clone()) + } + ImportIndex::Global(i) => VMImportType::Global(module.globals[*i]), + }, + }) + .collect(); + + let function_relocations = executable.function_relocations.iter(); + let section_relocations = executable.custom_section_relocations.iter(); + crate::link_module( + &functions, + |func_idx, jt_idx| executable.function_jt_offsets[func_idx][jt_idx], + function_relocations.map(|(i, rs)| (i, rs.iter().cloned())), + &custom_sections, + section_relocations.map(|(i, rs)| (i, rs.iter().cloned())), + &executable.trampolines, + ); + + // Make all code loaded executable. + inner_engine.publish_compiled_code(); + if let Some(ref d) = executable.debug { + unsafe { + // TODO: safety comment + inner_engine.publish_eh_frame(std::slice::from_raw_parts( + *custom_sections[d.eh_frame], + executable.custom_sections[d.eh_frame].bytes.len(), + ))?; + } + } + let exports = module + .exports + .iter() + .map(|(s, i)| (s.clone(), i.clone())) + .collect::>(); + + Ok(UniversalArtifact { + engine: self.clone(), + import_counts: module.import_counts, + start_function: module.start_function, + vmoffsets: VMOffsets::for_host().with_module_info(&*module), + imports, + dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(), + functions: functions.into_boxed_slice(), + exports, + signatures, + local_memories, + data_segments: executable.data_initializers.clone(), + passive_data: module.passive_data.clone(), + local_tables, + element_segments: module.table_initializers.clone(), + passive_elements: module.passive_elements.clone(), + local_globals, + }) + } + + /// Load a [`UniversalExecutableRef`](crate::UniversalExecutableRef) with this engine. + pub fn load_universal_executable_ref( + &self, + executable: &UniversalExecutableRef, + ) -> Result { + let info = &executable.compile_info; + let module = &info.module; + let import_counts: ImportCounts = unrkyv(&module.import_counts); + let local_memories = (import_counts.memories as usize..module.memories.len()) + .map(|idx| { + let idx = MemoryIndex::new(idx); + let mty = &module.memories[&idx]; + (unrkyv(mty), unrkyv(&info.memory_styles[&idx])) + }) + .collect(); + let local_tables = (import_counts.tables as usize..module.tables.len()) + .map(|idx| { + let idx = TableIndex::new(idx); + let tty = &module.tables[&idx]; + (unrkyv(tty), unrkyv(&info.table_styles[&idx])) + }) + .collect(); + let local_globals: Vec<(GlobalType, GlobalInit)> = module + .globals + .iter() + .skip(import_counts.globals as _) + .enumerate() + .map(|(idx, (_, t))| { + let init = unrkyv(&module.global_initializers[&LocalGlobalIndex::new(idx)]); + (*t, init) + }) + .collect(); + + let passive_data = + rkyv::Deserialize::deserialize(&module.passive_data, &mut SharedDeserializeMap::new()) + .map_err(|_| CompileError::Validate("could not deserialize passive data".into()))?; + let data_segments = executable.data_initializers.iter(); + let data_segments = data_segments + .map(|s| DataInitializer::from(s).into()) + .collect(); + let element_segments = unrkyv(&module.table_initializers); + let passive_elements: BTreeMap> = + unrkyv(&module.passive_elements); + + let import_counts: ImportCounts = unrkyv(&module.import_counts); + let mut inner_engine = self.inner_mut(); + + let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into()); + let call_trampolines = executable.function_call_trampolines.iter(); + let dynamic_trampolines = executable.dynamic_function_trampolines.iter(); + let signatures = module + .signatures + .values() + .map(|sig| inner_engine.signatures.register(sig.into())) + .collect::>() + .into_boxed_slice(); + let (functions, _, dynamic_trampolines, custom_sections) = inner_engine.allocate( + local_functions, + call_trampolines.map(|(_, b)| b.into()), + dynamic_trampolines.map(|(_, b)| b.into()), + executable.custom_sections.iter().map(|(_, s)| s.into()), + |idx: LocalFunctionIndex| { + let func_idx = import_counts.function_index(idx); + let sig_idx = module.functions[&func_idx]; + (sig_idx, signatures[sig_idx]) + }, + )?; + let imports = { + module + .imports + .iter() + .map(|((module_name, field, idx), entity)| wasmer_vm::VMImport { + module: String::from(module_name.as_str()), + field: String::from(field.as_str()), + import_no: *idx, + ty: match entity { + ImportIndex::Function(i) => { + let sig_idx = module.functions[i]; + VMImportType::Function(signatures[sig_idx]) + } + ImportIndex::Table(i) => VMImportType::Table(unrkyv(&module.tables[i])), + ImportIndex::Memory(i) => { + let ty = unrkyv(&module.memories[i]); + VMImportType::Memory(ty, unrkyv(&info.memory_styles[i])) + } + ImportIndex::Global(i) => VMImportType::Global(unrkyv(&module.globals[i])), + }, + }) + .collect() + }; + + let function_relocations = executable.function_relocations.iter(); + let section_relocations = executable.custom_section_relocations.iter(); + crate::link_module( + &functions, + |func_idx, jt_idx| { + let func_idx = rkyv::Archived::::new(func_idx.index()); + let jt_idx = rkyv::Archived::::new(jt_idx.index()); + executable.function_jt_offsets[&func_idx][&jt_idx] + }, + function_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))), + &custom_sections, + section_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))), + &unrkyv(&executable.trampolines), + ); + + // Make all code compiled thus far executable. + inner_engine.publish_compiled_code(); + if let rkyv::option::ArchivedOption::Some(ref d) = executable.debug { + unsafe { + // TODO: safety comment + let s = CustomSectionRef::from(&executable.custom_sections[&d.eh_frame]); + inner_engine.publish_eh_frame(std::slice::from_raw_parts( + *custom_sections[unrkyv(&d.eh_frame)], + s.bytes.len(), + ))?; + } + } + let exports = module + .exports + .iter() + .map(|(s, i)| (unrkyv(s), unrkyv(i))) + .collect::>(); + Ok(UniversalArtifact { + engine: self.clone(), + import_counts, + start_function: unrkyv(&module.start_function), + vmoffsets: VMOffsets::for_host().with_archived_module_info(&*module), + imports, + dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(), + functions: functions.into_boxed_slice(), + exports, + signatures, + local_memories, + data_segments, + passive_data, + local_tables, + element_segments, + passive_elements, + local_globals, + }) + } } impl Engine for UniversalEngine { @@ -88,25 +430,17 @@ impl Engine for UniversalEngine { } /// Register a signature - fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex { - let compiler = self.inner(); - compiler.signatures().register(func_type) - } - - fn use_signals(&self) -> bool { - let compiler = self.inner(); - compiler.use_signals() + fn register_signature(&self, func_type: FunctionTypeRef<'_>) -> VMSharedSignatureIndex { + self.inner().signatures.register(func_type) } fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { - let compiler = self.inner(); - compiler.func_data().register(func_data) + self.inner().func_data().register(func_data) } /// Lookup a signature fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { - let compiler = self.inner(); - compiler.signatures().lookup(sig) + self.inner().signatures.lookup(sig).cloned() } /// Validates a WebAssembly module @@ -114,32 +448,34 @@ impl Engine for UniversalEngine { self.inner().validate(binary) } - /// Compile a WebAssembly binary - #[cfg(feature = "compiler")] + #[cfg(not(feature = "compiler"))] fn compile( &self, binary: &[u8], tunables: &dyn Tunables, - ) -> Result, CompileError> { - Ok(Arc::new(UniversalArtifact::new(&self, binary, tunables)?)) + ) -> Result, CompileError> { + return Err(CompileError::Codegen( + "The UniversalEngine is operating in headless mode, so it can not compile Modules." + .to_string(), + )); } /// Compile a WebAssembly binary - #[cfg(not(feature = "compiler"))] + #[cfg(feature = "compiler")] fn compile( &self, - _binary: &[u8], - _tunables: &dyn Tunables, - ) -> Result, CompileError> { - Err(CompileError::Codegen( - "The UniversalEngine is operating in headless mode, so it can not compile Modules." - .to_string(), - )) + binary: &[u8], + tunables: &dyn Tunables, + ) -> Result, CompileError> { + self.compile_universal(binary, tunables) + .map(|ex| Box::new(ex) as _) } - /// Deserializes a WebAssembly module - unsafe fn deserialize(&self, bytes: &[u8]) -> Result, DeserializeError> { - Ok(Arc::new(UniversalArtifact::deserialize(&self, &bytes)?)) + fn load( + &self, + executable: &(dyn wasmer_engine::Executable), + ) -> Result, CompileError> { + executable.load(self) } fn id(&self) -> &EngineId { @@ -152,7 +488,6 @@ impl Engine for UniversalEngine { } /// The inner contents of `UniversalEngine` -#[derive(MemoryUsage)] pub struct UniversalEngineInner { /// The compiler #[cfg(feature = "compiler")] @@ -164,7 +499,7 @@ pub struct UniversalEngineInner { code_memory: Vec, /// The signature registry is used mainly to operate with trampolines /// performantly. - signatures: SignatureRegistry, + pub(crate) signatures: SignatureRegistry, /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. @@ -201,49 +536,49 @@ impl UniversalEngineInner { &self.features } - /// If need to install signal handlers. - pub fn use_signals(&self) -> bool { - #[cfg(feature = "compiler")] - match self.compiler() { - Ok(compiler) => compiler.use_signals(), - _ => true, - } - #[cfg(not(feature = "compiler"))] - true - } - /// Allocate compiled functions into memory #[allow(clippy::type_complexity)] - pub(crate) fn allocate( + pub(crate) fn allocate<'a>( &mut self, - _module: &ModuleInfo, - functions: &PrimaryMap, - function_call_trampolines: &PrimaryMap, - dynamic_function_trampolines: &PrimaryMap, - custom_sections: &PrimaryMap, + local_functions: impl ExactSizeIterator>, + call_trampolines: impl ExactSizeIterator>, + dynamic_trampolines: impl ExactSizeIterator>, + custom_sections: impl ExactSizeIterator>, + function_signature: impl Fn(LocalFunctionIndex) -> (SignatureIndex, VMSharedSignatureIndex), ) -> Result< ( - PrimaryMap, + PrimaryMap, PrimaryMap, PrimaryMap, PrimaryMap, ), CompileError, > { - let function_bodies = functions - .values() - .chain(function_call_trampolines.values()) - .chain(dynamic_function_trampolines.values()) + let code_memory = &mut self.code_memory; + let function_count = local_functions.len(); + let call_trampoline_count = call_trampolines.len(); + let function_bodies = call_trampolines + .chain(local_functions) + .chain(dynamic_trampolines) .collect::>(); - let (executable_sections, data_sections): (Vec<_>, _) = custom_sections - .values() - .partition(|section| section.protection == CustomSectionProtection::ReadExecute); - self.code_memory.push(CodeMemory::new()); + + // TOOD: this shouldn't be necessary.... + let mut section_types = Vec::with_capacity(custom_sections.len()); + let mut executable_sections = Vec::new(); + let mut data_sections = Vec::new(); + for section in custom_sections { + if let CustomSectionProtection::ReadExecute = section.protection { + executable_sections.push(section); + } else { + data_sections.push(section); + } + section_types.push(section.protection); + } + code_memory.push(CodeMemory::new()); + let code_memory = self.code_memory.last_mut().expect("infallible"); let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) = - self.code_memory - .last_mut() - .unwrap() + code_memory .allocate( function_bodies.as_slice(), executable_sections.as_slice(), @@ -256,25 +591,35 @@ impl UniversalEngineInner { )) })?; - let allocated_functions_result = allocated_functions - .drain(0..functions.len()) - .map(|slice| FunctionExtent { - ptr: FunctionBodyPtr(slice.as_ptr()), - length: slice.len(), - }) - .collect::>(); - let mut allocated_function_call_trampolines: PrimaryMap = PrimaryMap::new(); for ptr in allocated_functions - .drain(0..function_call_trampolines.len()) + .drain(0..call_trampoline_count) .map(|slice| slice.as_ptr()) { + // TODO: What in damnation have you done?! – Bannon let trampoline = unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) }; allocated_function_call_trampolines.push(trampoline); } + let allocated_functions_result = allocated_functions + .drain(0..function_count) + .enumerate() + .map(|(index, slice)| -> Result<_, CompileError> { + let index = LocalFunctionIndex::new(index); + let (sig_idx, sig) = function_signature(index); + Ok(VMLocalFunction { + body: FunctionBodyPtr(slice.as_ptr()), + length: u32::try_from(slice.len()).map_err(|_| { + CompileError::Codegen("function body length exceeds 4GiB".into()) + })?, + signature: sig, + trampoline: allocated_function_call_trampolines[sig_idx], + }) + }) + .collect::, _>>()?; + let allocated_dynamic_function_trampolines = allocated_functions .drain(..) .map(|slice| FunctionBodyPtr(slice.as_ptr())) @@ -282,11 +627,11 @@ impl UniversalEngineInner { let mut exec_iter = allocated_executable_sections.iter(); let mut data_iter = allocated_data_sections.iter(); - let allocated_custom_sections = custom_sections - .iter() - .map(|(_, section)| { + let allocated_custom_sections = section_types + .into_iter() + .map(|protection| { SectionBodyPtr( - if section.protection == CustomSectionProtection::ReadExecute { + if protection == CustomSectionProtection::ReadExecute { exec_iter.next() } else { data_iter.next() @@ -311,7 +656,7 @@ impl UniversalEngineInner { } /// Register DWARF-type exception handling information associated with the code. - pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> { + pub(crate) fn publish_eh_frame(&mut self, eh_frame: &[u8]) -> Result<(), CompileError> { self.code_memory .last_mut() .unwrap() @@ -323,11 +668,6 @@ impl UniversalEngineInner { Ok(()) } - /// Shared signature registry. - pub fn signatures(&self) -> &SignatureRegistry { - &self.signatures - } - /// Shared func metadata registry. pub(crate) fn func_data(&self) -> &Arc { &self.func_data diff --git a/lib/engine-universal/src/executable.rs b/lib/engine-universal/src/executable.rs new file mode 100644 index 0000000000..3e04ce1ab3 --- /dev/null +++ b/lib/engine-universal/src/executable.rs @@ -0,0 +1,246 @@ +use std::sync::Arc; + +use enumset::EnumSet; +use rkyv::de::deserializers::SharedDeserializeMap; +use rkyv::ser::serializers::{ + AllocScratchError, AllocSerializer, CompositeSerializerError, SharedSerializeMapError, +}; +use wasmer_compiler::{ + CompileError, CompileModuleInfo, CompiledFunctionFrameInfo, CpuFeature, CustomSection, Dwarf, + Features, FunctionBody, JumpTableOffsets, Relocation, SectionIndex, TrampolinesSection, +}; +use wasmer_engine::{DeserializeError, Engine}; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::{ + ExportIndex, FunctionIndex, ImportIndex, LocalFunctionIndex, OwnedDataInitializer, + SignatureIndex, +}; +use wasmer_vm::Artifact; + +const MAGIC_HEADER: [u8; 32] = { + let value = *b"\0wasmer-universal\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + let _length_must_be_multiple_of_16: bool = [true][value.len() % 16]; + value +}; + +/// A 0-copy view of the encoded `UniversalExecutable` payload. +#[derive(Clone, Copy)] +pub struct UniversalExecutableRef<'a> { + buffer: &'a [u8], + archive: &'a ArchivedUniversalExecutable, +} + +impl<'a> std::ops::Deref for UniversalExecutableRef<'a> { + type Target = ArchivedUniversalExecutable; + fn deref(&self) -> &Self::Target { + self.archive + } +} + +impl<'a> UniversalExecutableRef<'a> { + /// Verify the buffer for whether it is a valid `UniversalExecutable`. + pub fn verify_serialized(data: &[u8]) -> Result<(), &'static str> { + if !data.starts_with(&MAGIC_HEADER) { + return Err("the provided bytes are not wasmer-universal"); + } + if data.len() < MAGIC_HEADER.len() + 8 { + return Err("the data buffer is too small to be valid"); + } + let (remaining, position) = data.split_at(data.len() - 8); + let mut position_value = [0u8; 8]; + position_value.copy_from_slice(position); + if u64::from_le_bytes(position_value) > remaining.len() as u64 { + return Err("the buffer is malformed"); + } + // TODO(0-copy): bytecheck too. + Ok(()) + } + + /// # Safety + /// + /// This method is unsafe since it deserializes data directly + /// from memory. + /// Right now we are not doing any extra work for validation, but + /// `rkyv` has an option to do bytecheck on the serialized data before + /// serializing (via `rkyv::check_archived_value`). + pub unsafe fn deserialize( + data: &'a [u8], + ) -> Result, DeserializeError> { + Self::verify_serialized(data).map_err(|e| DeserializeError::Incompatible(e.to_string()))?; + let (archive, position) = data.split_at(data.len() - 8); + let mut position_value = [0u8; 8]; + position_value.copy_from_slice(position); + let (_, data) = archive.split_at(MAGIC_HEADER.len()); + Ok(UniversalExecutableRef { + buffer: data, + archive: rkyv::archived_value::( + data, + u64::from_le_bytes(position_value) as usize, + ), + }) + } + + // TODO(0-copy): this should never fail. + /// Convert this reference to an owned `UniversalExecutable` value. + pub fn to_owned(self) -> Result { + let mut deserializer = SharedDeserializeMap::new(); + rkyv::Deserialize::deserialize(self.archive, &mut deserializer) + .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e))) + } +} + +/// A wasm module compiled to some shape, ready to be loaded with `UniversalEngine` to produce an +/// `UniversalArtifact`. +/// +/// This is the result obtained after validating and compiling a WASM module with any of the +/// supported compilers. This type falls in-between a module and [`Artifact`](crate::Artifact). +#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)] +pub struct UniversalExecutable { + pub(crate) function_bodies: PrimaryMap, + pub(crate) function_relocations: PrimaryMap>, + pub(crate) function_jt_offsets: PrimaryMap, + pub(crate) function_frame_info: PrimaryMap, + pub(crate) function_call_trampolines: PrimaryMap, + pub(crate) dynamic_function_trampolines: PrimaryMap, + pub(crate) custom_sections: PrimaryMap, + pub(crate) custom_section_relocations: PrimaryMap>, + // The section indices corresponding to the Dwarf debug info + pub(crate) debug: Option, + // the Trampoline for Arm arch + pub(crate) trampolines: Option, + pub(crate) compile_info: CompileModuleInfo, + pub(crate) data_initializers: Vec, + pub(crate) cpu_features: u64, +} + +#[derive(thiserror::Error, Debug)] +pub enum ExecutableSerializeError { + #[error("could not serialize the executable data")] + Executable( + #[source] + CompositeSerializerError< + std::convert::Infallible, + AllocScratchError, + SharedSerializeMapError, + >, + ), +} + +impl wasmer_engine::Executable for UniversalExecutable { + fn load( + &self, + engine: &(dyn Engine + 'static), + ) -> Result, CompileError> { + engine + .downcast_ref::() + .ok_or(CompileError::EngineDowncast)? + .load_universal_executable(self) + .map(|a| Arc::new(a) as _) + } + + fn features(&self) -> Features { + self.compile_info.features.clone() + } + + fn cpu_features(&self) -> EnumSet { + EnumSet::from_u64(self.cpu_features) + } + + fn serialize(&self) -> Result, Box<(dyn std::error::Error + Send + Sync + 'static)>> { + // The format is as thus: + // + // HEADER + // RKYV PAYLOAD + // RKYV POSITION + // + // It is expected that any framing for message length is handled by the caller. + let mut serializer = AllocSerializer::<1024>::default(); + let pos = rkyv::ser::Serializer::serialize_value(&mut serializer, self) + .map_err(ExecutableSerializeError::Executable)? as u64; + let pos_bytes = pos.to_le_bytes(); + let data = serializer.into_serializer().into_inner(); + let mut out = Vec::with_capacity(MAGIC_HEADER.len() + pos_bytes.len() + data.len()); + out.extend(&MAGIC_HEADER); + out.extend(data.as_slice()); + out.extend(&pos_bytes); + Ok(out) + } + + fn function_name(&self, index: FunctionIndex) -> Option<&str> { + let module = &self.compile_info.module; + // First, lets see if there's a name by which this function is exported. + for (name, idx) in module.exports.iter() { + match idx { + &ExportIndex::Function(fi) if fi == index => return Some(&*name), + _ => continue, + } + } + if let Some(r) = module.function_names.get(&index) { + return Some(&**r); + } + for ((_, field, _), idx) in module.imports.iter() { + match idx { + &ImportIndex::Function(fi) if fi == index => return Some(&*field), + _ => continue, + } + } + None + } +} + +impl<'a> wasmer_engine::Executable for UniversalExecutableRef<'a> { + fn load( + &self, + engine: &(dyn Engine + 'static), + ) -> Result, CompileError> { + engine + .downcast_ref::() + .ok_or_else(|| CompileError::Codegen("can't downcast TODO FIXME".into()))? + .load_universal_executable_ref(self) + .map(|a| Arc::new(a) as _) + } + + fn features(&self) -> Features { + unrkyv(&self.archive.compile_info.features) + } + + fn cpu_features(&self) -> EnumSet { + EnumSet::from_u64(unrkyv(&self.archive.cpu_features)) + } + + fn serialize(&self) -> Result, Box> { + Ok(self.buffer.to_vec()) + } + + fn function_name(&self, index: FunctionIndex) -> Option<&str> { + let module = &self.compile_info.module; + // First, lets see if there's a name by which this function is exported. + for (name, idx) in module.exports.iter() { + match idx { + &ExportIndex::Function(fi) if fi == index => return Some(&*name), + _ => continue, + } + } + if let Some(r) = module.function_names.get(&index) { + return Some(&**r); + } + for ((_, field, _), idx) in module.imports.iter() { + match idx { + &ImportIndex::Function(fi) if fi == index => return Some(&*field), + _ => continue, + } + } + None + } +} + +pub(crate) fn unrkyv(archive: &T::Archived) -> T +where + T: rkyv::Archive, + T::Archived: rkyv::Deserialize, +{ + Result::<_, std::convert::Infallible>::unwrap(rkyv::Deserialize::deserialize( + archive, + &mut rkyv::Infallible, + )) +} diff --git a/lib/engine-universal/src/lib.rs b/lib/engine-universal/src/lib.rs index 2bbafadd4d..f4a2d8ebaa 100644 --- a/lib/engine-universal/src/lib.rs +++ b/lib/engine-universal/src/lib.rs @@ -6,6 +6,7 @@ #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] #![warn(unused_import_braces)] +#![warn(unsafe_op_in_unsafe_fn)] #![cfg_attr( feature = "cargo-clippy", allow(clippy::new_without_default, clippy::new_without_default) @@ -28,14 +29,15 @@ mod artifact; mod builder; mod code_memory; mod engine; +mod executable; mod link; -mod serialize; mod unwind; pub use crate::artifact::UniversalArtifact; pub use crate::builder::Universal; pub use crate::code_memory::CodeMemory; pub use crate::engine::UniversalEngine; +pub use crate::executable::{UniversalExecutable, UniversalExecutableRef}; pub use crate::link::link_module; /// Version number of this crate. diff --git a/lib/engine-universal/src/link.rs b/lib/engine-universal/src/link.rs index cb7b724519..5cee1d8d2f 100644 --- a/lib/engine-universal/src/link.rs +++ b/lib/engine-universal/src/link.rs @@ -3,13 +3,11 @@ use std::collections::HashMap; use std::ptr::{read_unaligned, write_unaligned}; use wasmer_compiler::{ - JumpTable, JumpTableOffsets, Relocation, RelocationKind, RelocationTarget, Relocations, - SectionIndex, TrampolinesSection, + JumpTable, Relocation, RelocationKind, RelocationTarget, SectionIndex, TrampolinesSection, }; -use wasmer_engine::FunctionExtent; -use wasmer_types::entity::{EntityRef, PrimaryMap}; -use wasmer_types::{LocalFunctionIndex, ModuleInfo}; -use wasmer_vm::SectionBodyPtr; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::LocalFunctionIndex; +use wasmer_vm::{SectionBodyPtr, VMLocalFunction}; /// Add a new trampoline address, given the base adress of the Section. Return the address of the jump /// The trampoline itself still have to be writen @@ -47,7 +45,7 @@ fn use_trampoline( } } -fn fill_trampolin_map( +fn fill_trampoline_map( allocated_sections: &PrimaryMap, trampolines: &Option, ) -> HashMap { @@ -72,24 +70,21 @@ fn fill_trampolin_map( fn apply_relocation( body: usize, r: &Relocation, - allocated_functions: &PrimaryMap, - jt_offsets: &PrimaryMap, + allocated_functions: &PrimaryMap, + jt_offsets: impl Fn(LocalFunctionIndex, JumpTable) -> wasmer_compiler::CodeOffset, allocated_sections: &PrimaryMap, trampolines: &Option, trampolines_map: &mut HashMap, ) { let target_func_address: usize = match r.reloc_target { - RelocationTarget::LocalFunc(index) => *allocated_functions[index].ptr as usize, + RelocationTarget::LocalFunc(index) => *allocated_functions[index].body as usize, RelocationTarget::LibCall(libcall) => libcall.function_pointer(), RelocationTarget::CustomSection(custom_section) => { *allocated_sections[custom_section] as usize } RelocationTarget::JumpTable(func_index, jt) => { - let offset = *jt_offsets - .get(func_index) - .and_then(|ofs| ofs.get(JumpTable::new(jt.index()))) - .expect("func jump table"); - *allocated_functions[func_index].ptr as usize + offset as usize + let offset = jt_offsets(func_index, jt); + *allocated_functions[func_index].body as usize + offset as usize } }; @@ -174,37 +169,36 @@ fn apply_relocation( /// Links a module, patching the allocated functions with the /// required relocations and jump tables. pub fn link_module( - _module: &ModuleInfo, - allocated_functions: &PrimaryMap, - jt_offsets: &PrimaryMap, - function_relocations: Relocations, + allocated_functions: &PrimaryMap, + jt_offsets: impl Fn(LocalFunctionIndex, JumpTable) -> wasmer_compiler::CodeOffset, + function_relocations: impl Iterator)>, allocated_sections: &PrimaryMap, - section_relocations: &PrimaryMap>, + section_relocations: impl Iterator)>, trampolines: &Option, ) { - let mut trampolines_map = fill_trampolin_map(allocated_sections, trampolines); - for (i, section_relocs) in section_relocations.iter() { + let mut trampolines_map = fill_trampoline_map(allocated_sections, trampolines); + for (i, section_relocs) in section_relocations { let body = *allocated_sections[i] as usize; for r in section_relocs { apply_relocation( body, - r, + &r, allocated_functions, - jt_offsets, + &jt_offsets, allocated_sections, trampolines, &mut trampolines_map, ); } } - for (i, function_relocs) in function_relocations.iter() { - let body = *allocated_functions[i].ptr as usize; + for (i, function_relocs) in function_relocations { + let body = *allocated_functions[i].body as usize; for r in function_relocs { apply_relocation( body, - r, + &r, allocated_functions, - jt_offsets, + &jt_offsets, allocated_sections, trampolines, &mut trampolines_map, diff --git a/lib/engine-universal/src/serialize.rs b/lib/engine-universal/src/serialize.rs deleted file mode 100644 index a15f68effa..0000000000 --- a/lib/engine-universal/src/serialize.rs +++ /dev/null @@ -1,104 +0,0 @@ -use loupe::MemoryUsage; -use rkyv::{ - archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer, - ser::Serializer as RkyvSerializer, Archive, Deserialize as RkyvDeserialize, - Serialize as RkyvSerialize, -}; -use wasmer_compiler::{ - CompileModuleInfo, CompiledFunctionFrameInfo, CustomSection, Dwarf, FunctionBody, - JumpTableOffsets, Relocation, SectionIndex, TrampolinesSection, -}; -use wasmer_engine::{DeserializeError, SerializeError}; -use wasmer_types::entity::PrimaryMap; -use wasmer_types::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex}; - -/// The compilation related data for a serialized modules -#[derive(MemoryUsage, Archive, RkyvDeserialize, RkyvSerialize)] -pub struct SerializableCompilation { - pub function_bodies: PrimaryMap, - pub function_relocations: PrimaryMap>, - pub function_jt_offsets: PrimaryMap, - pub function_frame_info: PrimaryMap, - pub function_call_trampolines: PrimaryMap, - pub dynamic_function_trampolines: PrimaryMap, - pub custom_sections: PrimaryMap, - pub custom_section_relocations: PrimaryMap>, - // The section indices corresponding to the Dwarf debug info - pub debug: Option, - // the Trampoline for Arm arch - pub trampolines: Option, -} - -/// Serializable struct that is able to serialize from and to -/// a `UniversalArtifactInfo`. -#[derive(MemoryUsage, Archive, RkyvDeserialize, RkyvSerialize)] -pub struct SerializableModule { - pub compilation: SerializableCompilation, - pub compile_info: CompileModuleInfo, - pub data_initializers: Box<[OwnedDataInitializer]>, - pub cpu_features: u64, -} - -fn to_serialize_error(err: impl std::error::Error) -> SerializeError { - SerializeError::Generic(format!("{}", err)) -} - -impl SerializableModule { - /// Serialize a Module into bytes - /// The bytes will have the following format: - /// RKYV serialization (any length) + POS (8 bytes) - pub fn serialize(&self) -> Result, SerializeError> { - let mut serializer = AllocSerializer::<4096>::default(); - let pos = serializer - .serialize_value(self) - .map_err(to_serialize_error)? as u64; - let mut serialized_data = serializer.into_serializer().into_inner(); - serialized_data.extend_from_slice(&pos.to_le_bytes()); - Ok(serialized_data.to_vec()) - } - - /// Deserialize a Module from a slice. - /// The slice must have the following format: - /// RKYV serialization (any length) + POS (8 bytes) - /// - /// # Safety - /// - /// This method is unsafe since it deserializes data directly - /// from memory. - /// Right now we are not doing any extra work for validation, but - /// `rkyv` has an option to do bytecheck on the serialized data before - /// serializing (via `rkyv::check_archived_value`). - pub unsafe fn deserialize(metadata_slice: &[u8]) -> Result { - let archived = Self::archive_from_slice(metadata_slice)?; - Self::deserialize_from_archive(archived) - } - - /// # Safety - /// - /// This method is unsafe. - /// Please check `SerializableModule::deserialize` for more details. - unsafe fn archive_from_slice<'a>( - metadata_slice: &'a [u8], - ) -> Result<&'a ArchivedSerializableModule, DeserializeError> { - if metadata_slice.len() < 8 { - return Err(DeserializeError::Incompatible( - "invalid serialized data".into(), - )); - } - let mut pos: [u8; 8] = Default::default(); - pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]); - let pos: u64 = u64::from_le_bytes(pos); - Ok(archived_value::( - &metadata_slice[..metadata_slice.len() - 8], - pos as usize, - )) - } - - pub fn deserialize_from_archive( - archived: &ArchivedSerializableModule, - ) -> Result { - let mut deserializer = SharedDeserializeMap::new(); - RkyvDeserialize::deserialize(archived, &mut deserializer) - .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e))) - } -} diff --git a/lib/engine-universal/src/unwind/dummy.rs b/lib/engine-universal/src/unwind/dummy.rs index cfb636bd2c..3d029ecd98 100644 --- a/lib/engine-universal/src/unwind/dummy.rs +++ b/lib/engine-universal/src/unwind/dummy.rs @@ -1,11 +1,9 @@ //! Module for Dummy unwind registry. -use loupe::MemoryUsage; use wasmer_compiler::CompiledFunctionUnwindInfo; /// Represents a registry of function unwind information when the host system /// support any one in specific. -#[derive(MemoryUsage)] pub struct DummyUnwindRegistry {} impl DummyUnwindRegistry { diff --git a/lib/engine-universal/src/unwind/systemv.rs b/lib/engine-universal/src/unwind/systemv.rs index 8362fec499..b4c4ebe810 100644 --- a/lib/engine-universal/src/unwind/systemv.rs +++ b/lib/engine-universal/src/unwind/systemv.rs @@ -3,11 +3,9 @@ //! Module for System V ABI unwind registry. -use loupe::MemoryUsage; -use wasmer_compiler::CompiledFunctionUnwindInfo; +use wasmer_compiler::CompiledFunctionUnwindInfoRef; /// Represents a registry of function unwind information for System V ABI. -#[derive(MemoryUsage)] pub struct UnwindRegistry { registrations: Vec, published: bool, @@ -34,25 +32,23 @@ impl UnwindRegistry { _base_address: usize, _func_start: u32, _func_len: u32, - info: &CompiledFunctionUnwindInfo, + info: CompiledFunctionUnwindInfoRef<'_>, ) -> Result<(), String> { match info { - CompiledFunctionUnwindInfo::Dwarf => {} + CompiledFunctionUnwindInfoRef::Dwarf => {} _ => return Err("unsupported unwind information".to_string()), }; Ok(()) } /// Publishes all registered functions. - pub fn publish(&mut self, eh_frame: Option<&[u8]>) -> Result<(), String> { + pub fn publish(&mut self, eh_frame: &[u8]) -> Result<(), String> { if self.published { return Err("unwind registry has already been published".to_string()); } - if let Some(eh_frame) = eh_frame { - unsafe { - self.register_frames(eh_frame); - } + unsafe { + self.register_frames(eh_frame); } self.published = true; diff --git a/lib/engine-universal/src/unwind/windows_x64.rs b/lib/engine-universal/src/unwind/windows_x64.rs index 63de9597ce..8ee3903b32 100644 --- a/lib/engine-universal/src/unwind/windows_x64.rs +++ b/lib/engine-universal/src/unwind/windows_x64.rs @@ -2,7 +2,6 @@ // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md //! Module for Windows x64 ABI unwind registry. -use loupe::{MemoryUsage, MemoryUsageTracker}; use std::collections::HashMap; use wasmer_compiler::CompiledFunctionUnwindInfo; use winapi::um::winnt; @@ -104,22 +103,3 @@ impl Drop for UnwindRegistry { } } } - -impl MemoryUsage for UnwindRegistry { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - // To count `RUNTIME_FUNCTION`, we first check the - // [documentation][doc1]. We see that it's a type alias to - // `_IMAGE_RUNTIME_FUNCTION_ENTRY`, let's check the - // [documentation][doc2]. It's composed of two `DWORD`, so two - // `u64`, and one `IMAGE_RUNTIME_FUNCTION_ENTRY_u`, which we - // approximate to size of `u64`. - // - // [doc1]: https://docs.rs/winapi/0.3.9/winapi/um/winnt/type.RUNTIME_FUNCTION.html - // [doc2]: https://docs.rs/winapi/0.3.9/winapi/um/winnt/struct._IMAGE_RUNTIME_FUNCTION_ENTRY.html - self.functions - .iter() - .map(|(_, _)| std::mem::size_of::() * 3) - .sum::() - + self.published.size_of_val(tracker) - } -} diff --git a/lib/engine/Cargo.toml b/lib/engine/Cargo.toml index 3e32f58b1b..359d858fb2 100644 --- a/lib/engine/Cargo.toml +++ b/lib/engine/Cargo.toml @@ -27,7 +27,6 @@ thiserror = "1.0" serde = { version = "1.0", features = ["derive", "rc"] } serde_bytes = { version = "0.11" } lazy_static = "1.4" -loupe = "0.1" enumset = "1.0" [badges] diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs deleted file mode 100644 index 136edca375..0000000000 --- a/lib/engine/src/artifact.rs +++ /dev/null @@ -1,224 +0,0 @@ -use crate::{ - resolve_imports, InstantiationError, Resolver, RuntimeError, SerializeError, Tunables, -}; -use enumset::EnumSet; -use loupe::MemoryUsage; -use std::any::Any; -use std::fs; -use std::path::Path; -use std::sync::Arc; -use wasmer_compiler::{CpuFeature, Features}; -use wasmer_types::entity::{BoxedSlice, PrimaryMap}; -use wasmer_types::{ - DataInitializer, FunctionIndex, InstanceConfig, LocalFunctionIndex, MemoryIndex, ModuleInfo, - OwnedDataInitializer, SignatureIndex, TableIndex, -}; -use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, TableStyle, - TrapHandler, VMSharedSignatureIndex, VMTrampoline, -}; - -/// An `Artifact` is the product that the `Engine` -/// implementation produce and use. -/// -/// The `Artifact` contains the compiled data for a given -/// module as well as extra information needed to run the -/// module at runtime, such as [`ModuleInfo`] and [`Features`]. -pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { - /// Return a reference-counted pointer to the module - fn module(&self) -> Arc; - - /// Return a pointer to a module. - fn module_ref(&self) -> &ModuleInfo; - - /// Gets a mutable reference to the info. - /// - /// Note: this will return `None` if the module is already instantiated. - fn module_mut(&mut self) -> Option<&mut ModuleInfo>; - - /// Register thie `Artifact` stack frame information into the global scope. - /// - /// This is required to ensure that any traps can be properly symbolicated. - fn register_frame_info(&self); - - /// Returns the features for this Artifact - fn features(&self) -> &Features; - - /// Returns the CPU features for this Artifact - fn cpu_features(&self) -> EnumSet; - - /// Returns the memory styles associated with this `Artifact`. - fn memory_styles(&self) -> &PrimaryMap; - - /// Returns the table plans associated with this `Artifact`. - fn table_styles(&self) -> &PrimaryMap; - - /// Returns data initializers to pass to `InstanceHandle::initialize` - fn data_initializers(&self) -> &[OwnedDataInitializer]; - - /// Returns the functions allocated in memory or this `Artifact` - /// ready to be run. - fn finished_functions(&self) -> &BoxedSlice; - - /// Returns the functions code length. - fn finished_functions_lengths(&self) -> &BoxedSlice; - - /// Returns the function call trampolines allocated in memory of this - /// `Artifact`, ready to be run. - fn finished_function_call_trampolines(&self) -> &BoxedSlice; - - /// Returns the dynamic function trampolines allocated in memory - /// of this `Artifact`, ready to be run. - fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice; - - /// Returns the associated VM signatures for this `Artifact`. - fn signatures(&self) -> &BoxedSlice; - - /// Get the func data registry - fn func_data_registry(&self) -> &FuncDataRegistry; - - /// Serializes an artifact into bytes - fn serialize(&self) -> Result, SerializeError>; - - /// Serializes an artifact into a file path - fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { - let serialized = self.serialize()?; - fs::write(&path, serialized)?; - Ok(()) - } - - /// Do preinstantiation logic that is executed before instantiating - fn preinstantiate(&self) -> Result<(), InstantiationError> { - Ok(()) - } - - /// Crate an `Instance` from this `Artifact`. - /// - /// # Safety - /// - /// See [`InstanceHandle::new`]. - unsafe fn instantiate( - &self, - tunables: &dyn Tunables, - resolver: &dyn Resolver, - host_state: Box, - config: InstanceConfig, - ) -> Result { - self.preinstantiate()?; - let module = self.module(); - let (imports, import_function_envs) = { - let mut imports = resolve_imports( - &module, - resolver, - &self.finished_dynamic_function_trampolines(), - self.memory_styles(), - self.table_styles(), - ) - .map_err(InstantiationError::Link)?; - - // Get the `WasmerEnv::init_with_instance` function pointers and the pointers - // to the envs to call it on. - let import_function_envs = imports.get_imported_function_envs(); - - (imports, import_function_envs) - }; - - // Get pointers to where metadata about local memories should live in VM memory. - // Get pointers to where metadata about local tables should live in VM memory. - - let (allocator, memory_definition_locations, table_definition_locations) = - InstanceAllocator::new(&*module); - let finished_memories = tunables - .create_memories(&module, self.memory_styles(), &memory_definition_locations) - .map_err(InstantiationError::Link)? - .into_boxed_slice(); - let finished_tables = tunables - .create_tables(&module, self.table_styles(), &table_definition_locations) - .map_err(InstantiationError::Link)? - .into_boxed_slice(); - let finished_globals = tunables - .create_globals(&module) - .map_err(InstantiationError::Link)? - .into_boxed_slice(); - - self.register_frame_info(); - - let handle = InstanceHandle::new( - allocator, - module, - self.finished_functions().clone(), - self.finished_functions_lengths().clone(), - self.finished_function_call_trampolines().clone(), - finished_memories, - finished_tables, - finished_globals, - imports, - self.signatures().clone(), - host_state, - import_function_envs, - config, - ) - .map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))?; - Ok(handle) - } - - /// Finishes the instantiation of a just created `InstanceHandle`. - /// - /// # Safety - /// - /// See [`InstanceHandle::finish_instantiation`]. - unsafe fn finish_instantiation( - &self, - trap_handler: &dyn TrapHandler, - handle: &InstanceHandle, - ) -> Result<(), InstantiationError> { - let data_initializers = self - .data_initializers() - .iter() - .map(|init| DataInitializer { - location: init.location.clone(), - data: &*init.data, - }) - .collect::>(); - handle - .finish_instantiation(trap_handler, &data_initializers) - .map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap))) - } -} - -// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 . -/// Trait needed to get downcasting of `Engine`s to work. -pub trait Upcastable { - fn upcast_any_ref(&'_ self) -> &'_ dyn Any; - fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any; - fn upcast_any_box(self: Box) -> Box; -} - -impl Upcastable for T { - #[inline] - fn upcast_any_ref(&'_ self) -> &'_ dyn Any { - self - } - #[inline] - fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any { - self - } - #[inline] - fn upcast_any_box(self: Box) -> Box { - self - } -} - -impl dyn Artifact + 'static { - /// Try to downcast the artifact into a given type. - #[inline] - pub fn downcast_ref(&'_ self) -> Option<&'_ T> { - self.upcast_any_ref().downcast_ref::() - } - - /// Try to downcast the artifact into a given type mutably. - #[inline] - pub fn downcast_mut(&'_ mut self) -> Option<&'_ mut T> { - self.upcast_any_mut().downcast_mut::() - } -} diff --git a/lib/engine/src/engine.rs b/lib/engine/src/engine.rs index f296526901..2e4e48ffde 100644 --- a/lib/engine/src/engine.rs +++ b/lib/engine/src/engine.rs @@ -1,15 +1,14 @@ //! Engine trait and associated types. -use crate::tunables::Tunables; -use crate::{Artifact, DeserializeError}; -use loupe::MemoryUsage; -use memmap2::Mmap; -use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; use wasmer_compiler::{CompileError, Target}; -use wasmer_types::FunctionType; -use wasmer_vm::{VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex}; +use wasmer_types::{FunctionType, FunctionTypeRef}; +use wasmer_vm::{Artifact, Tunables, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex}; + +mod private { + pub struct Internal(pub(super) ()); +} /// A unimplemented Wasmer `Engine`. /// @@ -17,12 +16,12 @@ use wasmer_vm::{VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex}; /// such as: Universal or Native. /// /// The product that an `Engine` produces and consumes is the [`Artifact`]. -pub trait Engine: MemoryUsage { +pub trait Engine { /// Gets the target fn target(&self) -> &Target; /// Register a signature - fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex; + fn register_signature(&self, func_type: FunctionTypeRef<'_>) -> VMSharedSignatureIndex; /// Register a function's data. fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef; @@ -38,31 +37,11 @@ pub trait Engine: MemoryUsage { &self, binary: &[u8], tunables: &dyn Tunables, - ) -> Result, CompileError>; - - /// If this engine needs to install POSIX signal handlers. - fn use_signals(&self) -> bool; - - /// Deserializes a WebAssembly module - /// - /// # Safety - /// - /// The serialized content must represent a serialized WebAssembly module. - unsafe fn deserialize(&self, bytes: &[u8]) -> Result, DeserializeError>; + ) -> Result, CompileError>; - /// Deserializes a WebAssembly module from a path - /// - /// # Safety - /// - /// The file's content must represent a serialized WebAssembly module. - unsafe fn deserialize_from_file( - &self, - file_ref: &Path, - ) -> Result, DeserializeError> { - let file = std::fs::File::open(file_ref)?; - let mmap = Mmap::map(&file)?; - self.deserialize(&mmap) - } + /// Load a compiled executable with this engine. + fn load(&self, executable: &(dyn crate::Executable)) + -> Result, CompileError>; /// A unique identifier for this object. /// @@ -73,9 +52,18 @@ pub trait Engine: MemoryUsage { /// Clone the engine fn cloned(&self) -> Arc; + + /// Internal: support for downcasting `Engine`s. + #[doc(hidden)] + fn type_id(&self, _: private::Internal) -> std::any::TypeId + where + Self: 'static, + { + std::any::TypeId::of::() + } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, MemoryUsage)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] /// A unique identifier for an Engine. pub struct EngineId { @@ -103,3 +91,14 @@ impl Default for EngineId { } } } + +impl dyn Engine { + /// Downcast a dynamic Executable object to a concrete implementation of the trait. + pub fn downcast_ref(&self) -> Option<&T> { + if std::any::TypeId::of::() == self.type_id(private::Internal(())) { + unsafe { Some(&*(self as *const dyn Engine as *const T)) } + } else { + None + } + } +} diff --git a/lib/engine/src/error.rs b/lib/engine/src/error.rs index 5874e5d8be..a2826cb55e 100644 --- a/lib/engine/src/error.rs +++ b/lib/engine/src/error.rs @@ -5,18 +5,6 @@ use thiserror::Error; use wasmer_compiler::CompileError; use wasmer_types::ExternType; -/// The Serialize error can occur when serializing a -/// compiled Module into a binary. -#[derive(Error, Debug)] -pub enum SerializeError { - /// An IO error - #[error(transparent)] - Io(#[from] io::Error), - /// A generic serialization error - #[error("{0}")] - Generic(String), -} - /// The Deserialize error can occur when loading a /// compiled Module from a binary. #[derive(Error, Debug)] @@ -96,6 +84,10 @@ pub enum InstantiationError { #[error("module compiled with CPU feature that is missing from host")] CpuFeature(String), + /// Could not create an instance. + #[error("could not create instance")] + CreateInstance(RuntimeError), + /// A runtime error occured while invoking the start function #[error(transparent)] Start(RuntimeError), diff --git a/lib/engine/src/executable.rs b/lib/engine/src/executable.rs new file mode 100644 index 0000000000..9b5c17761e --- /dev/null +++ b/lib/engine/src/executable.rs @@ -0,0 +1,57 @@ +use crate::Engine; +use enumset::EnumSet; +use wasmer_compiler::{CompileError, CpuFeature, Features}; +use wasmer_types::FunctionIndex; +use wasmer_vm::Artifact; + +mod private { + pub struct Internal(pub(super) ()); +} + +/// A WASM module built by some [`Engine`](crate::Engine). +/// +/// Types implementing this trait are ready to be saved (to e.g. disk) for later use or loaded with +/// the `Engine` to in order to produce an [`Artifact`](crate::Artifact). +pub trait Executable { + /// Load this executable with the specified engine. + /// + /// TODO(0-copy): change error type here. + fn load( + &self, + engine: &(dyn Engine + 'static), + ) -> Result, CompileError>; + + /// The features with which this `Executable` was built. + fn features(&self) -> Features; + + /// The CPU features this `Executable` requires. + fn cpu_features(&self) -> EnumSet; + + /// Serializes the artifact into bytes + fn serialize(&self) -> Result, Box>; + + /// Obtain a best effort description for the function at the given function index. + /// + /// Implementations are not required to maintain symbol names, so this may always return None. + fn function_name(&self, index: FunctionIndex) -> Option<&str>; + + /// Internal: support for downcasting `Executable`s. + #[doc(hidden)] + fn type_id(&self, _: private::Internal) -> std::any::TypeId + where + Self: 'static, + { + std::any::TypeId::of::() + } +} + +impl dyn Executable { + /// Downcast a dynamic Executable object to a concrete implementation of the trait. + pub fn downcast_ref(&self) -> Option<&T> { + if std::any::TypeId::of::() == self.type_id(private::Internal(())) { + unsafe { Some(&*(self as *const dyn Executable as *const T)) } + } else { + None + } + } +} diff --git a/lib/engine/src/export.rs b/lib/engine/src/export.rs index 62f3bde2cc..5bb83ac645 100644 --- a/lib/engine/src/export.rs +++ b/lib/engine/src/export.rs @@ -1,170 +1,3 @@ -use loupe::MemoryUsage; use std::sync::Arc; use wasmer_vm::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable}; -/// The value of an export passed from one instance to another. -#[derive(Debug, Clone)] -pub enum Export { - /// A function export value. - Function(ExportFunction), - - /// A table export value. - Table(VMTable), - - /// A memory export value. - Memory(VMMemory), - - /// A global export value. - Global(VMGlobal), -} - -impl From for VMExtern { - fn from(other: Export) -> Self { - match other { - Export::Function(ExportFunction { vm_function, .. }) => Self::Function(vm_function), - Export::Memory(vm_memory) => Self::Memory(vm_memory), - Export::Table(vm_table) => Self::Table(vm_table), - Export::Global(vm_global) => Self::Global(vm_global), - } - } -} - -impl From for Export { - fn from(other: VMExtern) -> Self { - match other { - VMExtern::Function(vm_function) => Self::Function(ExportFunction { - vm_function, - metadata: None, - }), - VMExtern::Memory(vm_memory) => Self::Memory(vm_memory), - VMExtern::Table(vm_table) => Self::Table(vm_table), - VMExtern::Global(vm_global) => Self::Global(vm_global), - } - } -} - -/// Extra metadata about `ExportFunction`s. -/// -/// The metadata acts as a kind of manual virtual dispatch. We store the -/// user-supplied `WasmerEnv` as a void pointer and have methods on it -/// that have been adapted to accept a void pointer. -/// -/// This struct owns the original `host_env`, thus when it gets dropped -/// it calls the `drop` function on it. -#[derive(Debug, PartialEq, MemoryUsage)] -pub struct ExportFunctionMetadata { - /// This field is stored here to be accessible by `Drop`. - /// - /// At the time it was added, it's not accessed anywhere outside of - /// the `Drop` implementation. This field is the "master copy" of the env, - /// that is, the original env passed in by the user. Every time we create - /// an `Instance` we clone this with the `host_env_clone_fn` field. - /// - /// Thus, we only bother to store the master copy at all here so that - /// we can free it. - /// - /// See `wasmer_vm::export::VMFunction::vmctx` for the version of - /// this pointer that is used by the VM when creating an `Instance`. - pub(crate) host_env: *mut std::ffi::c_void, - - /// Function pointer to `WasmerEnv::init_with_instance(&mut self, instance: &Instance)`. - /// - /// This function is called to finish setting up the environment after - /// we create the `api::Instance`. - // This one is optional for now because dynamic host envs need the rest - // of this without the init fn - #[loupe(skip)] - pub(crate) import_init_function_ptr: Option, - - /// A function analogous to `Clone::clone` that returns a leaked `Box`. - #[loupe(skip)] - pub(crate) host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, - - /// The destructor to free the host environment. - /// - /// # Safety - /// - This function should only be called in when properly synchronized. - /// For example, in the `Drop` implementation of this type. - #[loupe(skip)] - pub(crate) host_env_drop_fn: unsafe fn(*mut std::ffi::c_void), -} - -/// This can be `Send` because `host_env` comes from `WasmerEnv` which is -/// `Send`. Therefore all operations should work on any thread. -unsafe impl Send for ExportFunctionMetadata {} -/// This data may be shared across threads, `drop` is an unsafe function -/// pointer, so care must be taken when calling it. -unsafe impl Sync for ExportFunctionMetadata {} - -impl ExportFunctionMetadata { - /// Create an `ExportFunctionMetadata` type with information about - /// the exported function. - /// - /// # Safety - /// - the `host_env` must be `Send`. - /// - all function pointers must work on any thread. - pub unsafe fn new( - host_env: *mut std::ffi::c_void, - import_init_function_ptr: Option, - host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, - host_env_drop_fn: fn(*mut std::ffi::c_void), - ) -> Self { - Self { - host_env, - import_init_function_ptr, - host_env_clone_fn, - host_env_drop_fn, - } - } -} - -// We have to free `host_env` here because we always clone it before using it -// so all the `host_env`s freed at the `Instance` level won't touch the original. -impl Drop for ExportFunctionMetadata { - fn drop(&mut self) { - if !self.host_env.is_null() { - // # Safety - // - This is correct because we know no other references - // to this data can exist if we're dropping it. - unsafe { - (self.host_env_drop_fn)(self.host_env); - } - } - } -} - -/// A function export value with an extra function pointer to initialize -/// host environments. -#[derive(Debug, Clone, PartialEq, MemoryUsage)] -pub struct ExportFunction { - /// The VM function, containing most of the data. - pub vm_function: VMFunction, - /// Contains functions necessary to create and initialize host envs - /// with each `Instance` as well as being responsible for the - /// underlying memory of the host env. - pub metadata: Option>, -} - -impl From for Export { - fn from(func: ExportFunction) -> Self { - Self::Function(func) - } -} - -impl From for Export { - fn from(table: VMTable) -> Self { - Self::Table(table) - } -} - -impl From for Export { - fn from(memory: VMMemory) -> Self { - Self::Memory(memory) - } -} - -impl From for Export { - fn from(global: VMGlobal) -> Self { - Self::Global(global) - } -} diff --git a/lib/engine/src/lib.rs b/lib/engine/src/lib.rs index 9cc5f1a68b..9746f1013f 100644 --- a/lib/engine/src/lib.rs +++ b/lib/engine/src/lib.rs @@ -20,26 +20,17 @@ ) )] -mod artifact; mod engine; mod error; -mod export; +mod executable; mod resolver; mod trap; -mod tunables; -pub use crate::artifact::Artifact; pub use crate::engine::{Engine, EngineId}; -pub use crate::error::{ - DeserializeError, ImportError, InstantiationError, LinkError, SerializeError, -}; -pub use crate::export::{Export, ExportFunction, ExportFunctionMetadata}; -pub use crate::resolver::{ - resolve_imports, ChainableNamedResolver, NamedResolver, NamedResolverChain, NullResolver, - Resolver, -}; +pub use crate::error::{DeserializeError, ImportError, InstantiationError, LinkError}; +pub use crate::executable::Executable; +pub use crate::resolver::resolve_imports; pub use crate::trap::*; -pub use crate::tunables::Tunables; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/engine/src/resolver.rs b/lib/engine/src/resolver.rs index 1a60f8992a..040019ca96 100644 --- a/lib/engine/src/resolver.rs +++ b/lib/engine/src/resolver.rs @@ -1,124 +1,29 @@ //! Define the `Resolver` trait, allowing custom resolution for external //! references. -use crate::{Export, ExportFunctionMetadata, ImportError, LinkError}; +use crate::{Engine, ImportError, LinkError}; use more_asserts::assert_ge; use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; -use wasmer_types::{ExternType, FunctionIndex, ImportIndex, MemoryIndex, ModuleInfo, TableIndex}; +use wasmer_types::{ExternType, FunctionIndex, ImportCounts, MemoryType, TableType}; use wasmer_vm::{ - FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle, TableStyle, VMFunctionBody, - VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalImport, VMMemoryImport, - VMTableImport, + Export, ExportFunctionMetadata, FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle, + Resolver, VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, + VMGlobalImport, VMImport, VMImportType, VMMemoryImport, VMTableImport, }; -/// Import resolver connects imports with available exported values. -pub trait Resolver { - /// Resolves an import a WebAssembly module to an export it's hooked up to. - /// - /// The `index` provided is the index of the import in the wasm module - /// that's being resolved. For example 1 means that it's the second import - /// listed in the wasm module. - /// - /// The `module` and `field` arguments provided are the module/field names - /// listed on the import itself. - /// - /// # Notes: - /// - /// The index is useful because some WebAssembly modules may rely on that - /// for resolving ambiguity in their imports. Such as: - /// ```ignore - /// (module - /// (import "" "" (func)) - /// (import "" "" (func (param i32) (result i32))) - /// ) - /// ``` - fn resolve(&self, _index: u32, module: &str, field: &str) -> Option; +fn is_compatible_table(ex: &TableType, im: &TableType) -> bool { + (ex.ty == wasmer_types::Type::FuncRef || ex.ty == im.ty) + && im.minimum <= ex.minimum + && (im.maximum.is_none() + || (!ex.maximum.is_none() && im.maximum.unwrap() >= ex.maximum.unwrap())) } -/// Import resolver connects imports with available exported values. -/// -/// This is a specific subtrait for [`Resolver`] for those users who don't -/// care about the `index`, but only about the `module` and `field` for -/// the resolution. -pub trait NamedResolver { - /// Resolves an import a WebAssembly module to an export it's hooked up to. - /// - /// It receives the `module` and `field` names and return the [`Export`] in - /// case it's found. - fn resolve_by_name(&self, module: &str, field: &str) -> Option; -} - -// All NamedResolvers should extend `Resolver`. -impl Resolver for T { - /// By default this method will be calling [`NamedResolver::resolve_by_name`], - /// dismissing the provided `index`. - fn resolve(&self, _index: u32, module: &str, field: &str) -> Option { - self.resolve_by_name(module, field) - } -} - -impl NamedResolver for &T { - fn resolve_by_name(&self, module: &str, field: &str) -> Option { - (**self).resolve_by_name(module, field) - } -} - -impl NamedResolver for Box { - fn resolve_by_name(&self, module: &str, field: &str) -> Option { - (**self).resolve_by_name(module, field) - } -} - -impl NamedResolver for () { - /// Always returns `None`. - fn resolve_by_name(&self, _module: &str, _field: &str) -> Option { - None - } -} - -/// `Resolver` implementation that always resolves to `None`. Equivalent to `()`. -pub struct NullResolver {} - -impl Resolver for NullResolver { - fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option { - None - } -} - -/// Get an `ExternType` given a import index. -fn get_extern_from_import(module: &ModuleInfo, import_index: &ImportIndex) -> ExternType { - match import_index { - ImportIndex::Function(index) => { - let func = module.signatures[module.functions[*index]].clone(); - ExternType::Function(func) - } - ImportIndex::Table(index) => { - let table = module.tables[*index]; - ExternType::Table(table) - } - ImportIndex::Memory(index) => { - let memory = module.memories[*index]; - ExternType::Memory(memory) - } - ImportIndex::Global(index) => { - let global = module.globals[*index]; - ExternType::Global(global) - } - } -} - -/// Get an `ExternType` given an export (and Engine signatures in case is a function). -fn get_extern_from_export(_module: &ModuleInfo, export: &Export) -> ExternType { - match export { - Export::Function(ref f) => ExternType::Function(f.vm_function.signature.clone()), - Export::Table(ref t) => ExternType::Table(*t.ty()), - Export::Memory(ref m) => ExternType::Memory(m.ty()), - Export::Global(ref g) => { - let global = g.from.ty(); - ExternType::Global(*global) - } - } +fn is_compatible_memory(ex: &MemoryType, im: &MemoryType) -> bool { + im.minimum <= ex.minimum + && (im.maximum.is_none() + || (!ex.maximum.is_none() && im.maximum.unwrap() >= ex.maximum.unwrap())) + && ex.shared == im.shared } /// This function allows to match all imports of a `ModuleInfo` with concrete definitions provided by @@ -126,43 +31,65 @@ fn get_extern_from_export(_module: &ModuleInfo, export: &Export) -> ExternType { /// /// If all imports are satisfied returns an `Imports` instance required for a module instantiation. pub fn resolve_imports( - module: &ModuleInfo, + engine: &dyn Engine, resolver: &dyn Resolver, + import_counts: &ImportCounts, + imports: &[VMImport], finished_dynamic_function_trampolines: &BoxedSlice, - memory_styles: &PrimaryMap, - _table_styles: &PrimaryMap, ) -> Result { - let mut function_imports = PrimaryMap::with_capacity(module.num_imported_functions); + let mut function_imports = PrimaryMap::with_capacity(import_counts.functions as _); let mut host_function_env_initializers = - PrimaryMap::with_capacity(module.num_imported_functions); - let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables); - let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories); - let mut global_imports = PrimaryMap::with_capacity(module.num_imported_globals); - - for ((module_name, field, import_idx), import_index) in module.imports.iter() { - let resolved = resolver.resolve(*import_idx, module_name, field); - let import_extern = get_extern_from_import(module, import_index); + PrimaryMap::with_capacity(import_counts.functions as _); + let mut table_imports = PrimaryMap::with_capacity(import_counts.tables as _); + let mut memory_imports = PrimaryMap::with_capacity(import_counts.memories as _); + let mut global_imports = PrimaryMap::with_capacity(import_counts.globals as _); + for VMImport { + import_no, + module, + field, + ty, + } in imports + { + let resolved = resolver.resolve(*import_no, module, field); + let import_extern = || match ty { + &VMImportType::Table(t) => ExternType::Table(t), + &VMImportType::Memory(t, _) => ExternType::Memory(t), + &VMImportType::Global(t) => ExternType::Global(t), + &VMImportType::Function(sig_idx) => ExternType::Function( + engine + .lookup_signature(sig_idx) + .expect("VMSharedSignatureIndex is not valid?"), + ), + }; let resolved = match resolved { + Some(r) => r, None => { return Err(LinkError::Import( - module_name.to_string(), + module.to_string(), field.to_string(), - ImportError::UnknownImport(import_extern), + ImportError::UnknownImport(import_extern()), )); } - Some(r) => r, }; - let export_extern = get_extern_from_export(module, &resolved); - if !export_extern.is_compatible_with(&import_extern) { - return Err(LinkError::Import( - module_name.to_string(), - field.to_string(), - ImportError::IncompatibleType(import_extern, export_extern), - )); - } - match resolved { - Export::Function(ref f) => { - let address = match f.vm_function.kind { + let export_extern = || match resolved { + Export::Function(ref f) => ExternType::Function( + engine + .lookup_signature(f.vm_function.signature) + .expect("VMSharedSignatureIndex not registered with engine (wrong engine?)") + .clone(), + ), + Export::Table(ref t) => ExternType::Table(*t.ty()), + Export::Memory(ref m) => ExternType::Memory(m.ty()), + Export::Global(ref g) => { + let global = g.from.ty(); + ExternType::Global(*global) + } + }; + match (&resolved, ty) { + (Export::Function(ex), VMImportType::Function(im)) + if ex.vm_function.signature == *im => + { + let address = match ex.vm_function.kind { VMFunctionKind::Dynamic => { // If this is a dynamic imported function, // the address of the function is the address of the @@ -173,37 +100,42 @@ pub fn resolve_imports( // TODO: We should check that the f.vmctx actually matches // the shape of `VMDynamicFunctionImportContext` } - VMFunctionKind::Static => f.vm_function.address, + VMFunctionKind::Static => ex.vm_function.address, }; // Clone the host env for this `Instance`. let env = if let Some(ExportFunctionMetadata { host_env_clone_fn: clone, .. - }) = f.metadata.as_deref() + }) = ex.metadata.as_deref() { // TODO: maybe start adding asserts in all these // unsafe blocks to prevent future changes from // horribly breaking things. unsafe { - assert!(!f.vm_function.vmctx.host_env.is_null()); - (clone)(f.vm_function.vmctx.host_env) + assert!(!ex.vm_function.vmctx.host_env.is_null()); + (clone)(ex.vm_function.vmctx.host_env) } } else { // No `clone` function means we're dealing with some // other kind of `vmctx`, not a host env of any // kind. - unsafe { f.vm_function.vmctx.host_env } + unsafe { ex.vm_function.vmctx.host_env } }; function_imports.push(VMFunctionImport { - body: address, + body: FunctionBodyPtr(address), + signature: *im, environment: VMFunctionEnvironment { host_env: env }, + trampoline: ex.vm_function.call_trampoline, }); - let initializer = f.metadata.as_ref().and_then(|m| m.import_init_function_ptr); - let clone = f.metadata.as_ref().map(|m| m.host_env_clone_fn); - let destructor = f.metadata.as_ref().map(|m| m.host_env_drop_fn); + let initializer = ex + .metadata + .as_ref() + .and_then(|m| m.import_init_function_ptr); + let clone = ex.metadata.as_ref().map(|m| m.host_env_clone_fn); + let destructor = ex.metadata.as_ref().map(|m| m.host_env_drop_fn); let import_function_env = if let (Some(clone), Some(destructor)) = (clone, destructor) { ImportFunctionEnv::Env { @@ -218,71 +150,61 @@ pub fn resolve_imports( host_function_env_initializers.push(import_function_env); } - Export::Table(ref t) => match import_index { - ImportIndex::Table(index) => { - let import_table_ty = t.from.ty(); - let expected_table_ty = &module.tables[*index]; - if import_table_ty.ty != expected_table_ty.ty { - return Err(LinkError::Import( - module_name.to_string(), - field.to_string(), - ImportError::IncompatibleType(import_extern, export_extern), - )); - } - - table_imports.push(VMTableImport { - definition: t.from.vmtable(), - from: t.from.clone(), - }); + (Export::Table(ex), VMImportType::Table(im)) if is_compatible_table(ex.ty(), im) => { + let import_table_ty = ex.from.ty(); + if import_table_ty.ty != im.ty { + return Err(LinkError::Import( + module.to_string(), + field.to_string(), + ImportError::IncompatibleType(import_extern(), export_extern()), + )); } - _ => { - unreachable!("Table resolution did not match"); - } - }, - Export::Memory(ref m) => { - match import_index { - ImportIndex::Memory(index) => { - // Sanity-check: Ensure that the imported memory has at least - // guard-page protections the importing module expects it to have. - let export_memory_style = m.style(); - let import_memory_style = &memory_styles[*index]; - if let ( - MemoryStyle::Static { bound, .. }, - MemoryStyle::Static { - bound: import_bound, - .. - }, - ) = (export_memory_style.clone(), &import_memory_style) - { - assert_ge!(bound, *import_bound); - } - assert_ge!( - export_memory_style.offset_guard_size(), - import_memory_style.offset_guard_size() - ); - } - _ => { - // This should never be reached, as we did compatibility - // checks before - panic!("Memory resolution didn't matched"); - } + table_imports.push(VMTableImport { + definition: ex.from.vmtable(), + from: ex.from.clone(), + }); + } + (Export::Memory(ex), VMImportType::Memory(im, import_memory_style)) + if is_compatible_memory(&ex.ty(), im) => + { + // Sanity-check: Ensure that the imported memory has at least + // guard-page protections the importing module expects it to have. + let export_memory_style = ex.style(); + if let ( + MemoryStyle::Static { bound, .. }, + MemoryStyle::Static { + bound: import_bound, + .. + }, + ) = (export_memory_style.clone(), &import_memory_style) + { + assert_ge!(bound, *import_bound); } - + assert_ge!( + export_memory_style.offset_guard_size(), + import_memory_style.offset_guard_size() + ); memory_imports.push(VMMemoryImport { - definition: m.from.vmmemory(), - from: m.from.clone(), + definition: ex.from.vmmemory(), + from: ex.from.clone(), }); } - Export::Global(ref g) => { + (Export::Global(ex), VMImportType::Global(im)) if ex.from.ty() == im => { global_imports.push(VMGlobalImport { - definition: g.from.vmglobal(), - from: g.from.clone(), + definition: ex.from.vmglobal(), + from: ex.from.clone(), }); } + _ => { + return Err(LinkError::Import( + module.to_string(), + field.to_string(), + ImportError::IncompatibleType(import_extern(), export_extern()), + )); + } } } - Ok(Imports::new( function_imports, host_function_env_initializers, @@ -291,93 +213,3 @@ pub fn resolve_imports( global_imports, )) } - -/// A [`Resolver`] that links two resolvers together in a chain. -pub struct NamedResolverChain { - a: A, - b: B, -} - -/// A trait for chaining resolvers together. -/// -/// ``` -/// # use wasmer_engine::{ChainableNamedResolver, NamedResolver}; -/// # fn chainable_test(imports1: A, imports2: B) -/// # where A: NamedResolver + Sized + Send + Sync, -/// # B: NamedResolver + Sized + Send + Sync, -/// # { -/// // override duplicates with imports from `imports2` -/// imports1.chain_front(imports2); -/// # } -/// ``` -pub trait ChainableNamedResolver: NamedResolver + Sized + Send + Sync { - /// Chain a resolver in front of the current resolver. - /// - /// This will cause the second resolver to override the first. - /// - /// ``` - /// # use wasmer_engine::{ChainableNamedResolver, NamedResolver}; - /// # fn chainable_test(imports1: A, imports2: B) - /// # where A: NamedResolver + Sized + Send + Sync, - /// # B: NamedResolver + Sized + Send + Sync, - /// # { - /// // override duplicates with imports from `imports2` - /// imports1.chain_front(imports2); - /// # } - /// ``` - fn chain_front(self, other: U) -> NamedResolverChain - where - U: NamedResolver + Send + Sync, - { - NamedResolverChain { a: other, b: self } - } - - /// Chain a resolver behind the current resolver. - /// - /// This will cause the first resolver to override the second. - /// - /// ``` - /// # use wasmer_engine::{ChainableNamedResolver, NamedResolver}; - /// # fn chainable_test(imports1: A, imports2: B) - /// # where A: NamedResolver + Sized + Send + Sync, - /// # B: NamedResolver + Sized + Send + Sync, - /// # { - /// // override duplicates with imports from `imports1` - /// imports1.chain_back(imports2); - /// # } - /// ``` - fn chain_back(self, other: U) -> NamedResolverChain - where - U: NamedResolver + Send + Sync, - { - NamedResolverChain { a: self, b: other } - } -} - -// We give these chain methods to all types implementing NamedResolver -impl ChainableNamedResolver for T {} - -impl NamedResolver for NamedResolverChain -where - A: NamedResolver + Send + Sync, - B: NamedResolver + Send + Sync, -{ - fn resolve_by_name(&self, module: &str, field: &str) -> Option { - self.a - .resolve_by_name(module, field) - .or_else(|| self.b.resolve_by_name(module, field)) - } -} - -impl Clone for NamedResolverChain -where - A: NamedResolver + Clone + Send + Sync, - B: NamedResolver + Clone + Send + Sync, -{ - fn clone(&self) -> Self { - Self { - a: self.a.clone(), - b: self.b.clone(), - } - } -} diff --git a/lib/engine/src/trap/frame_info.rs b/lib/engine/src/trap/frame_info.rs index 9b7e4df95c..10a6c4f89c 100644 --- a/lib/engine/src/trap/frame_info.rs +++ b/lib/engine/src/trap/frame_info.rs @@ -11,14 +11,11 @@ //! let module: ModuleInfo = ...; //! FRAME_INFO.register(module, compiled_functions); //! ``` -use loupe::MemoryUsage; -use std::cmp; use std::collections::BTreeMap; use std::sync::{Arc, RwLock}; use wasmer_compiler::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation}; -use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; +use wasmer_types::entity::{EntityRef, PrimaryMap}; use wasmer_types::{LocalFunctionIndex, ModuleInfo}; -use wasmer_vm::FunctionBodyPtr; lazy_static::lazy_static! { /// This is a global cache of backtrace frame information for all active @@ -43,17 +40,8 @@ pub struct GlobalFrameInfo { ranges: BTreeMap, } -/// Returns whether the `pc`, according to globally registered information, -/// is a wasm trap or not. -pub fn is_wasm_pc(pc: usize) -> bool { - let frame_info = FRAME_INFO.read().unwrap(); - let module_info = frame_info.module_info(pc); - module_info.is_some() -} - /// An RAII structure used to unregister a module's frame information when the /// module is destroyed. -#[derive(MemoryUsage)] pub struct GlobalFrameInfoRegistration { /// The key that will be removed from the global `ranges` map when this is /// dropped. @@ -178,78 +166,6 @@ impl Drop for GlobalFrameInfoRegistration { } } -/// Represents a continuous region of executable memory starting with a function -/// entry point. -#[derive(Debug)] -#[repr(C)] -pub struct FunctionExtent { - /// Entry point for normal entry of the function. All addresses in the - /// function lie after this address. - pub ptr: FunctionBodyPtr, - /// Length in bytes. - pub length: usize, -} - -/// Registers a new compiled module's frame information. -/// -/// This function will register the `names` information for all of the -/// compiled functions within `module`. If the `module` has no functions -/// then `None` will be returned. Otherwise the returned object, when -/// dropped, will be used to unregister all name information from this map. -pub fn register( - module: Arc, - finished_functions: &BoxedSlice, - frame_infos: PrimaryMap, -) -> Option { - let mut min = usize::max_value(); - let mut max = 0; - let mut functions = BTreeMap::new(); - for ( - i, - FunctionExtent { - ptr: start, - length: len, - }, - ) in finished_functions.iter() - { - let start = **start as usize; - let end = start + len; - min = cmp::min(min, start); - max = cmp::max(max, end); - let func = FunctionInfo { - start, - local_index: i, - }; - assert!(functions.insert(end, func).is_none()); - } - if functions.is_empty() { - return None; - } - - let mut info = FRAME_INFO.write().unwrap(); - // First up assert that our chunk of jit functions doesn't collide with - // any other known chunks of jit functions... - if let Some((_, prev)) = info.ranges.range(max..).next() { - assert!(prev.start > max); - } - if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() { - assert!(*prev_end < min); - } - - // ... then insert our range and assert nothing was there previously - let prev = info.ranges.insert( - max, - ModuleInfoFrameInfo { - start: min, - functions, - module, - frame_infos, - }, - ); - assert!(prev.is_none()); - Some(GlobalFrameInfoRegistration { key: max }) -} - /// Description of a frame in a backtrace for a [`RuntimeError::trace`](crate::RuntimeError::trace). /// /// Whenever a WebAssembly trap occurs an instance of [`RuntimeError`] diff --git a/lib/engine/src/trap/mod.rs b/lib/engine/src/trap/mod.rs index ca7b548c69..3144b41cff 100644 --- a/lib/engine/src/trap/mod.rs +++ b/lib/engine/src/trap/mod.rs @@ -1,7 +1,4 @@ mod error; mod frame_info; pub use error::RuntimeError; -pub use frame_info::{ - is_wasm_pc, register as register_frame_info, FrameInfo, FunctionExtent, - GlobalFrameInfoRegistration, FRAME_INFO, -}; +pub use frame_info::{FrameInfo, GlobalFrameInfoRegistration}; diff --git a/lib/engine/src/tunables.rs b/lib/engine/src/tunables.rs deleted file mode 100644 index 239bd7676f..0000000000 --- a/lib/engine/src/tunables.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::error::LinkError; -use loupe::MemoryUsage; -use std::ptr::NonNull; -use std::sync::Arc; -use wasmer_types::entity::{EntityRef, PrimaryMap}; -use wasmer_types::{ - GlobalType, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, - ModuleInfo, TableIndex, TableType, -}; -use wasmer_vm::MemoryError; -use wasmer_vm::{Global, Memory, Table}; -use wasmer_vm::{MemoryStyle, TableStyle}; -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: MemoryUsage { - /// Construct a `MemoryStyle` for the provided `MemoryType` - fn memory_style(&self, memory: &MemoryType) -> MemoryStyle; - - /// Construct a `TableStyle` for the provided `TableType` - fn table_style(&self, table: &TableType) -> TableStyle; - - /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`]. - fn create_host_memory( - &self, - ty: &MemoryType, - style: &MemoryStyle, - ) -> Result, MemoryError>; - - /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`]. - /// - /// # Safety - /// - `vm_definition_location` must point to a valid location in VM memory. - unsafe fn create_vm_memory( - &self, - ty: &MemoryType, - style: &MemoryStyle, - vm_definition_location: NonNull, - ) -> Result, MemoryError>; - - /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. - fn create_host_table( - &self, - ty: &TableType, - style: &TableStyle, - ) -> Result, String>; - - /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. - /// - /// # Safety - /// - `vm_definition_location` must point to a valid location in VM memory. - unsafe fn create_vm_table( - &self, - ty: &TableType, - style: &TableStyle, - vm_definition_location: NonNull, - ) -> Result, String>; - - /// Create a global with an unset value. - fn create_global(&self, ty: GlobalType) -> Result, String> { - Ok(Arc::new(Global::new(ty))) - } - - /// Allocate memory for just the memories of the current module. - unsafe fn create_memories( - &self, - module: &ModuleInfo, - memory_styles: &PrimaryMap, - memory_definition_locations: &[NonNull], - ) -> Result>, LinkError> { - let num_imports = module.num_imported_memories; - let mut memories: PrimaryMap = - PrimaryMap::with_capacity(module.memories.len() - num_imports); - for index in num_imports..module.memories.len() { - let mi = MemoryIndex::new(index); - let ty = &module.memories[mi]; - let style = &memory_styles[mi]; - let mdl = memory_definition_locations[index]; - memories.push( - self.create_vm_memory(ty, style, mdl) - .map_err(|e| LinkError::Resource(format!("Failed to create memory: {}", e)))?, - ); - } - Ok(memories) - } - - /// Allocate memory for just the tables of the current module. - unsafe fn create_tables( - &self, - module: &ModuleInfo, - table_styles: &PrimaryMap, - table_definition_locations: &[NonNull], - ) -> Result>, LinkError> { - let num_imports = module.num_imported_tables; - let mut tables: PrimaryMap = - PrimaryMap::with_capacity(module.tables.len() - num_imports); - for index in num_imports..module.tables.len() { - let ti = TableIndex::new(index); - let ty = &module.tables[ti]; - let style = &table_styles[ti]; - let tdl = table_definition_locations[index]; - tables.push( - self.create_vm_table(ty, style, tdl) - .map_err(LinkError::Resource)?, - ); - } - Ok(tables) - } - - /// Allocate memory for just the globals of the current module, - /// with initializers applied. - fn create_globals( - &self, - module: &ModuleInfo, - ) -> Result>, LinkError> { - let num_imports = module.num_imported_globals; - let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports); - - for &global_type in module.globals.values().skip(num_imports) { - vmctx_globals.push( - self.create_global(global_type) - .map_err(LinkError::Resource)?, - ); - } - - Ok(vmctx_globals) - } -} diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index e673d39dea..b2e0d3a68e 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -17,14 +17,12 @@ name = "wasmer_types" serde = { version = "1.0", features = ["derive", "rc"], optional = true, default-features = false } thiserror = "1.0" indexmap = { version = "1.6", features = ["serde-1"] } -rkyv = { version = "0.7.20", optional = true } -loupe = { version = "0.1", features = ["enable-indexmap"] } +rkyv = { version = "0.7.20" } [features] -default = ["std", "enable-serde", "enable-rkyv"] +default = ["std", "enable-serde" ] std = [] core = [] -enable-rkyv = ["rkyv"] enable-serde = ["serde", "serde/std"] # experimental / in-development features diff --git a/lib/types/src/archives.rs b/lib/types/src/archives.rs index f620520b5e..c0558a31fb 100644 --- a/lib/types/src/archives.rs +++ b/lib/types/src/archives.rs @@ -11,6 +11,12 @@ pub struct ArchivableIndexMap { entries: Vec<(K, V)>, } +impl ArchivedArchivableIndexMap { + pub fn iter(&self) -> core::slice::Iter<'_, (K::Archived, V::Archived)> { + self.entries.iter() + } +} + impl From> for ArchivableIndexMap { diff --git a/lib/types/src/entity/boxed_slice.rs b/lib/types/src/entity/boxed_slice.rs index 19fdd4b45e..4f9aa95eaf 100644 --- a/lib/types/src/entity/boxed_slice.rs +++ b/lib/types/src/entity/boxed_slice.rs @@ -10,8 +10,6 @@ use crate::lib::std::boxed::Box; use crate::lib::std::marker::PhantomData; use crate::lib::std::ops::{Index, IndexMut}; use crate::lib::std::slice; -use loupe::{MemoryUsage, MemoryUsageTracker}; -use std::mem; /// A slice mapping `K -> V` allocating dense entity references. /// @@ -146,21 +144,6 @@ where } } -impl MemoryUsage for BoxedSlice -where - K: EntityRef, - V: MemoryUsage, -{ - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - + self - .elems - .iter() - .map(|value| value.size_of_val(tracker) - mem::size_of_val(value)) - .sum::() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/lib/types/src/entity/primary_map.rs b/lib/types/src/entity/primary_map.rs index 330db9e3f7..6578762a1a 100644 --- a/lib/types/src/entity/primary_map.rs +++ b/lib/types/src/entity/primary_map.rs @@ -12,12 +12,9 @@ use crate::lib::std::marker::PhantomData; use crate::lib::std::ops::{Index, IndexMut}; use crate::lib::std::slice; use crate::lib::std::vec::Vec; -use loupe::{MemoryUsage, MemoryUsageTracker}; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -use std::mem; /// A primary mapping `K -> V` allocating dense entity references. /// @@ -36,10 +33,7 @@ use std::mem; /// `into_boxed_slice`. #[derive(Debug, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] pub struct PrimaryMap where K: EntityRef, @@ -244,18 +238,39 @@ where } } -impl MemoryUsage for PrimaryMap +impl ArchivedPrimaryMap where - K: EntityRef, - V: MemoryUsage, + K: Archive + EntityRef, + V: Archive, { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - + self - .elems - .iter() - .map(|value| value.size_of_val(tracker) - mem::size_of_val(value)) - .sum::() + /// Get the total number of entity references created. + pub fn len(&self) -> usize { + self.elems.len() + } + + /// Iterate over all the values in this map. + pub fn values(&self) -> slice::Iter> { + self.elems.iter() + } + + /// Iterate over all the keys and values in this map. + pub fn iter(&self) -> Iter> { + Iter::new(self.elems.iter()) + } +} + +/// Immutable indexing into an `PrimaryMap`. +/// The indexed value must be in the map. +impl Index<&K::Archived> for ArchivedPrimaryMap +where + K: EntityRef + Archive, + K::Archived: EntityRef, + V: Archive, +{ + type Output = ::Archived; + + fn index(&self, k: &K::Archived) -> &Self::Output { + &self.elems[k.index()] } } diff --git a/lib/types/src/entity/secondary_map.rs b/lib/types/src/entity/secondary_map.rs index afd84b5413..a0149a6098 100644 --- a/lib/types/src/entity/secondary_map.rs +++ b/lib/types/src/entity/secondary_map.rs @@ -11,8 +11,6 @@ use crate::lib::std::marker::PhantomData; use crate::lib::std::ops::{Index, IndexMut}; use crate::lib::std::slice; use crate::lib::std::vec::Vec; -use loupe::{MemoryUsage, MemoryUsageTracker}; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{ @@ -20,7 +18,6 @@ use serde::{ ser::{SerializeSeq, Serializer}, Deserialize, Serialize, }; -use std::mem; /// A mapping `K -> V` for densely indexed entity references. /// @@ -30,11 +27,7 @@ use std::mem; /// /// The map does not track if an entry for a key has been inserted or not. Instead it behaves as if /// all keys have a default entry from the beginning. -#[derive(Debug, Clone)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] pub struct SecondaryMap where K: EntityRef, @@ -168,6 +161,22 @@ where } } +/// Immutable indexing into an `SecondaryMap`. +/// +/// All keys are permitted. Untouched entries have the default value. +impl Index<&K::Archived> for ArchivedSecondaryMap +where + K: EntityRef + Archive, + K::Archived: EntityRef, + V: Archive + Clone, +{ + type Output = ::Archived; + + fn index(&self, k: &K::Archived) -> &Self::Output { + &self.elems.get(k.index()).unwrap_or(&self.default) + } +} + /// Mutable indexing into an `SecondaryMap`. /// /// The map grows as needed to accommodate new keys. @@ -287,21 +296,6 @@ where } } -impl MemoryUsage for SecondaryMap -where - K: EntityRef, - V: Clone + MemoryUsage, -{ - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - + self - .elems - .iter() - .map(|value| value.size_of_val(tracker) - mem::size_of_val(value)) - .sum::() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/lib/types/src/features.rs b/lib/types/src/features.rs index 25317f3205..95b966dcc9 100644 --- a/lib/types/src/features.rs +++ b/lib/types/src/features.rs @@ -1,5 +1,3 @@ -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -8,12 +6,9 @@ use serde::{Deserialize, Serialize}; /// Features usually have a corresponding [WebAssembly proposal]. /// /// [WebAssembly proposal]: https://github.com/WebAssembly/proposals -#[derive(Clone, Debug, Eq, PartialEq, MemoryUsage)] +#[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] pub struct Features { /// Threads proposal should be enabled pub threads: bool, @@ -35,8 +30,6 @@ pub struct Features { pub memory64: bool, /// Wasm exceptions proposal should be enabled pub exceptions: bool, - /// If signalless mode is enabled. - pub signal_less: bool, } impl Features { @@ -57,7 +50,6 @@ impl Features { multi_memory: false, memory64: false, exceptions: false, - signal_less: false, } } @@ -233,13 +225,6 @@ impl Features { self.memory64 = enable; self } - - /// Configures whether the binary was compiled in signalless mode. - /// Currently only supported for Signlepass compiler. - pub fn signal_less(&mut self, enable: bool) -> &mut Self { - self.signal_less = enable; - self - } } impl Default for Features { @@ -267,7 +252,6 @@ mod test_features { multi_memory: false, memory64: false, exceptions: false, - signal_less: false, } ); } @@ -347,11 +331,4 @@ mod test_features { features.memory64(true); assert!(features.memory64); } - - #[test] - fn enable_signal_less() { - let mut features = Features::new(); - features.signal_less(true); - assert!(features.signal_less); - } } diff --git a/lib/types/src/indexes.rs b/lib/types/src/indexes.rs index b7fd2008ba..ad41855517 100644 --- a/lib/types/src/indexes.rs +++ b/lib/types/src/indexes.rs @@ -1,195 +1,115 @@ //! Helper functions and structures for the translation. use crate::entity::entity_impl; use core::u32; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -#[cfg(feature = "enable-serde")] -use serde::{Deserialize, Serialize}; /// Index type of a function defined locally inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct LocalFunctionIndex(u32); entity_impl!(LocalFunctionIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedLocalFunctionIndex); /// Index type of a table defined locally inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] pub struct LocalTableIndex(u32); entity_impl!(LocalTableIndex); /// Index type of a memory defined locally inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] pub struct LocalMemoryIndex(u32); entity_impl!(LocalMemoryIndex); /// Index type of a global defined locally inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct LocalGlobalIndex(u32); entity_impl!(LocalGlobalIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedLocalGlobalIndex); /// Index type of a function (imported or local) inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct FunctionIndex(u32); entity_impl!(FunctionIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedFunctionIndex); /// Index type of a table (imported or local) inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct TableIndex(u32); entity_impl!(TableIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedTableIndex); /// Index type of a global variable (imported or local) inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct GlobalIndex(u32); entity_impl!(GlobalIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedGlobalIndex); /// Index type of a linear memory (imported or local) inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct MemoryIndex(u32); entity_impl!(MemoryIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedMemoryIndex); /// Index type of a signature (imported or local) inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct SignatureIndex(u32); entity_impl!(SignatureIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedSignatureIndex); /// Index type of a passive data segment inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct DataIndex(u32); entity_impl!(DataIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedDataIndex); /// Index type of a passive element segment inside the WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct ElemIndex(u32); entity_impl!(ElemIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedElemIndex); /// Index type of a custom section inside a WebAssembly module. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct CustomSectionIndex(u32); entity_impl!(CustomSectionIndex); -#[cfg(feature = "enable-rkyv")] -entity_impl!(ArchivedCustomSectionIndex); /// An entity to export. -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(u8)] pub enum ExportIndex { /// Function export. Function(FunctionIndex), @@ -202,16 +122,11 @@ pub enum ExportIndex { } /// An entity to import. -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, MemoryUsage)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[cfg_attr( - feature = "enable-rkyv", - archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)) -)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(u8)] pub enum ImportIndex { /// Function import. Function(FunctionIndex), diff --git a/lib/types/src/initializers.rs b/lib/types/src/initializers.rs index a37a23011c..47878b9504 100644 --- a/lib/types/src/initializers.rs +++ b/lib/types/src/initializers.rs @@ -1,20 +1,14 @@ use crate::indexes::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex}; use crate::lib::std::boxed::Box; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; /// A WebAssembly table initializer. -#[derive(Clone, Debug, Hash, MemoryUsage, PartialEq, Eq)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct TableInitializer { +pub struct OwnedTableInitializer { /// The index of a table to initialize. pub table_index: TableIndex, /// Optionally, a global variable giving a base index. @@ -27,12 +21,9 @@ pub struct TableInitializer { /// A memory index and offset within that memory where a data initialization /// should be performed. -#[derive(Clone, Debug, MemoryUsage, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] pub struct DataInitializerLocation { /// The index of the memory to initialize. pub memory_index: MemoryIndex, @@ -57,18 +48,15 @@ pub struct DataInitializer<'data> { /// As `DataInitializer` but owning the data rather than /// holding a reference to it -#[derive(Debug, Clone, MemoryUsage, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive)] pub struct OwnedDataInitializer { /// The location where the initialization is to be performed. pub location: DataInitializerLocation, /// The initialization owned data. - pub data: Box<[u8]>, + pub data: Vec, } impl OwnedDataInitializer { @@ -76,7 +64,35 @@ impl OwnedDataInitializer { pub fn new(borrowed: &DataInitializer<'_>) -> Self { Self { location: borrowed.location.clone(), - data: borrowed.data.to_vec().into_boxed_slice(), + data: borrowed.data.to_vec(), + } + } +} + +impl<'a> From<&'a OwnedDataInitializer> for DataInitializer<'a> { + fn from(init: &'a OwnedDataInitializer) -> Self { + DataInitializer { + location: init.location.clone(), + data: &*init.data, + } + } +} + +impl<'a> From<&'a ArchivedOwnedDataInitializer> for DataInitializer<'a> { + fn from(init: &'a ArchivedOwnedDataInitializer) -> Self { + DataInitializer { + location: rkyv::Deserialize::deserialize(&init.location, &mut rkyv::Infallible) + .expect("deserialization cannot fail"), + data: &*init.data, + } + } +} + +impl<'a> From> for OwnedDataInitializer { + fn from(init: DataInitializer<'a>) -> Self { + OwnedDataInitializer { + location: init.location.clone(), + data: init.data.to_vec(), } } } diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 1eebf38d8a..bdccbe188b 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -54,7 +54,6 @@ pub mod lib { } } -#[cfg(feature = "enable-rkyv")] mod archives; mod extern_ref; mod features; @@ -77,21 +76,20 @@ pub use crate::indexes::{ SignatureIndex, TableIndex, }; pub use crate::initializers::{ - DataInitializer, DataInitializerLocation, OwnedDataInitializer, TableInitializer, + DataInitializer, DataInitializerLocation, OwnedDataInitializer, OwnedTableInitializer, }; pub use crate::memory_view::{Atomically, MemoryView}; -pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo}; +pub use crate::module::{ImportCounts, ModuleInfo}; pub use crate::native::{NativeWasmType, ValueType}; pub use crate::units::{ Bytes, PageCountOutOfRange, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; pub use crate::values::{Value, WasmValueType}; pub use types::{ - ExportType, ExternType, FastGasCounter, FunctionType, GlobalInit, GlobalType, ImportType, - InstanceConfig, MemoryType, Mutability, NamedFunction, TableType, Type, V128, + ExportType, ExternType, FastGasCounter, FunctionType, FunctionTypeRef, GlobalInit, GlobalType, + Import, InstanceConfig, MemoryType, Mutability, TableType, Type, V128, }; -#[cfg(feature = "enable-rkyv")] pub use archives::ArchivableIndexMap; /// Version number of this crate. diff --git a/lib/types/src/module.rs b/lib/types/src/module.rs index b9fb247c8b..7ea2e782bf 100644 --- a/lib/types/src/module.rs +++ b/lib/types/src/module.rs @@ -5,17 +5,14 @@ //! `wasmer::Module`. use crate::entity::{EntityRef, PrimaryMap}; -#[cfg(feature = "enable-rkyv")] use crate::ArchivableIndexMap; use crate::{ - CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, ExportType, ExternType, FunctionIndex, - FunctionType, GlobalIndex, GlobalInit, GlobalType, ImportIndex, ImportType, LocalFunctionIndex, - LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, SignatureIndex, - TableIndex, TableInitializer, TableType, + CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, FunctionIndex, FunctionType, + GlobalIndex, GlobalInit, GlobalType, ImportIndex, LocalFunctionIndex, LocalGlobalIndex, + LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, OwnedTableInitializer, + SignatureIndex, TableIndex, TableType, }; use indexmap::IndexMap; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{ de::SharedDeserializeRegistry, ser::ScratchSpace, ser::Serializer, ser::SharedSerializeRegistry, Archive, Archived, Deserialize as RkyvDeserialize, Fallible, @@ -23,19 +20,13 @@ use rkyv::{ }; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "enable-rkyv")] use std::collections::BTreeMap; use std::collections::HashMap; use std::fmt; -use std::iter::ExactSizeIterator; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; -#[derive(Debug, Clone, MemoryUsage)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] pub struct ModuleId { id: usize, } @@ -55,9 +46,86 @@ impl Default for ModuleId { } } +/// The counts of imported entities in a WebAssembly module. +#[derive( + Debug, Copy, Clone, Default, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))] +#[archive(as = "Self")] +pub struct ImportCounts { + /// Number of imported functions in the module. + pub functions: u32, + + /// Number of imported tables in the module. + pub tables: u32, + + /// Number of imported memories in the module. + pub memories: u32, + + /// Number of imported globals in the module. + pub globals: u32, +} + +impl ImportCounts { + fn make_local(idx: I, imports: u32) -> Result { + EntityRef::index(idx) + .checked_sub(imports as _) + .map(R::new) + .ok_or(idx) + } + + /// Convert the `FunctionIndex` to a `LocalFunctionIndex`. + pub fn local_function_index( + &self, + idx: FunctionIndex, + ) -> Result { + Self::make_local(idx, self.functions) + } + + /// Convert the `TableIndex` to a `LocalTableIndex`. + pub fn local_table_index(&self, idx: TableIndex) -> Result { + Self::make_local(idx, self.tables) + } + + /// Convert the `MemoryIndex` to a `LocalMemoryIndex`. + pub fn local_memory_index(&self, idx: MemoryIndex) -> Result { + Self::make_local(idx, self.memories) + } + + /// Convert the `GlobalIndex` to a `LocalGlobalIndex`. + pub fn local_global_index(&self, idx: GlobalIndex) -> Result { + Self::make_local(idx, self.globals) + } + + fn make_index(idx: I, imports: u32) -> R { + let imports = imports as usize; + R::new(idx.index() + imports) + } + + /// Convert the `LocalFunctionIndex` to a `FunctionIndex`. + pub fn function_index(&self, idx: LocalFunctionIndex) -> FunctionIndex { + Self::make_index(idx, self.functions) + } + + /// Convert the `LocalTableIndex` to a `TableIndex`. + pub fn table_index(&self, idx: LocalTableIndex) -> TableIndex { + Self::make_index(idx, self.tables) + } + + /// Convert the `LocalMemoryIndex` to a `MemoryIndex`. + pub fn memory_index(&self, idx: LocalMemoryIndex) -> MemoryIndex { + Self::make_index(idx, self.memories) + } + + /// Convert the `LocalGlobalIndex` to a `GlobalIndex`. + pub fn global_index(&self, idx: LocalGlobalIndex) -> GlobalIndex { + Self::make_index(idx, self.globals) + } +} + /// A translated WebAssembly module, excluding the function bodies and /// memory initializers. -#[derive(Debug, Clone, Default, MemoryUsage)] +#[derive(Debug, Clone, Default)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct ModuleInfo { /// A unique identifier (within this process) for this module. @@ -86,13 +154,13 @@ pub struct ModuleInfo { pub start_function: Option, /// WebAssembly table initializers. - pub table_initializers: Vec, + pub table_initializers: Vec, /// WebAssembly passive elements. - pub passive_elements: HashMap>, + pub passive_elements: BTreeMap>, /// WebAssembly passive data segments. - pub passive_data: HashMap>, + pub passive_data: BTreeMap>, /// WebAssembly global initializers. pub global_initializers: PrimaryMap, @@ -121,46 +189,32 @@ pub struct ModuleInfo { /// The data for each CustomSection in the module. pub custom_sections_data: PrimaryMap>, - /// Number of imported functions in the module. - pub num_imported_functions: usize, - - /// Number of imported tables in the module. - pub num_imported_tables: usize, - - /// Number of imported memories in the module. - pub num_imported_memories: usize, - - /// Number of imported globals in the module. - pub num_imported_globals: usize, + /// The counts of imported entities. + pub import_counts: ImportCounts, } /// Mirror version of ModuleInfo that can derive rkyv traits -#[cfg(feature = "enable-rkyv")] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] pub struct ArchivableModuleInfo { - name: Option, - imports: ArchivableIndexMap<(String, String, u32), ImportIndex>, - exports: ArchivableIndexMap, - start_function: Option, - table_initializers: Vec, - passive_elements: BTreeMap>, - passive_data: BTreeMap>, - global_initializers: PrimaryMap, - function_names: BTreeMap, - signatures: PrimaryMap, - functions: PrimaryMap, - tables: PrimaryMap, - memories: PrimaryMap, - globals: PrimaryMap, - custom_sections: ArchivableIndexMap, - custom_sections_data: PrimaryMap>, - num_imported_functions: usize, - num_imported_tables: usize, - num_imported_memories: usize, - num_imported_globals: usize, + pub name: Option, + pub imports: ArchivableIndexMap<(String, String, u32), ImportIndex>, + pub exports: ArchivableIndexMap, + pub start_function: Option, + pub table_initializers: Vec, + pub passive_elements: BTreeMap>, + pub passive_data: BTreeMap>, + pub global_initializers: PrimaryMap, + pub function_names: BTreeMap, + pub signatures: PrimaryMap, + pub functions: PrimaryMap, + pub tables: PrimaryMap, + pub memories: PrimaryMap, + pub globals: PrimaryMap, + pub custom_sections: ArchivableIndexMap, + pub custom_sections_data: PrimaryMap>, + pub import_counts: ImportCounts, } -#[cfg(feature = "enable-rkyv")] impl From for ArchivableModuleInfo { fn from(it: ModuleInfo) -> ArchivableModuleInfo { ArchivableModuleInfo { @@ -180,15 +234,11 @@ impl From for ArchivableModuleInfo { globals: it.globals, custom_sections: ArchivableIndexMap::from(it.custom_sections), custom_sections_data: it.custom_sections_data, - num_imported_functions: it.num_imported_functions, - num_imported_tables: it.num_imported_tables, - num_imported_memories: it.num_imported_memories, - num_imported_globals: it.num_imported_globals, + import_counts: it.import_counts, } } } -#[cfg(feature = "enable-rkyv")] impl From for ModuleInfo { fn from(it: ArchivableModuleInfo) -> ModuleInfo { ModuleInfo { @@ -209,22 +259,17 @@ impl From for ModuleInfo { globals: it.globals, custom_sections: it.custom_sections.into(), custom_sections_data: it.custom_sections_data, - num_imported_functions: it.num_imported_functions, - num_imported_tables: it.num_imported_tables, - num_imported_memories: it.num_imported_memories, - num_imported_globals: it.num_imported_globals, + import_counts: it.import_counts, } } } -#[cfg(feature = "enable-rkyv")] impl From<&ModuleInfo> for ArchivableModuleInfo { fn from(it: &ModuleInfo) -> ArchivableModuleInfo { ArchivableModuleInfo::from(it.clone()) } } -#[cfg(feature = "enable-rkyv")] impl Archive for ModuleInfo { type Archived = ::Archived; type Resolver = ::Resolver; @@ -234,7 +279,6 @@ impl Archive for ModuleInfo { } } -#[cfg(feature = "enable-rkyv")] impl RkyvSerialize for ModuleInfo { @@ -243,7 +287,6 @@ impl RkyvSerial } } -#[cfg(feature = "enable-rkyv")] impl RkyvDeserialize for Archived { @@ -273,10 +316,7 @@ impl PartialEq for ModuleInfo { && self.globals == other.globals && self.custom_sections == other.custom_sections && self.custom_sections_data == other.custom_sections_data - && self.num_imported_functions == other.num_imported_functions - && self.num_imported_tables == other.num_imported_tables - && self.num_imported_memories == other.num_imported_memories - && self.num_imported_globals == other.num_imported_globals + && self.import_counts == other.import_counts } } @@ -308,63 +348,6 @@ impl ModuleInfo { .collect::>() } - /// Get the export types of the module - pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { - let iter = self.exports.iter().map(move |(name, export_index)| { - let extern_type = match export_index { - ExportIndex::Function(i) => { - let signature = self.functions.get(*i).unwrap(); - let func_type = self.signatures.get(*signature).unwrap(); - ExternType::Function(func_type.clone()) - } - ExportIndex::Table(i) => { - let table_type = self.tables.get(*i).unwrap(); - ExternType::Table(*table_type) - } - ExportIndex::Memory(i) => { - let memory_type = self.memories.get(*i).unwrap(); - ExternType::Memory(*memory_type) - } - ExportIndex::Global(i) => { - let global_type = self.globals.get(*i).unwrap(); - ExternType::Global(*global_type) - } - }; - ExportType::new(name, extern_type) - }); - ExportsIterator::new(iter, self.exports.len()) - } - - /// Get the import types of the module - pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { - let iter = self - .imports - .iter() - .map(move |((module, field, _), import_index)| { - let extern_type = match import_index { - ImportIndex::Function(i) => { - let signature = self.functions.get(*i).unwrap(); - let func_type = self.signatures.get(*signature).unwrap(); - ExternType::Function(func_type.clone()) - } - ImportIndex::Table(i) => { - let table_type = self.tables.get(*i).unwrap(); - ExternType::Table(*table_type) - } - ImportIndex::Memory(i) => { - let memory_type = self.memories.get(*i).unwrap(); - ExternType::Memory(*memory_type) - } - ImportIndex::Global(i) => { - let global_type = self.globals.get(*i).unwrap(); - ExternType::Global(*global_type) - } - }; - ImportType::new(module, field, extern_type) - }); - ImportsIterator::new(iter, self.imports.len()) - } - /// Get the custom sections of the module given a `name`. pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { self.custom_sections @@ -379,77 +362,66 @@ impl ModuleInfo { /// Convert a `LocalFunctionIndex` into a `FunctionIndex`. pub fn func_index(&self, local_func: LocalFunctionIndex) -> FunctionIndex { - FunctionIndex::new(self.num_imported_functions + local_func.index()) + self.import_counts.function_index(local_func) } /// Convert a `FunctionIndex` into a `LocalFunctionIndex`. Returns None if the /// index is an imported function. pub fn local_func_index(&self, func: FunctionIndex) -> Option { - func.index() - .checked_sub(self.num_imported_functions) - .map(LocalFunctionIndex::new) + self.import_counts.local_function_index(func).ok() } /// Test whether the given function index is for an imported function. pub fn is_imported_function(&self, index: FunctionIndex) -> bool { - index.index() < self.num_imported_functions + self.local_func_index(index).is_none() } /// Convert a `LocalTableIndex` into a `TableIndex`. pub fn table_index(&self, local_table: LocalTableIndex) -> TableIndex { - TableIndex::new(self.num_imported_tables + local_table.index()) + self.import_counts.table_index(local_table) } /// Convert a `TableIndex` into a `LocalTableIndex`. Returns None if the /// index is an imported table. pub fn local_table_index(&self, table: TableIndex) -> Option { - table - .index() - .checked_sub(self.num_imported_tables) - .map(LocalTableIndex::new) + self.import_counts.local_table_index(table).ok() } /// Test whether the given table index is for an imported table. pub fn is_imported_table(&self, index: TableIndex) -> bool { - index.index() < self.num_imported_tables + self.local_table_index(index).is_none() } /// Convert a `LocalMemoryIndex` into a `MemoryIndex`. pub fn memory_index(&self, local_memory: LocalMemoryIndex) -> MemoryIndex { - MemoryIndex::new(self.num_imported_memories + local_memory.index()) + self.import_counts.memory_index(local_memory) } /// Convert a `MemoryIndex` into a `LocalMemoryIndex`. Returns None if the /// index is an imported memory. pub fn local_memory_index(&self, memory: MemoryIndex) -> Option { - memory - .index() - .checked_sub(self.num_imported_memories) - .map(LocalMemoryIndex::new) + self.import_counts.local_memory_index(memory).ok() } /// Test whether the given memory index is for an imported memory. pub fn is_imported_memory(&self, index: MemoryIndex) -> bool { - index.index() < self.num_imported_memories + self.local_memory_index(index).is_none() } /// Convert a `LocalGlobalIndex` into a `GlobalIndex`. pub fn global_index(&self, local_global: LocalGlobalIndex) -> GlobalIndex { - GlobalIndex::new(self.num_imported_globals + local_global.index()) + self.import_counts.global_index(local_global) } /// Convert a `GlobalIndex` into a `LocalGlobalIndex`. Returns None if the /// index is an imported global. pub fn local_global_index(&self, global: GlobalIndex) -> Option { - global - .index() - .checked_sub(self.num_imported_globals) - .map(LocalGlobalIndex::new) + self.import_counts.local_global_index(global).ok() } /// Test whether the given global index is for an imported global. pub fn is_imported_global(&self, index: GlobalIndex) -> bool { - index.index() < self.num_imported_globals + self.local_global_index(index).is_none() } /// Get the Module name @@ -464,7 +436,7 @@ impl ModuleInfo { pub fn imported_function_types<'a>(&'a self) -> impl Iterator + 'a { self.functions .values() - .take(self.num_imported_functions) + .take(self.import_counts.functions as usize) .map(move |sig_index| self.signatures[*sig_index].clone()) } } @@ -474,128 +446,3 @@ impl fmt::Display for ModuleInfo { write!(f, "{}", self.name()) } } - -// Code inspired from -// https://www.reddit.com/r/rust/comments/9vspv4/extending_iterators_ergonomically/ - -/// This iterator allows us to iterate over the exports -/// and offer nice API ergonomics over it. -pub struct ExportsIterator + Sized> { - iter: I, - size: usize, -} - -impl + Sized> ExportsIterator { - /// Create a new `ExportsIterator` for a given iterator and size - pub fn new(iter: I, size: usize) -> Self { - Self { iter, size } - } -} - -impl + Sized> ExactSizeIterator for ExportsIterator { - // We can easily calculate the remaining number of iterations. - fn len(&self) -> usize { - self.size - } -} - -impl + Sized> ExportsIterator { - /// Get only the functions - pub fn functions(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Function(ty) => Some(ExportType::new(extern_.name(), ty.clone())), - _ => None, - }) - } - /// Get only the memories - pub fn memories(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Memory(ty) => Some(ExportType::new(extern_.name(), *ty)), - _ => None, - }) - } - /// Get only the tables - pub fn tables(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Table(ty) => Some(ExportType::new(extern_.name(), *ty)), - _ => None, - }) - } - /// Get only the globals - pub fn globals(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Global(ty) => Some(ExportType::new(extern_.name(), *ty)), - _ => None, - }) - } -} - -impl + Sized> Iterator for ExportsIterator { - type Item = ExportType; - fn next(&mut self) -> Option { - self.iter.next() - } -} - -/// This iterator allows us to iterate over the imports -/// and offer nice API ergonomics over it. -pub struct ImportsIterator + Sized> { - iter: I, - size: usize, -} - -impl + Sized> ImportsIterator { - /// Create a new `ImportsIterator` for a given iterator and size - pub fn new(iter: I, size: usize) -> Self { - Self { iter, size } - } -} - -impl + Sized> ExactSizeIterator for ImportsIterator { - // We can easily calculate the remaining number of iterations. - fn len(&self) -> usize { - self.size - } -} - -impl + Sized> ImportsIterator { - /// Get only the functions - pub fn functions(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Function(ty) => Some(ImportType::new( - extern_.module(), - extern_.name(), - ty.clone(), - )), - _ => None, - }) - } - /// Get only the memories - pub fn memories(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Memory(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)), - _ => None, - }) - } - /// Get only the tables - pub fn tables(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Table(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)), - _ => None, - }) - } - /// Get only the globals - pub fn globals(self) -> impl Iterator> + Sized { - self.iter.filter_map(|extern_| match extern_.ty() { - ExternType::Global(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)), - _ => None, - }) - } -} - -impl + Sized> Iterator for ImportsIterator { - type Item = ImportType; - fn next(&mut self) -> Option { - self.iter.next() - } -} diff --git a/lib/types/src/types.rs b/lib/types/src/types.rs index f5d1f4c844..d8cf7b5293 100644 --- a/lib/types/src/types.rs +++ b/lib/types/src/types.rs @@ -1,31 +1,25 @@ use crate::indexes::{FunctionIndex, GlobalIndex}; -use crate::lib::std::borrow::ToOwned; use crate::lib::std::fmt; use crate::lib::std::format; use crate::lib::std::string::{String, ToString}; use crate::lib::std::vec::Vec; use crate::units::Pages; use crate::values::{Value, WasmValueType}; -use loupe::{MemoryUsage, MemoryUsageTracker}; -use std::cell::UnsafeCell; -use std::rc::Rc; - -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; +use std::cell::UnsafeCell; +use std::rc::Rc; +use std::sync::Arc; // Type Representations // Value Types /// A list of all possible value types in WebAssembly. -#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, MemoryUsage)] +#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] pub enum Type { /// Signed 32 bit integer. I32, @@ -67,10 +61,8 @@ impl fmt::Display for Type { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] /// The WebAssembly V128 type pub struct V128(pub(crate) [u8; 16]); @@ -110,12 +102,6 @@ impl From<&[u8]> for V128 { } } -impl MemoryUsage for V128 { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - self.as_slice().size_of_val(tracker) - } -} - // External Types /// A list of all possible types which can be externally referenced from a @@ -136,64 +122,6 @@ pub enum ExternType { Memory(MemoryType), } -fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool { - let GlobalType { - ty: exported_ty, - mutability: exported_mutability, - } = exported; - let GlobalType { - ty: imported_ty, - mutability: imported_mutability, - } = imported; - - exported_ty == imported_ty && imported_mutability == exported_mutability -} - -fn is_table_element_type_compatible(exported_type: Type, imported_type: Type) -> bool { - match exported_type { - Type::FuncRef => true, - _ => imported_type == exported_type, - } -} - -fn is_table_compatible(exported: &TableType, imported: &TableType) -> bool { - let TableType { - ty: exported_ty, - minimum: exported_minimum, - maximum: exported_maximum, - } = exported; - let TableType { - ty: imported_ty, - minimum: imported_minimum, - maximum: imported_maximum, - } = imported; - - is_table_element_type_compatible(*exported_ty, *imported_ty) - && imported_minimum <= exported_minimum - && (imported_maximum.is_none() - || (!exported_maximum.is_none() - && imported_maximum.unwrap() >= exported_maximum.unwrap())) -} - -fn is_memory_compatible(exported: &MemoryType, imported: &MemoryType) -> bool { - let MemoryType { - minimum: exported_minimum, - maximum: exported_maximum, - shared: exported_shared, - } = exported; - let MemoryType { - minimum: imported_minimum, - maximum: imported_maximum, - shared: imported_shared, - } = imported; - - imported_minimum <= exported_minimum - && (imported_maximum.is_none() - || (!exported_maximum.is_none() - && imported_maximum.unwrap() >= exported_maximum.unwrap())) - && exported_shared == imported_shared -} - macro_rules! accessors { ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($( /// Attempt to return the underlying type of this external type, @@ -225,17 +153,6 @@ impl ExternType { (Table(TableType) table unwrap_table) (Memory(MemoryType) memory unwrap_memory) } - /// Check if two externs are compatible - pub fn is_compatible_with(&self, other: &Self) -> bool { - match (self, other) { - (Self::Function(a), Self::Function(b)) => a == b, - (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b), - (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b), - (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b), - // The rest of possibilities, are not compatible - _ => false, - } - } } // TODO: `shrink_to_fit` these or change it to `Box<[Type]>` if not using @@ -244,25 +161,22 @@ impl ExternType { /// in a Wasm module or exposed to Wasm by the host. /// /// WebAssembly functions can have 0 or more parameters and results. -#[derive(Debug, Clone, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] pub struct FunctionType { /// The parameters of the function - params: Box<[Type]>, + params: Arc<[Type]>, /// The return values of the function - results: Box<[Type]>, + results: Arc<[Type]>, } impl FunctionType { /// Creates a new Function Type with the given parameter and return types. pub fn new(params: Params, returns: Returns) -> Self where - Params: Into>, - Returns: Into>, + Params: Into>, + Returns: Into>, { Self { params: params.into(), @@ -306,7 +220,7 @@ macro_rules! implement_from_pair_to_functiontype { $( impl From<([Type; $N], [Type; $M])> for FunctionType { fn from(pair: ([Type; $N], [Type; $M])) -> Self { - Self::new(pair.0, pair.1) + Self::new(&pair.0[..], &pair.1[..]) } } )+ @@ -332,13 +246,51 @@ impl From<&FunctionType> for FunctionType { } } +/// Borrowed version of [`FunctionType`]. +pub struct FunctionTypeRef<'a> { + /// The parameters of the function + params: &'a [Type], + /// The return values of the function + results: &'a [Type], +} + +impl<'a> FunctionTypeRef<'a> { + /// Create a new temporary function type. + pub fn new(params: &'a [Type], results: &'a [Type]) -> Self { + Self { params, results } + } + + /// Parameter types. + pub fn params(&self) -> &[Type] { + self.params + } + + /// Return types. + pub fn results(&self) -> &[Type] { + self.results + } +} + +impl<'a> From<&'a FunctionType> for FunctionTypeRef<'a> { + fn from(FunctionType { params, results }: &'a FunctionType) -> Self { + Self { params, results } + } +} + +impl<'a> From<&'a ArchivedFunctionType> for FunctionTypeRef<'a> { + fn from(ArchivedFunctionType { params, results }: &'a ArchivedFunctionType) -> Self { + Self { + params: &**params, + results: &**results, + } + } +} + /// Indicator of whether a global is mutable or not -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] pub enum Mutability { /// The global is constant and its value does not change Const, @@ -349,36 +301,18 @@ pub enum Mutability { impl Mutability { /// Returns a boolean indicating if the enum is set to mutable. pub fn is_mutable(self) -> bool { - self.into() - } -} - -impl From for Mutability { - fn from(value: bool) -> Self { - if value { - Self::Var - } else { - Self::Const - } - } -} - -impl From for bool { - fn from(value: Mutability) -> Self { - match value { - Mutability::Var => true, - Mutability::Const => false, + match self { + Self::Const => false, + Self::Var => true, } } } /// WebAssembly global. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] pub struct GlobalType { /// The type of the value stored in the global. pub ty: Type, @@ -420,12 +354,10 @@ impl fmt::Display for GlobalType { } /// Globals are initialized via the `const` operators or by referring to another import. -#[derive(Debug, Clone, Copy, MemoryUsage, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] pub enum GlobalInit { /// An `i32.const`. I32Const(i32), @@ -480,12 +412,9 @@ impl GlobalInit { /// Tables are contiguous chunks of a specific element, typically a `funcref` or /// an `externref`. The most common use for tables is a function table through /// which `call_indirect` can invoke other functions. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] pub struct TableType { /// The type of data stored in elements of the table. pub ty: Type, @@ -523,12 +452,9 @@ impl fmt::Display for TableType { /// /// Memories are described in units of pages (64KB) and represent contiguous /// chunks of addressable memory. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] pub struct MemoryType { /// The minimum number of pages in the memory. pub minimum: Pages, @@ -573,33 +499,39 @@ impl fmt::Display for MemoryType { /// with the module/name that it's imported from as well as the type /// of item that's being imported. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct ImportType { - module: String, - name: String, +pub struct Import { + module: S, + name: S, + index: u32, ty: T, } -impl ImportType { +impl, T> Import { /// Creates a new import descriptor which comes from `module` and `name` and /// is of type `ty`. - pub fn new(module: &str, name: &str, ty: T) -> Self { + pub fn new(module: S, name: S, index: u32, ty: T) -> Self { Self { - module: module.to_owned(), - name: name.to_owned(), + module, + name, + index, ty, } } /// Returns the module name that this import is expected to come from. pub fn module(&self) -> &str { - &self.module + self.module.as_ref() } /// Returns the field name of the module that this import is expected to /// come from. pub fn name(&self) -> &str { - &self.name + self.name.as_ref() + } + + /// The index of the import in the module. + pub fn index(&self) -> u32 { + self.index } /// Returns the expected type of this import. @@ -693,11 +625,10 @@ impl fmt::Display for FastGasCounter { } /// External configuration of execution environment for Instance. -#[derive(Clone, MemoryUsage)] +#[derive(Clone)] pub struct InstanceConfig { /// External gas counter pointer. pub gas_counter: *mut FastGasCounter, - #[loupe(skip)] default_gas_counter: Option>>, /// Stack limit, in 8-byte slots. pub stack_limit: i32, @@ -737,17 +668,6 @@ impl InstanceConfig { } } -/// Named function information. -#[derive(Clone, Debug)] -pub struct NamedFunction { - /// Name of the function. - pub name: String, - /// Address of executable code. - pub address: u64, - /// Size of executable code. - pub size: usize, -} - #[cfg(test)] mod tests { use super::*; diff --git a/lib/types/src/units.rs b/lib/types/src/units.rs index 6ab314ddc4..c42237621d 100644 --- a/lib/types/src/units.rs +++ b/lib/types/src/units.rs @@ -1,9 +1,6 @@ use crate::lib::std::convert::TryFrom; use crate::lib::std::fmt; use crate::lib::std::ops::{Add, Sub}; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; use std::convert::TryInto; @@ -22,12 +19,11 @@ pub const WASM_MAX_PAGES: u32 = 0x10000; pub const WASM_MIN_PAGES: u32 = 0x100; /// Units of WebAssembly pages (as specified to be 65,536 bytes). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, MemoryUsage)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +#[repr(transparent)] pub struct Pages(pub u32); impl Pages { diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index 39d5e25e97..e24b0f8127 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -24,8 +24,7 @@ more-asserts = "0.2" cfg-if = "1.0" backtrace = "0.3" serde = { version = "1.0", features = ["derive", "rc"] } -rkyv = { version = "0.7.20", optional = true } -loupe = { version = "0.1", features = ["enable-indexmap"] } +rkyv = { version = "0.7.20" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winbase", "memoryapi", "errhandlingapi"] } @@ -38,5 +37,3 @@ maintenance = { status = "actively-developed" } [features] default = [] -enable-rkyv = ["rkyv"] -avoid-tls-signals = [] diff --git a/lib/vm/src/artifact.rs b/lib/vm/src/artifact.rs new file mode 100644 index 0000000000..af342047e9 --- /dev/null +++ b/lib/vm/src/artifact.rs @@ -0,0 +1,64 @@ +use crate::{InstanceHandle, Resolver, Tunables, VMLocalFunction, VMSharedSignatureIndex}; +use std::{any::Any, collections::BTreeMap, sync::Arc}; +use wasmer_types::{ + entity::BoxedSlice, ElemIndex, FunctionIndex, GlobalInit, GlobalType, ImportCounts, + InstanceConfig, LocalFunctionIndex, OwnedDataInitializer, OwnedTableInitializer, +}; + +/// A predecesor of a full module Instance. +/// +/// This type represents parts of a compiled WASM module ([`Executable`](crate::Executable)) that +/// are pre-allocated in within some Engine's store. +/// +/// Some other operations such as linking, relocating and similar may also be performed during +/// constructon of the Artifact, making this type particularly well suited for caching in-memory. +pub trait Artifact: Send + Sync { + /// Crate an `Instance` from this `Artifact`. + /// + /// # Safety + /// + /// See [`InstanceHandle::new`]. + unsafe fn instantiate( + self: Arc, + tunables: &dyn Tunables, + resolver: &dyn Resolver, + host_state: Box, + config: InstanceConfig, + ) -> Result>; + + /// The information about offsets into the VM context table. + fn offsets(&self) -> &crate::VMOffsets; + + /// The count of imported entities. + fn import_counts(&self) -> &ImportCounts; + + /// The locally defined functions. + /// + /// These are published and ready to call. + fn functions(&self) -> &BoxedSlice; + + /// Passive table elements. + fn passive_elements(&self) -> &BTreeMap>; + + /// Table initializers. + fn element_segments(&self) -> &[OwnedTableInitializer]; + + /// Memory initializers. + /// TODO: consider making it an iterator of `DataInitializer`s instead? + fn data_segments(&self) -> &[OwnedDataInitializer]; + + /// Passive table elements. + fn globals(&self) -> &[(GlobalType, GlobalInit)]; + + /// The function index to the start function. + fn start_function(&self) -> Option; + + /// Function by export name. + fn export_field(&self, name: &str) -> Option; + + /// Mapping between module SignatureIndex and VMSharedSignatureIndex. + fn signatures(&self) -> &[VMSharedSignatureIndex]; + + /// Obtain the function signature for either the import or local definition. + fn function_signature(&self, index: FunctionIndex) -> Option; +} diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index f956796210..eebf0b4a7b 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -6,9 +6,9 @@ use crate::instance::WeakOrStrongInstanceRef; use crate::memory::{Memory, MemoryStyle}; use crate::table::{Table, TableStyle}; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline}; -use loupe::MemoryUsage; +use crate::VMSharedSignatureIndex; use std::sync::Arc; -use wasmer_types::{FunctionType, MemoryType, TableType}; +use wasmer_types::{MemoryType, TableType}; /// The value of an export passed from one instance to another. #[derive(Debug)] @@ -27,7 +27,7 @@ pub enum VMExtern { } /// A function export value. -#[derive(Clone, Debug, PartialEq, MemoryUsage)] +#[derive(Clone, Debug, PartialEq)] pub struct VMFunction { /// The address of the native-code function. pub address: *const VMFunctionBody, @@ -36,7 +36,7 @@ pub struct VMFunction { pub vmctx: VMFunctionEnvironment, /// The function type, used for compatibility checking. - pub signature: FunctionType, + pub signature: VMSharedSignatureIndex, /// The function kind (specifies the calling convention for the /// function). @@ -47,7 +47,6 @@ pub struct VMFunction { /// /// May be `None` when the function is a host function (`FunctionType` /// == `Dynamic` or `vmctx` == `nullptr`). - #[loupe(skip)] pub call_trampoline: Option, /// A “reference” to the instance through the @@ -81,7 +80,7 @@ impl From for VMExtern { } /// A table export value. -#[derive(Clone, Debug, MemoryUsage)] +#[derive(Clone, Debug)] pub struct VMTable { /// Pointer to the containing `Table`. pub from: Arc, @@ -136,7 +135,7 @@ impl From for VMExtern { } /// A memory export value. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub struct VMMemory { /// Pointer to the containing `Memory`. pub from: Arc, @@ -191,7 +190,7 @@ impl From for VMExtern { } /// A global export value. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] pub struct VMGlobal { /// The global declaration, used for compatibility checking. pub from: Arc, diff --git a/lib/vm/src/func_data_registry.rs b/lib/vm/src/func_data_registry.rs index 7fc9fc24d0..3b0059c9e8 100644 --- a/lib/vm/src/func_data_registry.rs +++ b/lib/vm/src/func_data_registry.rs @@ -5,12 +5,11 @@ //! long as we need them to. use crate::vmcontext::VMCallerCheckedAnyfunc; -use loupe::MemoryUsage; use std::collections::HashMap; use std::sync::Mutex; /// The registry that holds the values that `VMFuncRef`s point to. -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] pub struct FuncDataRegistry { // This structure is stored in an `Engine` and is intended to be shared // across many instances. Ideally instances can themselves be sent across @@ -26,7 +25,7 @@ unsafe impl Sync for FuncDataRegistry {} /// A function reference. A single word that points to metadata about a function. #[repr(transparent)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, MemoryUsage)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct VMFuncRef(pub(crate) *const VMCallerCheckedAnyfunc); impl wasmer_types::NativeWasmType for VMFuncRef { @@ -87,9 +86,9 @@ impl std::ops::DerefMut for VMFuncRef { unsafe impl Send for VMFuncRef {} unsafe impl Sync for VMFuncRef {} -#[derive(Debug, Default, MemoryUsage)] +#[derive(Debug, Default)] struct Inner { - func_data: Vec>, + func_data: Vec, anyfunc_to_index: HashMap, } @@ -104,18 +103,14 @@ impl FuncDataRegistry { /// Register a signature and return its unique index. pub fn register(&self, anyfunc: VMCallerCheckedAnyfunc) -> VMFuncRef { let mut inner = self.inner.lock().unwrap(); - if let Some(&idx) = inner.anyfunc_to_index.get(&anyfunc) { - let data: &Box<_> = &inner.func_data[idx]; - let inner_ptr: &VMCallerCheckedAnyfunc = &*data; - VMFuncRef(inner_ptr) + let data = if let Some(&idx) = inner.anyfunc_to_index.get(&anyfunc) { + &inner.func_data[idx] } else { let idx = inner.func_data.len(); - inner.func_data.push(Box::new(anyfunc.clone())); + inner.func_data.push(anyfunc); inner.anyfunc_to_index.insert(anyfunc, idx); - - let data: &Box<_> = &inner.func_data[idx]; - let inner_ptr: &VMCallerCheckedAnyfunc = &*data; - VMFuncRef(inner_ptr) - } + &inner.func_data[idx] + }; + VMFuncRef(data) } } diff --git a/lib/vm/src/global.rs b/lib/vm/src/global.rs index 9917c06be9..a3d53a096a 100644 --- a/lib/vm/src/global.rs +++ b/lib/vm/src/global.rs @@ -1,16 +1,15 @@ use crate::vmcontext::VMGlobalDefinition; -use loupe::MemoryUsage; use std::cell::UnsafeCell; use std::ptr::NonNull; use std::sync::Mutex; use thiserror::Error; use wasmer_types::{GlobalType, Mutability, Type, Value, WasmValueType}; -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] /// A Global instance pub struct Global { ty: GlobalType, - // TODO: this box may be unnecessary + // TODO: this box is unnecessary vm_global_definition: Box>, // used to synchronize gets/sets lock: Mutex<()>, diff --git a/lib/vm/src/imports.rs b/lib/vm/src/imports.rs index 22bead4f73..c11d59aed2 100644 --- a/lib/vm/src/imports.rs +++ b/lib/vm/src/imports.rs @@ -3,9 +3,36 @@ use crate::instance::ImportFunctionEnv; use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; +use crate::VMSharedSignatureIndex; use wasmer_types::entity::{BoxedSlice, PrimaryMap}; use wasmer_types::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex}; +/// Type of the import. +pub enum VMImportType { + /// A function signature. + Function(VMSharedSignatureIndex), + /// A global. + Global(wasmer_types::GlobalType), + /// A table. + Table(wasmer_types::TableType), + /// Some memory. + Memory(wasmer_types::MemoryType, crate::MemoryStyle), +} + +/// A module import. +pub struct VMImport { + /// This is passed to the `resolve` method. + /// + /// This index is shared between different import types. + pub import_no: u32, + /// The module name. + pub module: String, + /// The field name. + pub field: String, + /// Type of the import. + pub ty: VMImportType, +} + /// Resolved import pointers. #[derive(Clone)] pub struct Imports { diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index f49b42a14b..83e3445c19 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; use std::mem; use std::ptr::{self, NonNull}; use wasmer_types::entity::EntityRef; -use wasmer_types::{LocalMemoryIndex, LocalTableIndex, ModuleInfo}; +use wasmer_types::{LocalMemoryIndex, LocalTableIndex}; /// This is an intermediate type that manages the raw allocation and /// metadata when creating an [`Instance`]. @@ -68,13 +68,12 @@ impl InstanceAllocator { /// /// [`InstanceHandle::new`]: super::InstanceHandle::new pub fn new( - module: &ModuleInfo, + offsets: VMOffsets, ) -> ( Self, Vec>, Vec>, ) { - let offsets = VMOffsets::new(mem::size_of::() as u8, module); let instance_layout = Self::instance_layout(&offsets); #[allow(clippy::cast_ptr_alignment)] @@ -206,9 +205,4 @@ impl InstanceAllocator { // because we write `Instance` to the pointer in this function. unsafe { InstanceRef::new(instance, instance_layout) } } - - /// Get the [`VMOffsets`] for the allocated buffer. - pub(crate) fn offsets(&self) -> &VMOffsets { - &self.offsets - } } diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index 0cb6af8c89..f2d35d2cea 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -13,29 +13,27 @@ mod r#ref; pub use allocator::InstanceAllocator; pub use r#ref::{InstanceRef, WeakInstanceRef, WeakOrStrongInstanceRef}; -use crate::export::VMExtern; use crate::func_data_registry::VMFuncRef; use crate::global::Global; use crate::imports::Imports; use crate::memory::{Memory, MemoryError}; +use crate::sig_registry::VMSharedSignatureIndex; use crate::table::{Table, TableElement}; use crate::trap::traphandlers::get_trap_handler; -use crate::trap::{catch_traps, Trap, TrapCode, TrapHandler}; +use crate::trap::{catch_traps, Trap, TrapCode}; use crate::vmcontext::{ VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, - VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, - VMTrampoline, + VMLocalFunction, VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport, }; -use crate::{FunctionBodyPtr, VMOffsets}; -use crate::{VMFunction, VMGlobal, VMMemory, VMTable}; -use loupe::{MemoryUsage, MemoryUsageTracker}; +use crate::{Artifact, VMOffsets}; +use crate::{VMExtern, VMFunction, VMGlobal}; use memoffset::offset_of; use more_asserts::assert_lt; use std::any::Any; use std::cell::RefCell; -use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; +use std::collections::BTreeMap; +use std::convert::TryFrom; use std::ffi; use std::fmt; use std::mem; @@ -45,9 +43,8 @@ use std::sync::Arc; use wasmer_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ DataIndex, DataInitializer, ElemIndex, ExportIndex, FastGasCounter, FunctionIndex, GlobalIndex, - GlobalInit, InstanceConfig, LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, - LocalTableIndex, MemoryIndex, ModuleInfo, NamedFunction, Pages, SignatureIndex, TableIndex, - TableInitializer, + GlobalInit, InstanceConfig, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + OwnedTableInitializer, Pages, TableIndex, }; /// The function pointer to call with data and an [`Instance`] pointer to @@ -61,44 +58,29 @@ pub type ImportInitializerFuncPtr = /// contain various data. That's why the type has a C representation /// to ensure that the `vmctx` field is last. See the documentation of /// the `vmctx` field to learn more. -#[derive(MemoryUsage)] #[repr(C)] pub(crate) struct Instance { - /// The `ModuleInfo` this `Instance` was instantiated from. - module: Arc, + pub(crate) artifact: Arc, /// External configuration for instance. config: InstanceConfig, - /// Offsets in the `vmctx` region. - offsets: VMOffsets, - /// WebAssembly linear memory data. memories: BoxedSlice>, - /// WebAssembly table data. + /// Table data... tables: BoxedSlice>, /// WebAssembly global data. globals: BoxedSlice>, - /// Pointers to functions in executable memory. - functions: BoxedSlice, - - /// Size of functions in executable memory. - function_lengths: BoxedSlice, - - /// Pointers to function call trampolines in executable memory. - #[loupe(skip)] - function_call_trampolines: BoxedSlice, - /// Passive elements in this instantiation. As `elem.drop`s happen, these /// entries get removed. - passive_elements: RefCell>>, + passive_elements: RefCell>>, /// Passive data segments from our module. As `data.drop`s happen, entries /// get removed. A missing entry is considered equivalent to an empty slice. - passive_data: RefCell>>, + passive_data: RefCell>>, /// Mapping of function indices to their func ref backing data. `VMFuncRef`s /// will point to elements here for functions defined or imported by this @@ -119,7 +101,6 @@ pub(crate) struct Instance { /// field is last, and represents a dynamically-sized array that /// extends beyond the nominal end of the struct (similar to a /// flexible array member). - #[loupe(skip)] vmctx: VMContext, } @@ -155,16 +136,6 @@ pub enum ImportFunctionEnv { }, } -impl ImportFunctionEnv { - /// Get the `initializer` function pointer if it exists. - fn initializer(&self) -> Option { - match self { - Self::Env { initializer, .. } => *initializer, - _ => None, - } - } -} - impl Clone for ImportFunctionEnv { fn clone(&self) -> Self { match &self { @@ -205,12 +176,6 @@ impl Drop for ImportFunctionEnv { } } -impl MemoryUsage for ImportFunctionEnv { - fn size_of_val(&self, _: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - } -} - impl fmt::Debug for Instance { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.debug_struct("Instance").finish() @@ -227,22 +192,14 @@ impl Instance { .cast() } - fn module(&self) -> &Arc { - &self.module - } - - pub(crate) fn module_ref(&self) -> &ModuleInfo { - &*self.module - } - /// Offsets in the `vmctx` region. fn offsets(&self) -> &VMOffsets { - &self.offsets + self.artifact.offsets() } /// Return a pointer to the `VMSharedSignatureIndex`s. fn signature_ids_ptr(&self) -> *mut VMSharedSignatureIndex { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_signature_ids_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_signature_ids_begin()) } } /// Return the indexed `VMFunctionImport`. @@ -251,17 +208,9 @@ impl Instance { unsafe { &*self.imported_functions_ptr().add(index) } } - /// Get the import initializer func at the given index if it exists. - fn imported_function_env_initializer( - &self, - index: FunctionIndex, - ) -> Option { - self.imported_function_envs[index].initializer() - } - /// Return a pointer to the `VMFunctionImport`s. fn imported_functions_ptr(&self) -> *mut VMFunctionImport { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_functions_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_functions_begin()) } } /// Return the index `VMTableImport`. @@ -272,7 +221,7 @@ impl Instance { /// Return a pointer to the `VMTableImports`s. fn imported_tables_ptr(&self) -> *mut VMTableImport { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_tables_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_tables_begin()) } } /// Return the indexed `VMMemoryImport`. @@ -283,7 +232,7 @@ impl Instance { /// Return a pointer to the `VMMemoryImport`s. fn imported_memories_ptr(&self) -> *mut VMMemoryImport { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_memories_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_memories_begin()) } } /// Return the indexed `VMGlobalImport`. @@ -294,17 +243,17 @@ impl Instance { /// Return a pointer to the `VMGlobalImport`s. fn imported_globals_ptr(&self) -> *mut VMGlobalImport { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_globals_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_globals_begin()) } } /// Return the indexed `VMTableDefinition`. - #[allow(dead_code)] + #[allow(unused)] fn table(&self, index: LocalTableIndex) -> VMTableDefinition { unsafe { *self.table_ptr(index).as_ref() } } - #[allow(dead_code)] /// Updates the value for a defined table to `VMTableDefinition`. + #[allow(unused)] fn set_table(&self, index: LocalTableIndex, table: &VMTableDefinition) { unsafe { *self.table_ptr(index).as_ptr() = *table; @@ -319,22 +268,15 @@ impl Instance { /// Return a pointer to the `VMTableDefinition`s. fn tables_ptr(&self) -> *mut VMTableDefinition { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_tables_begin()) } - } - - /// Get a locally defined or imported memory. - fn get_memory(&self, index: MemoryIndex) -> VMMemoryDefinition { - if let Some(local_index) = self.module.local_memory_index(index) { - self.memory(local_index) - } else { - let import = self.imported_memory(index); - unsafe { *import.definition.as_ref() } - } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_tables_begin()) } } /// Return the indexed `VMMemoryDefinition`. - fn memory(&self, index: LocalMemoryIndex) -> VMMemoryDefinition { - unsafe { *self.memory_ptr(index).as_ref() } + fn memory_definition(&self, index: MemoryIndex) -> &VMMemoryDefinition { + match self.artifact.import_counts().local_memory_index(index) { + Ok(local) => unsafe { self.memory_ptr(local).as_ref() }, + Err(import) => unsafe { &self.imported_memory(import).from.vmmemory().as_ref() }, + } } #[allow(dead_code)] @@ -353,12 +295,15 @@ impl Instance { /// Return a pointer to the `VMMemoryDefinition`s. fn memories_ptr(&self) -> *mut VMMemoryDefinition { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_memories_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_memories_begin()) } } /// Return the indexed `VMGlobalDefinition`. - fn global(&self, index: LocalGlobalIndex) -> VMGlobalDefinition { - unsafe { self.global_ptr(index).as_ref().clone() } + fn global(&self, index: GlobalIndex) -> &VMGlobalDefinition { + match self.artifact.import_counts().local_global_index(index) { + Ok(local) => unsafe { self.global_ptr(local).as_ref() }, + Err(import) => unsafe { self.imported_global(import).definition.as_ref() }, + } } /// Set the indexed global to `VMGlobalDefinition`. @@ -378,12 +323,12 @@ impl Instance { /// Return a pointer to the `VMGlobalDefinition`s. fn globals_ptr(&self) -> *mut *mut VMGlobalDefinition { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_globals_begin()) } } /// Return a pointer to the `VMBuiltinFunctionsArray`. fn builtin_functions_ptr(&self) -> *mut VMBuiltinFunctionsArray { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_builtin_functions_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_builtin_functions_begin()) } } /// Return a reference to the vmctx used by compiled wasm code. @@ -404,95 +349,38 @@ impl Instance { /// Return a pointer to the trap catcher. fn trap_catcher_ptr(&self) -> *mut *const u8 { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_trap_handler()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_trap_handler()) } } /// Return a pointer to the gas limiter. pub fn gas_counter_ptr(&self) -> *mut *const FastGasCounter { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_gas_limiter_pointer()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_gas_limiter_pointer()) } } /// Return a pointer to initial stack limit. pub fn stack_limit_initial_ptr(&self) -> *mut i32 { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_stack_limit_initial_begin()) } + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_stack_limit_initial_begin()) } } /// Return a pointer to current stack limit. pub fn stack_limit_ptr(&self) -> *mut i32 { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_stack_limit_begin()) } - } - - pub fn named_functions(&self) -> Vec { - let mut result = vec![]; - for (index, name) in &self.module.function_names { - match self.module.local_func_index(*index) { - Some(local_index) => { - let address = self.functions[local_index].0 as u64; - let size = self.function_lengths[local_index]; - result.push(NamedFunction { - name: name.clone(), - address, - size, - }) - } - None => {} - } - } - for (name, index) in &self.module.exports { - match index { - ExportIndex::Function(index) => match self.module.local_func_index(*index) { - Some(local_index) => { - let address = self.functions[local_index].0 as u64; - let size = self.function_lengths[local_index]; - result.push(NamedFunction { - name: name.clone(), - address, - size, - }) - } - None => {} - }, - _ => {} - } - } - result + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_stack_limit_begin()) } } /// Invoke the WebAssembly start function of the instance, if one is present. - fn invoke_start_function(&self, trap_handler: &dyn TrapHandler) -> Result<(), Trap> { - let start_index = match self.module.start_function { + fn invoke_start_function(&self) -> Result<(), Trap> { + let start_index = match self.artifact.start_function() { Some(idx) => idx, None => return Ok(()), }; - - let (callee_address, callee_vmctx) = match self.module.local_func_index(start_index) { - Some(local_index) => { - let body = self - .functions - .get(local_index) - .expect("function index is out of bounds") - .0; - ( - body as *const _, - VMFunctionEnvironment { - vmctx: self.vmctx_ptr(), - }, - ) - } - None => { - assert_lt!(start_index.index(), self.module.num_imported_functions); - let import = self.imported_function(start_index); - (import.body, import.environment) - } - }; - + let start_funcref = self.funcrefs[start_index]; // Make the call. self.on_call(); unsafe { - catch_traps(trap_handler, || { + catch_traps(|| { mem::transmute::<*const VMFunctionBody, unsafe extern "C" fn(VMFunctionEnvironment)>( - callee_address, - )(callee_vmctx) + start_funcref.func_ptr, + )(start_funcref.vmctx) }) } } @@ -569,8 +457,7 @@ impl Instance { IntoPages: Into, { let import = self.imported_memory(memory_index); - let from = import.from.as_ref(); - from.grow(delta.into()) + import.from.grow(delta.into()) } /// Returns the number of allocated wasm pages. @@ -587,17 +474,12 @@ impl Instance { /// This and `imported_memory_grow` are currently unsafe because they /// dereference the memory import's pointers. pub(crate) unsafe fn imported_memory_size(&self, memory_index: MemoryIndex) -> Pages { - let import = self.imported_memory(memory_index); - let from = import.from.as_ref(); - from.size() + self.imported_memory(memory_index).from.size() } /// Returns the number of elements in a given table. pub(crate) fn table_size(&self, table_index: LocalTableIndex) -> u32 { - self.tables - .get(table_index) - .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) - .size() + self.tables[table_index].size() } /// Returns the number of elements in a given imported table. @@ -605,9 +487,7 @@ impl Instance { /// # Safety /// `table_index` must be a valid, imported table index. pub(crate) unsafe fn imported_table_size(&self, table_index: TableIndex) -> u32 { - let import = self.imported_table(table_index); - let from = import.from.as_ref(); - from.size() + self.imported_table(table_index).from.size() } /// Grow table by the specified amount of elements. @@ -640,8 +520,7 @@ impl Instance { init_value: TableElement, ) -> Option { let import = self.imported_table(table_index); - let from = import.from.as_ref(); - from.grow(delta.into(), init_value) + import.from.grow(delta, init_value) } /// Get table element by index. @@ -666,8 +545,7 @@ impl Instance { index: u32, ) -> Option { let import = self.imported_table(table_index); - let from = import.from.as_ref(); - from.get(index) + import.from.get(index) } /// Set table element by index. @@ -694,8 +572,7 @@ impl Instance { val: TableElement, ) -> Result<(), Trap> { let import = self.imported_table(table_index); - let from = import.from.as_ref(); - from.set(index, val) + import.from.set(index, val) } pub(crate) fn func_ref(&self, function_index: FunctionIndex) -> Option { @@ -807,8 +684,7 @@ impl Instance { len: u32, ) -> Result<(), Trap> { // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy - - let memory = self.memory(memory_index); + let memory = unsafe { self.memory_ptr(memory_index).as_ref() }; // The following memory copy is not synchronized and is not atomic: unsafe { memory.memory_copy(dst, src, len) } } @@ -822,9 +698,8 @@ impl Instance { len: u32, ) -> Result<(), Trap> { let import = self.imported_memory(memory_index); - let memory = unsafe { import.definition.as_ref() }; // The following memory copy is not synchronized and is not atomic: - unsafe { memory.memory_copy(dst, src, len) } + unsafe { import.from.vmmemory().as_ref().memory_copy(dst, src, len) } } /// Perform the `memory.fill` operation on a locally defined memory. @@ -839,7 +714,7 @@ impl Instance { val: u32, len: u32, ) -> Result<(), Trap> { - let memory = self.memory(memory_index); + let memory = unsafe { self.memory_ptr(memory_index).as_ref() }; // The following memory fill is not synchronized and is not atomic: unsafe { memory.memory_fill(dst, val, len) } } @@ -857,9 +732,8 @@ impl Instance { len: u32, ) -> Result<(), Trap> { let import = self.imported_memory(memory_index); - let memory = unsafe { import.definition.as_ref() }; // The following memory fill is not synchronized and is not atomic: - unsafe { memory.memory_fill(dst, val, len) } + unsafe { import.from.vmmemory().as_ref().memory_fill(dst, val, len) } } /// Performs the `memory.init` operation. @@ -879,28 +753,26 @@ impl Instance { ) -> Result<(), Trap> { // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init - let memory = self.get_memory(memory_index); + let memory = self.memory_definition(memory_index); let passive_data = self.passive_data.borrow(); let data = passive_data.get(&data_index).map_or(&[][..], |d| &**d); - if src + let oob_access = src .checked_add(len) .map_or(true, |n| n as usize > data.len()) || dst.checked_add(len).map_or(true, |m| { usize::try_from(m).unwrap() > memory.current_length - }) - { + }); + + if oob_access { return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); } - let src_slice = &data[src as usize..(src + len) as usize]; - unsafe { let dst_start = memory.base.add(dst as usize); let dst_slice = slice::from_raw_parts_mut(dst_start, len as usize); dst_slice.copy_from_slice(src_slice); } - Ok(()) } @@ -913,10 +785,9 @@ impl Instance { /// Get a table by index regardless of whether it is locally-defined or an /// imported, foreign table. pub(crate) fn get_table(&self, table_index: TableIndex) -> &dyn Table { - if let Some(local_table_index) = self.module.local_table_index(table_index) { - self.get_local_table(local_table_index) - } else { - self.get_foreign_table(table_index) + match self.artifact.import_counts().local_table_index(table_index) { + Ok(local) => self.get_local_table(local), + Err(import) => self.get_foreign_table(import), } } @@ -937,7 +808,7 @@ impl Instance { /// /// This is more or less a public facade of the private `Instance`, /// providing useful higher-level API. -#[derive(Debug, PartialEq, MemoryUsage)] +#[derive(Debug, PartialEq)] pub struct InstanceHandle { /// The [`InstanceRef`]. See its documentation to learn more. instance: InstanceRef, @@ -965,18 +836,16 @@ impl InstanceHandle { /// all the local tables. /// - The memory at `instance.memories_ptr()` must be initialized with data for /// all the local memories. + // FIXME: instances should just store a reference to an Artifact #[allow(clippy::too_many_arguments)] pub unsafe fn new( + artifact: Arc, allocator: InstanceAllocator, - module: Arc, - finished_functions: BoxedSlice, - finished_functions_lengths: BoxedSlice, - finished_function_call_trampolines: BoxedSlice, finished_memories: BoxedSlice>, finished_tables: BoxedSlice>, finished_globals: BoxedSlice>, imports: Imports, - vmshared_signatures: BoxedSlice, + passive_data: BTreeMap>, host_state: Box, imported_function_envs: BoxedSlice, instance_config: InstanceConfig, @@ -986,23 +855,18 @@ impl InstanceHandle { .map(|m| m.vmglobal()) .collect::>() .into_boxed_slice(); - let passive_data = RefCell::new(module.passive_data.clone()); + let passive_data = RefCell::new(passive_data); let handle = { - let offsets = allocator.offsets().clone(); // use dummy value to create an instance so we can get the vmctx pointer let funcrefs = PrimaryMap::new().into_boxed_slice(); // Create the `Instance`. The unique, the One. let instance = Instance { - module, + artifact, config: instance_config.clone(), - offsets, memories: finished_memories, tables: finished_tables, globals: finished_globals, - functions: finished_functions, - function_lengths: finished_functions_lengths, - function_call_trampolines: finished_function_call_trampolines, passive_elements: Default::default(), passive_data, host_state, @@ -1018,10 +882,8 @@ impl InstanceHandle { let instance = instance_ref.as_mut().unwrap(); let vmctx_ptr = instance.vmctx_ptr(); instance.funcrefs = build_funcrefs( - &*instance.module, &imports, - &instance.functions, - &vmshared_signatures, + instance.artifact.functions().iter().map(|(_, f)| f), vmctx_ptr, ); *(instance.trap_catcher_ptr()) = get_trap_handler(); @@ -1037,10 +899,11 @@ impl InstanceHandle { let instance = handle.instance().as_ref(); ptr::copy( - vmshared_signatures.values().as_slice().as_ptr(), + instance.artifact.signatures().as_ptr(), instance.signature_ids_ptr() as *mut VMSharedSignatureIndex, - vmshared_signatures.len(), + instance.artifact.signatures().len(), ); + ptr::copy( imports.functions.values().as_slice().as_ptr(), instance.imported_functions_ptr() as *mut VMFunctionImport, @@ -1092,20 +955,19 @@ impl InstanceHandle { /// # Safety /// /// Only safe to call immediately after instantiation. - pub unsafe fn finish_instantiation( - &self, - trap_handler: &dyn TrapHandler, - data_initializers: &[DataInitializer<'_>], - ) -> Result<(), Trap> { + pub unsafe fn finish_instantiation(&self) -> Result<(), Trap> { let instance = self.instance().as_ref(); // Apply the initializers. initialize_tables(instance)?; - initialize_memories(instance, data_initializers)?; + initialize_memories( + instance, + instance.artifact.data_segments().iter().map(Into::into), + )?; // The WebAssembly spec specifies that the start function is // invoked automatically at instantiation time. - instance.invoke_start_function(trap_handler)?; + instance.invoke_start_function()?; Ok(()) } @@ -1126,114 +988,95 @@ impl InstanceHandle { self.instance().as_ref().offsets() } - /// Return a reference-counting pointer to a module. - pub fn module(&self) -> &Arc { - self.instance().as_ref().module() + /// Lookup an exported function with the specified function index. + pub fn function_by_index(&self, idx: FunctionIndex) -> Option { + let instance = self.instance.as_ref(); + + let (address, signature, vmctx, trampoline) = + match instance.artifact.import_counts().local_function_index(idx) { + Ok(local) => { + let func = instance.artifact.functions().get(local)?; + ( + *(func.body), + func.signature, + VMFunctionEnvironment { + vmctx: instance.vmctx_ptr(), + }, + Some(func.trampoline), + ) + } + Err(import) => { + let import = instance.imported_function(import); + ( + *(import.body), + import.signature, + import.environment, + import.trampoline, + ) + } + }; + Some(VMFunction { + // Any function received is already static at this point as: + // 1. All locally defined functions in the Wasm have a static signature. + // 2. All the imported functions are already static (because + // they point to the trampolines rather than the dynamic addresses). + kind: VMFunctionKind::Static, + address, + signature, + vmctx, + call_trampoline: trampoline, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) } - /// Return a reference to a module. - pub fn module_ref(&self) -> &ModuleInfo { - self.instance().as_ref().module_ref() + /// Return the indexed `VMMemoryDefinition`. + fn memory_by_index(&self, index: MemoryIndex) -> Option { + let instance = self.instance.as_ref(); + let from = match instance.artifact.import_counts().local_memory_index(index) { + Ok(local) => Arc::clone(&instance.memories[local]), + Err(import) => Arc::clone(&instance.imported_memory(import).from), + }; + Some(crate::VMMemory { + from, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) } - /// Lookup an export with the given name. - pub fn lookup(&self, field: &str) -> Option { - let export = self.module_ref().exports.get(field)?; - - Some(self.lookup_by_declaration(&export)) - } - - /// Lookup an export with the given export declaration. - // TODO: maybe EngineExport - pub fn lookup_by_declaration(&self, export: &ExportIndex) -> VMExtern { - let instance = self.instance().clone(); - let instance_ref = instance.as_ref(); - - match export { - ExportIndex::Function(index) => { - let sig_index = &instance_ref.module.functions[*index]; - let (address, vmctx, _function_ptr) = - if let Some(def_index) = instance_ref.module.local_func_index(*index) { - ( - instance_ref.functions[def_index].0 as *const _, - VMFunctionEnvironment { - vmctx: instance_ref.vmctx_ptr(), - }, - None, - ) - } else { - let import = instance_ref.imported_function(*index); - let initializer = instance_ref.imported_function_env_initializer(*index); - (import.body, import.environment, initializer) - }; - let call_trampoline = Some(instance_ref.function_call_trampolines[*sig_index]); - let signature = instance_ref.module.signatures[*sig_index].clone(); - - VMFunction { - address, - // Any function received is already static at this point as: - // 1. All locally defined functions in the Wasm have a static signature. - // 2. All the imported functions are already static (because - // they point to the trampolines rather than the dynamic addresses). - kind: VMFunctionKind::Static, - signature, - vmctx, - call_trampoline, - instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), - } - .into() - } - ExportIndex::Table(index) => { - let from = if let Some(def_index) = instance_ref.module.local_table_index(*index) { - instance_ref.tables[def_index].clone() - } else { - let import = instance_ref.imported_table(*index); - import.from.clone() - }; - VMTable { - from, - instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), - } - .into() - } - ExportIndex::Memory(index) => { - let from = if let Some(def_index) = instance_ref.module.local_memory_index(*index) { - instance_ref.memories[def_index].clone() - } else { - let import = instance_ref.imported_memory(*index); - import.from.clone() - }; - VMMemory { - from, - instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), - } - .into() - } - ExportIndex::Global(index) => { - let from = { - if let Some(def_index) = instance_ref.module.local_global_index(*index) { - instance_ref.globals[def_index].clone() - } else { - let import = instance_ref.imported_global(*index); - import.from.clone() - } - }; - VMGlobal { - from, - instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), - } - .into() - } - } + /// Return the indexed `VMMemoryDefinition`. + fn table_by_index(&self, index: TableIndex) -> Option { + let instance = self.instance.as_ref(); + let from = match instance.artifact.import_counts().local_table_index(index) { + Ok(local) => Arc::clone(&instance.tables[local]), + Err(import) => Arc::clone(&instance.imported_table(import).from), + }; + Some(crate::VMTable { + from, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) + } + + /// Obtain a reference to a global entity by its index. + pub fn global_by_index(&self, index: GlobalIndex) -> Option { + let instance = self.instance.as_ref(); + let from = match instance.artifact.import_counts().local_global_index(index) { + Ok(local) => Arc::clone(&instance.globals[local]), + Err(import) => Arc::clone(&instance.imported_global(import).from), + }; + Some(crate::VMGlobal { + from, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) } - /// Return an iterator over the exports of this instance. - /// - /// Specifically, it provides access to the key-value pairs, where the keys - /// are export names, and the values are export declarations which can be - /// resolved `lookup_by_declaration`. - pub fn exports(&self) -> indexmap::map::Iter { - self.module().exports.iter() + /// Lookup an exported function with the given name. + pub fn lookup(&self, field: &str) -> Option { + let instance = self.instance.as_ref(); + Some(match instance.artifact.export_field(field)? { + ExportIndex::Function(idx) => VMExtern::Function(self.function_by_index(idx)?), + ExportIndex::Table(idx) => VMExtern::Table(self.table_by_index(idx)?), + ExportIndex::Global(idx) => VMExtern::Global(self.global_by_index(idx)?), + ExportIndex::Memory(idx) => VMExtern::Memory(self.memory_by_index(idx)?), + }) } /// Return a reference to the custom state attached to this instance. @@ -1304,24 +1147,23 @@ impl InstanceHandle { pub fn get_local_table(&self, index: LocalTableIndex) -> &dyn Table { self.instance().as_ref().get_local_table(index) } +} - /// Returns list of named functions along with their addresses in this instance. - pub fn named_functions(&self) -> Vec { - self.instance().as_ref().named_functions() - } - - /// Initializes the host environments. - /// - /// # Safety - /// - This function must be called with the correct `Err` type parameter: the error type is not - /// visible to code in `wasmer_vm`, so it's the caller's responsibility to ensure these - /// functions are called with the correct type. - /// - `instance_ptr` must point to a valid `wasmer::Instance`. - pub unsafe fn initialize_host_envs( - &mut self, - instance_ptr: *const ffi::c_void, - ) -> Result<(), Err> { - let instance_ref = self.instance.as_mut_unchecked(); +/// Initializes the host environments. +/// +/// # Safety +/// - This function must be called with the correct `Err` type parameter: the error type is not +/// visible to code in `wasmer_vm`, so it's the caller's responsibility to ensure these +/// functions are called with the correct type. +/// - `instance_ptr` must point to a valid `wasmer::Instance`. +pub unsafe fn initialize_host_envs( + handle: &std::sync::Mutex, + instance_ptr: *const ffi::c_void, +) -> Result<(), Err> { + let initializers = { + let mut instance_lock = handle.lock().unwrap(); + let instance_ref = instance_lock.instance.as_mut_unchecked(); + let mut initializers = vec![]; for import_function_env in instance_ref.imported_function_envs.values_mut() { match import_function_env { ImportFunctionEnv::Env { @@ -1329,38 +1171,29 @@ impl InstanceHandle { ref mut initializer, .. } => { - if let Some(f) = initializer { - // transmute our function pointer into one with the correct error type - let f = mem::transmute::< - &ImportInitializerFuncPtr, - &ImportInitializerFuncPtr, - >(f); - f(*env, instance_ptr)?; + if let Some(init) = initializer.take() { + initializers.push((init, *env)); } - *initializer = None; } ImportFunctionEnv::NoEnv => (), } } - Ok(()) + initializers + }; + for (init, env) in initializers { + let f = mem::transmute::<&ImportInitializerFuncPtr, &ImportInitializerFuncPtr>(&init); + f(env, instance_ptr)?; } + Ok(()) } /// Compute the offset for a memory data initializer. fn get_memory_init_start(init: &DataInitializer<'_>, instance: &Instance) -> usize { let mut start = init.location.offset; - if let Some(base) = init.location.base { - let val = unsafe { - if let Some(def_index) = instance.module.local_global_index(base) { - instance.global(def_index).to_u32() - } else { - instance.imported_global(base).definition.as_ref().to_u32() - } - }; + let val = instance.global(base).to_u32(); start += usize::try_from(val).unwrap(); } - start } @@ -1370,40 +1203,23 @@ unsafe fn get_memory_slice<'instance>( init: &DataInitializer<'_>, instance: &'instance Instance, ) -> &'instance mut [u8] { - let memory = if let Some(local_memory_index) = instance - .module - .local_memory_index(init.location.memory_index) - { - instance.memory(local_memory_index) - } else { - let import = instance.imported_memory(init.location.memory_index); - *import.definition.as_ref() - }; - slice::from_raw_parts_mut(memory.base, memory.current_length.try_into().unwrap()) + let memory = instance.memory_definition(init.location.memory_index); + slice::from_raw_parts_mut(memory.base, memory.current_length) } /// Compute the offset for a table element initializer. -fn get_table_init_start(init: &TableInitializer, instance: &Instance) -> usize { +fn get_table_init_start(init: &OwnedTableInitializer, instance: &Instance) -> usize { let mut start = init.offset; - if let Some(base) = init.base { - let val = unsafe { - if let Some(def_index) = instance.module.local_global_index(base) { - instance.global(def_index).to_u32() - } else { - instance.imported_global(base).definition.as_ref().to_u32() - } - }; + let val = instance.global(base).to_u32(); start += usize::try_from(val).unwrap(); } - start } /// Initialize the table memory from the provided initializers. fn initialize_tables(instance: &Instance) -> Result<(), Trap> { - let module = Arc::clone(&instance.module); - for init in &module.table_initializers { + for init in instance.artifact.element_segments() { let start = get_table_init_start(init, instance); let table = instance.get_table(init.table_index); @@ -1440,8 +1256,8 @@ fn initialize_passive_elements(instance: &Instance) { passive_elements.extend( instance - .module - .passive_elements + .artifact + .passive_elements() .iter() .filter(|(_, segments)| !segments.is_empty()) .map(|(idx, segments)| { @@ -1457,23 +1273,23 @@ fn initialize_passive_elements(instance: &Instance) { } /// Initialize the table memory from the provided initializers. -fn initialize_memories( +fn initialize_memories<'a>( instance: &Instance, - data_initializers: &[DataInitializer<'_>], + data_initializers: impl Iterator>, ) -> Result<(), Trap> { for init in data_initializers { - let memory = instance.get_memory(init.location.memory_index); + let memory = instance.memory_definition(init.location.memory_index); - let start = get_memory_init_start(init, instance); + let start = get_memory_init_start(&init, instance); if start .checked_add(init.data.len()) - .map_or(true, |end| end > memory.current_length.try_into().unwrap()) + .map_or(true, |end| end > memory.current_length) { return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); } unsafe { - let mem_slice = get_memory_slice(init, instance); + let mem_slice = get_memory_slice(&init, instance); let end = start + init.data.len(); let to_init = &mut mem_slice[start..end]; to_init.copy_from_slice(init.data); @@ -1484,25 +1300,16 @@ fn initialize_memories( } fn initialize_globals(instance: &Instance) { - let module = Arc::clone(&instance.module); - for (index, initializer) in module.global_initializers.iter() { + for (index, (_, initializer)) in instance.artifact.globals().iter().enumerate() { unsafe { - let to = instance.global_ptr(index).as_ptr(); + let to = instance.global_ptr(LocalGlobalIndex::new(index)).as_ptr(); match initializer { GlobalInit::I32Const(x) => *(*to).as_i32_mut() = *x, GlobalInit::I64Const(x) => *(*to).as_i64_mut() = *x, GlobalInit::F32Const(x) => *(*to).as_f32_mut() = *x, GlobalInit::F64Const(x) => *(*to).as_f64_mut() = *x, GlobalInit::V128Const(x) => *(*to).as_bytes_mut() = *x.bytes(), - GlobalInit::GetGlobal(x) => { - let from: VMGlobalDefinition = - if let Some(def_x) = module.local_global_index(*x) { - instance.global(def_x) - } else { - instance.imported_global(*x).definition.as_ref().clone() - }; - *to = from; - } + GlobalInit::GetGlobal(x) => *to = instance.global(*x).clone(), GlobalInit::RefNullConst => *(*to).as_funcref_mut() = VMFuncRef::null(), GlobalInit::RefFunc(func_idx) => { let funcref = instance.func_ref(*func_idx).unwrap(); @@ -1515,39 +1322,30 @@ fn initialize_globals(instance: &Instance) { /// Eagerly builds all the `VMFuncRef`s for imported and local functions so that all /// future funcref operations are just looking up this data. -fn build_funcrefs( - module_info: &ModuleInfo, +pub fn build_funcrefs<'a>( imports: &Imports, - finished_functions: &BoxedSlice, - vmshared_signatures: &BoxedSlice, + finished_functions: impl ExactSizeIterator, + // vmshared_signatures: &BoxedSlice, vmctx_ptr: *mut VMContext, ) -> BoxedSlice { - let mut func_refs = PrimaryMap::with_capacity(module_info.functions.len()); - - // do imported functions - for (index, import) in imports.functions.iter() { - let sig_index = module_info.functions[index]; - let type_index = vmshared_signatures[sig_index]; + let mut func_refs = + PrimaryMap::with_capacity(imports.functions.len() + finished_functions.len()); + for (_, import) in imports.functions.iter() { let anyfunc = VMCallerCheckedAnyfunc { - func_ptr: import.body, - type_index, + func_ptr: *(import.body), + type_index: import.signature, vmctx: import.environment, }; func_refs.push(anyfunc); } - - // do local functions - for (local_index, func_ptr) in finished_functions.iter() { - let index = module_info.func_index(local_index); - let sig_index = module_info.functions[index]; - let type_index = vmshared_signatures[sig_index]; + // local functions + for function in finished_functions { let anyfunc = VMCallerCheckedAnyfunc { - func_ptr: func_ptr.0, - type_index, + func_ptr: *(function.body), + type_index: function.signature, vmctx: VMFunctionEnvironment { vmctx: vmctx_ptr }, }; func_refs.push(anyfunc); } - func_refs.into_boxed_slice() } diff --git a/lib/vm/src/instance/ref.rs b/lib/vm/src/instance/ref.rs index 47f319593a..fa8a1a2b6a 100644 --- a/lib/vm/src/instance/ref.rs +++ b/lib/vm/src/instance/ref.rs @@ -1,8 +1,6 @@ use super::Instance; -use loupe::{MemoryUsage, MemoryUsageTracker}; use std::alloc::Layout; use std::convert::TryFrom; -use std::mem; use std::ptr::{self, NonNull}; use std::sync::{Arc, Weak}; @@ -82,15 +80,6 @@ impl Drop for InstanceInner { unsafe impl Send for InstanceInner {} unsafe impl Sync for InstanceInner {} -impl MemoryUsage for InstanceInner { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) + self.instance_layout.size_of_val(tracker) - - mem::size_of_val(&self.instance_layout) - + self.as_ref().size_of_val(tracker) - - mem::size_of_val(&self.instance) - } -} - /// An `InstanceRef` is responsible to properly deallocate, /// and to give access to an `Instance`, in such a way that `Instance` /// is unique, can be shared, safely, across threads, without @@ -110,7 +99,7 @@ impl MemoryUsage for InstanceInner { /// share an [`Instance`] between an [`InstanceHandle`] and the module /// exports, so that one can drop a [`InstanceHandle`] but still being /// able to use the exports properly. -#[derive(Debug, PartialEq, Clone, MemoryUsage)] +#[derive(Debug, PartialEq, Clone)] pub struct InstanceRef(Arc); impl InstanceRef { @@ -177,22 +166,11 @@ impl WeakInstanceRef { } } -impl MemoryUsage for WeakInstanceRef { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - + if let Some(ir) = self.upgrade() { - ir.size_of_val(tracker) - } else { - 0 - } - } -} - /// An `InstanceRef` that may or may not be keeping the `Instance` alive. /// /// This type is useful for types that conditionally must keep / not keep the /// underlying `Instance` alive. For example, to prevent cycles in `WasmerEnv`s. -#[derive(Debug, Clone, PartialEq, MemoryUsage)] +#[derive(Debug, Clone, PartialEq)] pub enum WeakOrStrongInstanceRef { /// A weak instance ref. Weak(WeakInstanceRef), diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index ecf4fd71ff..38b7dcb517 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -5,7 +5,7 @@ #![warn(unused_import_braces)] #![cfg_attr( feature = "cargo-clippy", - allow(clippy::new_without_default, vtable_address_comparisons) + allow(clippy::new_without_default, clippy::vtable_address_comparisons) )] #![cfg_attr( feature = "cargo-clippy", @@ -13,7 +13,7 @@ clippy::float_arithmetic, clippy::mut_mut, clippy::nonminimal_bool, - clippy::option_map_unwrap_or, + clippy::map_unwrap_or, clippy::option_map_unwrap_or_else, clippy::print_stdout, clippy::unicode_not_nfc, @@ -21,6 +21,7 @@ ) )] +mod artifact; mod export; mod func_data_registry; mod global; @@ -29,77 +30,50 @@ mod instance; mod memory; mod mmap; mod probestack; +mod resolver; mod sig_registry; mod table; mod trap; +mod tunables; mod vmcontext; mod vmoffsets; pub mod libcalls; +pub use crate::artifact::Artifact; pub use crate::export::*; pub use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; pub use crate::global::*; -pub use crate::imports::Imports; +pub use crate::imports::{Imports, VMImport, VMImportType}; pub use crate::instance::{ - ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle, - WeakOrStrongInstanceRef, + initialize_host_envs, ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, + InstanceHandle, WeakOrStrongInstanceRef, }; pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle}; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; -pub use crate::sig_registry::SignatureRegistry; +pub use crate::resolver::{ + ChainableNamedResolver, Export, ExportFunction, ExportFunctionMetadata, NamedResolver, + NamedResolverChain, NullResolver, Resolver, +}; +pub use crate::sig_registry::{SignatureRegistry, VMSharedSignatureIndex}; pub use crate::table::{LinearTable, Table, TableElement, TableStyle}; pub use crate::trap::*; +pub use crate::tunables::Tunables; pub use crate::vmcontext::{ - VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, - VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, - VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, - VMTableImport, VMTrampoline, + FunctionBodyPtr, FunctionExtent, SectionBodyPtr, VMBuiltinFunctionIndex, + VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, + VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, + VMLocalFunction, VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport, + VMTrampoline, }; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets}; -use loupe::MemoryUsage; -pub use wasmer_types::VMExternRef; #[deprecated( since = "2.1.0", note = "ModuleInfo, ExportsIterator, ImportsIterator should be imported from wasmer_types." )] -pub use wasmer_types::{ExportsIterator, ImportsIterator, ModuleInfo}; +pub use wasmer_types::ModuleInfo; +pub use wasmer_types::VMExternRef; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); - -/// A safe wrapper around `VMFunctionBody`. -#[derive(Clone, Copy, Debug, MemoryUsage)] -#[repr(transparent)] -pub struct FunctionBodyPtr(pub *const VMFunctionBody); - -impl std::ops::Deref for FunctionBodyPtr { - type Target = *const VMFunctionBody; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// # Safety -/// The VMFunctionBody that this points to is opaque, so there's no data to -/// read or write through this pointer. This is essentially a usize. -unsafe impl Send for FunctionBodyPtr {} -/// # Safety -/// The VMFunctionBody that this points to is opaque, so there's no data to -/// read or write through this pointer. This is essentially a usize. -unsafe impl Sync for FunctionBodyPtr {} - -/// Pointers to section data. -#[derive(Clone, Copy, Debug)] -#[repr(transparent)] -pub struct SectionBodyPtr(pub *const u8); - -impl std::ops::Deref for SectionBodyPtr { - type Target = *const u8; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/lib/vm/src/libcalls.rs b/lib/vm/src/libcalls.rs index 301568e4b2..9f875d02fe 100644 --- a/lib/vm/src/libcalls.rs +++ b/lib/vm/src/libcalls.rs @@ -43,8 +43,6 @@ use crate::table::{RawTableElement, TableElement}; use crate::trap::{raise_lib_trap, Trap, TrapCode}; use crate::vmcontext::VMContext; use crate::VMExternRef; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use serde::{Deserialize, Serialize}; use std::fmt; @@ -381,22 +379,23 @@ pub unsafe extern "C" fn wasmer_vm_table_set( ) { let instance = (&*vmctx).instance(); let table_index = TableIndex::from_u32(table_index); - let table_index = instance - .module_ref() + if let Ok(local_table) = instance + .artifact + .import_counts() .local_table_index(table_index) - .unwrap(); - - let elem = match instance.get_local_table(table_index).ty().ty { - Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), - Type::FuncRef => TableElement::FuncRef(value.func_ref), - _ => panic!("Unrecognized table type: does not contain references"), - }; - - // TODO: type checking, maybe have specialized accessors - let result = instance.table_set(table_index, elem_index, elem); - - if let Err(trap) = result { - raise_lib_trap(trap); + { + let elem = match instance.get_local_table(local_table).ty().ty { + Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + // TODO: type checking, maybe have specialized accessors + let result = instance.table_set(local_table, elem_index, elem); + if let Err(trap) = result { + raise_lib_trap(trap); + } + } else { + panic!("wasmer_vm_imported_table_set should have been called"); } } @@ -414,14 +413,12 @@ pub unsafe extern "C" fn wasmer_vm_imported_table_set( ) { let instance = (&*vmctx).instance(); let table_index = TableIndex::from_u32(table_index); - let elem = match instance.get_table(table_index).ty().ty { + let elem = match instance.get_foreign_table(table_index).ty().ty { Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), Type::FuncRef => TableElement::FuncRef(value.func_ref), _ => panic!("Unrecognized table type: does not contain references"), }; - let result = instance.imported_table_set(table_index, elem_index, elem); - if let Err(trap) = result { raise_lib_trap(trap); } @@ -441,13 +438,11 @@ pub unsafe extern "C" fn wasmer_vm_table_grow( ) -> u32 { let instance = (&*vmctx).instance(); let table_index = LocalTableIndex::from_u32(table_index); - let init_value = match instance.get_local_table(table_index).ty().ty { Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), Type::FuncRef => TableElement::FuncRef(init_value.func_ref), _ => panic!("Unrecognized table type: does not contain references"), }; - instance .table_grow(table_index, delta, init_value) .unwrap_or(u32::max_value()) @@ -684,11 +679,19 @@ pub static wasmer_vm_probestack: unsafe extern "C" fn() = PROBESTACK; /// The name of a runtime library routine. /// /// This list is likely to grow over time. -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) +#[derive( + RkyvSerialize, + RkyvDeserialize, + Archive, + Copy, + Clone, + Debug, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, )] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, MemoryUsage)] pub enum LibCall { /// ceil.f32 CeilF32, diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 0465d3dbf1..8a3fb16b94 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -7,9 +7,7 @@ use crate::mmap::Mmap; use crate::vmcontext::VMMemoryDefinition; -use loupe::MemoryUsage; use more_asserts::assert_ge; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use serde::{Deserialize, Serialize}; use std::borrow::BorrowMut; @@ -64,10 +62,17 @@ pub enum MemoryError { } /// Implementation styles for WebAssembly linear memory. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, MemoryUsage)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + RkyvSerialize, + RkyvDeserialize, + Archive, )] pub enum MemoryStyle { /// The actual memory can be resized and moved. @@ -103,7 +108,7 @@ impl MemoryStyle { } /// Trait for implementing Wasm Memory used by Wasmer. -pub trait Memory: fmt::Debug + Send + Sync + MemoryUsage { +pub trait Memory: fmt::Debug + Send + Sync { /// Returns the memory type for this memory. fn ty(&self) -> MemoryType; @@ -123,7 +128,7 @@ pub trait Memory: fmt::Debug + Send + Sync + MemoryUsage { } /// A linear memory instance. -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] pub struct LinearMemory { // The underlying allocation. mmap: Mutex, @@ -143,15 +148,11 @@ pub struct LinearMemory { /// The owned memory definition used by the generated code vm_memory_definition: VMMemoryDefinitionOwnership, - - // Records whether we're using a bounds-checking strategy which requires - // handlers to catch trapping accesses. - pub(crate) needs_signal_handlers: bool, } /// A type to help manage who is responsible for the backing memory of them /// `VMMemoryDefinition`. -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] enum VMMemoryDefinitionOwnership { /// The `VMMemoryDefinition` is owned by the `Instance` and we should use /// its memory. This is how a local memory that's exported should be stored. @@ -174,7 +175,7 @@ unsafe impl Send for LinearMemory {} /// This is correct because all internal mutability is protected by a mutex. unsafe impl Sync for LinearMemory {} -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] struct WasmMmap { // Our OS allocation of mmap'd memory. alloc: Mmap, @@ -238,15 +239,6 @@ impl LinearMemory { let offset_guard_bytes = style.offset_guard_size() as usize; - // If we have an offset guard, or if we're doing the static memory - // allocation strategy, we need signal handlers to catch out of bounds - // acceses. - let needs_signal_handlers = offset_guard_bytes > 0 - || match style { - MemoryStyle::Dynamic { .. } => false, - MemoryStyle::Static { .. } => true, - }; - let minimum_pages = match style { MemoryStyle::Dynamic { .. } => memory.minimum, MemoryStyle::Static { bound, .. } => { @@ -266,12 +258,11 @@ impl LinearMemory { }; let base_ptr = mmap.alloc.as_mut_ptr(); - let mem_length = memory.minimum.bytes().0.try_into().unwrap(); + let mem_length = memory.minimum.bytes().0; Ok(Self { mmap: Mutex::new(mmap), maximum: memory.maximum, offset_guard_size: offset_guard_bytes, - needs_signal_handlers, vm_memory_definition: if let Some(mem_loc) = vm_memory_location { { let mut ptr = mem_loc; @@ -312,7 +303,7 @@ impl Memory for LinearMemory { /// Returns the type for this memory. fn ty(&self) -> MemoryType { let minimum = self.size(); - let mut out = self.memory.clone(); + let mut out = self.memory; out.minimum = minimum; out @@ -410,7 +401,7 @@ impl Memory for LinearMemory { unsafe { let mut md_ptr = self.get_vm_memory_definition(); let md = md_ptr.as_mut(); - md.current_length = new_pages.bytes().0.try_into().unwrap(); + md.current_length = new_pages.bytes().0; md.base = mmap.alloc.as_mut_ptr() as _; } diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 7f33e88628..6b3dcdd19c 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -4,7 +4,6 @@ //! Low-level abstraction for allocating and managing zero-filled pages //! of memory. -use loupe::{MemoryUsage, MemoryUsageTracker}; use more_asserts::assert_le; use more_asserts::assert_lt; use std::io; @@ -280,14 +279,6 @@ impl Drop for Mmap { } } -impl MemoryUsage for Mmap { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - tracker.track(self.as_ptr() as *const ()); - - self.len() - } -} - fn _assert() { fn _assert_send_sync() {} _assert_send_sync::(); diff --git a/lib/vm/src/resolver.rs b/lib/vm/src/resolver.rs new file mode 100644 index 0000000000..010eaad0ab --- /dev/null +++ b/lib/vm/src/resolver.rs @@ -0,0 +1,332 @@ +use std::sync::Arc; + +use crate::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable}; + +/// The value of an export passed from one instance to another. +#[derive(Debug, Clone)] +pub enum Export { + /// A function export value. + Function(ExportFunction), + + /// A table export value. + Table(VMTable), + + /// A memory export value. + Memory(VMMemory), + + /// A global export value. + Global(VMGlobal), +} + +impl From for VMExtern { + fn from(other: Export) -> Self { + match other { + Export::Function(ExportFunction { vm_function, .. }) => Self::Function(vm_function), + Export::Memory(vm_memory) => Self::Memory(vm_memory), + Export::Table(vm_table) => Self::Table(vm_table), + Export::Global(vm_global) => Self::Global(vm_global), + } + } +} + +impl From for Export { + fn from(other: VMExtern) -> Self { + match other { + VMExtern::Function(vm_function) => Self::Function(ExportFunction { + vm_function, + metadata: None, + }), + VMExtern::Memory(vm_memory) => Self::Memory(vm_memory), + VMExtern::Table(vm_table) => Self::Table(vm_table), + VMExtern::Global(vm_global) => Self::Global(vm_global), + } + } +} + +/// Extra metadata about `ExportFunction`s. +/// +/// The metadata acts as a kind of manual virtual dispatch. We store the +/// user-supplied `WasmerEnv` as a void pointer and have methods on it +/// that have been adapted to accept a void pointer. +/// +/// This struct owns the original `host_env`, thus when it gets dropped +/// it calls the `drop` function on it. +#[derive(Debug, PartialEq)] +pub struct ExportFunctionMetadata { + /// This field is stored here to be accessible by `Drop`. + /// + /// At the time it was added, it's not accessed anywhere outside of + /// the `Drop` implementation. This field is the "master copy" of the env, + /// that is, the original env passed in by the user. Every time we create + /// an `Instance` we clone this with the `host_env_clone_fn` field. + /// + /// Thus, we only bother to store the master copy at all here so that + /// we can free it. + /// + /// See `wasmer_vm::export::VMFunction::vmctx` for the version of + /// this pointer that is used by the VM when creating an `Instance`. + pub host_env: *mut std::ffi::c_void, + + /// Function pointer to `WasmerEnv::init_with_instance(&mut self, instance: &Instance)`. + /// + /// This function is called to finish setting up the environment after + /// we create the `api::Instance`. + // This one is optional for now because dynamic host envs need the rest + // of this without the init fn + pub import_init_function_ptr: Option, + + /// A function analogous to `Clone::clone` that returns a leaked `Box`. + pub host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, + + /// The destructor to free the host environment. + /// + /// # Safety + /// - This function should only be called in when properly synchronized. + /// For example, in the `Drop` implementation of this type. + pub host_env_drop_fn: unsafe fn(*mut std::ffi::c_void), +} + +/// This can be `Send` because `host_env` comes from `WasmerEnv` which is +/// `Send`. Therefore all operations should work on any thread. +unsafe impl Send for ExportFunctionMetadata {} +/// This data may be shared across threads, `drop` is an unsafe function +/// pointer, so care must be taken when calling it. +unsafe impl Sync for ExportFunctionMetadata {} + +impl ExportFunctionMetadata { + /// Create an `ExportFunctionMetadata` type with information about + /// the exported function. + /// + /// # Safety + /// - the `host_env` must be `Send`. + /// - all function pointers must work on any thread. + pub unsafe fn new( + host_env: *mut std::ffi::c_void, + import_init_function_ptr: Option, + host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, + host_env_drop_fn: fn(*mut std::ffi::c_void), + ) -> Self { + Self { + host_env, + import_init_function_ptr, + host_env_clone_fn, + host_env_drop_fn, + } + } +} + +// We have to free `host_env` here because we always clone it before using it +// so all the `host_env`s freed at the `Instance` level won't touch the original. +impl Drop for ExportFunctionMetadata { + fn drop(&mut self) { + if !self.host_env.is_null() { + // # Safety + // - This is correct because we know no other references + // to this data can exist if we're dropping it. + unsafe { + (self.host_env_drop_fn)(self.host_env); + } + } + } +} + +/// A function export value with an extra function pointer to initialize +/// host environments. +#[derive(Debug, Clone, PartialEq)] +pub struct ExportFunction { + /// The VM function, containing most of the data. + pub vm_function: VMFunction, + /// Contains functions necessary to create and initialize host envs + /// with each `Instance` as well as being responsible for the + /// underlying memory of the host env. + pub metadata: Option>, +} + +impl From for Export { + fn from(func: ExportFunction) -> Self { + Self::Function(func) + } +} + +impl From for Export { + fn from(table: VMTable) -> Self { + Self::Table(table) + } +} + +impl From for Export { + fn from(memory: VMMemory) -> Self { + Self::Memory(memory) + } +} + +impl From for Export { + fn from(global: VMGlobal) -> Self { + Self::Global(global) + } +} + +/// +/// Import resolver connects imports with available exported values. +pub trait Resolver { + /// Resolves an import a WebAssembly module to an export it's hooked up to. + /// + /// The `index` provided is the index of the import in the wasm module + /// that's being resolved. For example 1 means that it's the second import + /// listed in the wasm module. + /// + /// The `module` and `field` arguments provided are the module/field names + /// listed on the import itself. + /// + /// # Notes: + /// + /// The index is useful because some WebAssembly modules may rely on that + /// for resolving ambiguity in their imports. Such as: + /// ```ignore + /// (module + /// (import "" "" (func)) + /// (import "" "" (func (param i32) (result i32))) + /// ) + /// ``` + fn resolve(&self, _index: u32, module: &str, field: &str) -> Option; +} + +/// Import resolver connects imports with available exported values. +/// +/// This is a specific subtrait for [`Resolver`] for those users who don't +/// care about the `index`, but only about the `module` and `field` for +/// the resolution. +pub trait NamedResolver { + /// Resolves an import a WebAssembly module to an export it's hooked up to. + /// + /// It receives the `module` and `field` names and return the [`Export`] in + /// case it's found. + fn resolve_by_name(&self, module: &str, field: &str) -> Option; +} + +// All NamedResolvers should extend `Resolver`. +impl Resolver for T { + /// By default this method will be calling [`NamedResolver::resolve_by_name`], + /// dismissing the provided `index`. + fn resolve(&self, _index: u32, module: &str, field: &str) -> Option { + self.resolve_by_name(module, field) + } +} + +impl NamedResolver for &T { + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + (**self).resolve_by_name(module, field) + } +} + +impl NamedResolver for Box { + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + (**self).resolve_by_name(module, field) + } +} + +impl NamedResolver for () { + /// Always returns `None`. + fn resolve_by_name(&self, _module: &str, _field: &str) -> Option { + None + } +} + +/// `Resolver` implementation that always resolves to `None`. Equivalent to `()`. +pub struct NullResolver {} + +impl Resolver for NullResolver { + fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option { + None + } +} + +/// A [`Resolver`] that links two resolvers together in a chain. +pub struct NamedResolverChain { + a: A, + b: B, +} + +/// A trait for chaining resolvers together. +/// +/// ``` +/// # use wasmer_vm::{ChainableNamedResolver, NamedResolver}; +/// # fn chainable_test(imports1: A, imports2: B) +/// # where A: NamedResolver + Sized + Send + Sync, +/// # B: NamedResolver + Sized + Send + Sync, +/// # { +/// // override duplicates with imports from `imports2` +/// imports1.chain_front(imports2); +/// # } +/// ``` +pub trait ChainableNamedResolver: NamedResolver + Sized + Send + Sync { + /// Chain a resolver in front of the current resolver. + /// + /// This will cause the second resolver to override the first. + /// + /// ``` + /// # use wasmer_vm::{ChainableNamedResolver, NamedResolver}; + /// # fn chainable_test(imports1: A, imports2: B) + /// # where A: NamedResolver + Sized + Send + Sync, + /// # B: NamedResolver + Sized + Send + Sync, + /// # { + /// // override duplicates with imports from `imports2` + /// imports1.chain_front(imports2); + /// # } + /// ``` + fn chain_front(self, other: U) -> NamedResolverChain + where + U: NamedResolver + Send + Sync, + { + NamedResolverChain { a: other, b: self } + } + + /// Chain a resolver behind the current resolver. + /// + /// This will cause the first resolver to override the second. + /// + /// ``` + /// # use wasmer_vm::{ChainableNamedResolver, NamedResolver}; + /// # fn chainable_test(imports1: A, imports2: B) + /// # where A: NamedResolver + Sized + Send + Sync, + /// # B: NamedResolver + Sized + Send + Sync, + /// # { + /// // override duplicates with imports from `imports1` + /// imports1.chain_back(imports2); + /// # } + /// ``` + fn chain_back(self, other: U) -> NamedResolverChain + where + U: NamedResolver + Send + Sync, + { + NamedResolverChain { a: self, b: other } + } +} + +// We give these chain methods to all types implementing NamedResolver +impl ChainableNamedResolver for T {} + +impl NamedResolver for NamedResolverChain +where + A: NamedResolver + Send + Sync, + B: NamedResolver + Send + Sync, +{ + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + self.a + .resolve_by_name(module, field) + .or_else(|| self.b.resolve_by_name(module, field)) + } +} + +impl Clone for NamedResolverChain +where + A: NamedResolver + Clone + Send + Sync, + B: NamedResolver + Clone + Send + Sync, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + } + } +} diff --git a/lib/vm/src/sig_registry.rs b/lib/vm/src/sig_registry.rs index 051006bc96..de0cc352ac 100644 --- a/lib/vm/src/sig_registry.rs +++ b/lib/vm/src/sig_registry.rs @@ -4,59 +4,63 @@ //! Implement a registry of function signatures, for fast indirect call //! signature checking. -use crate::vmcontext::VMSharedSignatureIndex; -use loupe::MemoryUsage; -use more_asserts::{assert_lt, debug_assert_lt}; use std::collections::{hash_map, HashMap}; use std::convert::TryFrom; -use std::sync::RwLock; -use wasmer_types::FunctionType; +use wasmer_types::{FunctionType, FunctionTypeRef}; + +/// An index into the shared signature registry, usable for checking signatures +/// at indirect calls. +#[repr(C)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +pub struct VMSharedSignatureIndex(u32); + +impl VMSharedSignatureIndex { + /// Create a new `VMSharedSignatureIndex`. + pub fn new(value: u32) -> Self { + Self(value) + } +} /// WebAssembly requires that the caller and callee signatures in an indirect /// call must match. To implement this efficiently, keep a registry of all /// signatures, shared by all instances, so that call sites can just do an /// index comparison. -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] pub struct SignatureRegistry { - // This structure is stored in an `Engine` and is intended to be shared - // across many instances. Ideally instances can themselves be sent across - // threads, and ideally we can compile across many threads. As a result we - // use interior mutability here with a lock to avoid having callers to - // externally synchronize calls to compilation. - inner: RwLock, -} - -#[derive(Debug, Default, MemoryUsage)] -struct Inner { - signature2index: HashMap, - index2signature: HashMap, + type_to_index: HashMap, + index_to_data: Vec, } impl SignatureRegistry { /// Create a new `SignatureRegistry`. pub fn new() -> Self { Self { - inner: Default::default(), + type_to_index: HashMap::new(), + index_to_data: Vec::new(), } } /// Register a signature and return its unique index. - pub fn register(&self, sig: &FunctionType) -> VMSharedSignatureIndex { - let mut inner = self.inner.write().unwrap(); - let len = inner.signature2index.len(); - match inner.signature2index.entry(sig.clone()) { + pub fn register(&mut self, sig: FunctionTypeRef<'_>) -> VMSharedSignatureIndex { + let len = self.index_to_data.len(); + // TODO(0-copy): this. should. not. allocate. + // + // This is pretty hard to avoid, however. In order to implement bijective map, we'd want + // a `Rc`, but indexing into a map keyed by `Rc` with + // `FunctionTypeRef` is… not possible given the current API either. + // + // Consider `transmute` or `hashbrown`'s raw_entry. + let sig = FunctionType::new(sig.params(), sig.results()); + match self.type_to_index.entry(sig.clone()) { hash_map::Entry::Occupied(entry) => *entry.get(), hash_map::Entry::Vacant(entry) => { - // Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX) - // is reserved for VMSharedSignatureIndex::default(). - debug_assert_lt!( - len, - std::u32::MAX as usize, - "Invariant check: signature_hash.len() < std::u32::MAX" + debug_assert!( + u32::try_from(len).is_ok(), + "invariant: can't have more than 2³²-1 signatures!" ); let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); entry.insert(sig_id); - inner.index2signature.insert(sig_id, sig.clone()); + self.index_to_data.push(sig); sig_id } } @@ -66,12 +70,7 @@ impl SignatureRegistry { /// /// Note that for this operation to be semantically correct the `idx` must /// have previously come from a call to `register` of this same object. - pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option { - self.inner - .read() - .unwrap() - .index2signature - .get(&idx) - .cloned() + pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<&FunctionType> { + self.index_to_data.get(idx.0 as usize) } } diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index 6f8abe4fdd..10c5556de4 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -9,8 +9,6 @@ use crate::func_data_registry::VMFuncRef; use crate::trap::{Trap, TrapCode}; use crate::vmcontext::VMTableDefinition; use crate::VMExternRef; -use loupe::{MemoryUsage, MemoryUsageTracker}; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use serde::{Deserialize, Serialize}; use std::borrow::{Borrow, BorrowMut}; @@ -22,10 +20,17 @@ use std::sync::Mutex; use wasmer_types::{ExternRef, TableType, Type as ValType}; /// Implementation styles for WebAssembly tables. -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, MemoryUsage)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + Serialize, + Deserialize, + RkyvSerialize, + RkyvDeserialize, + Archive, )] pub enum TableStyle { /// Signatures are stored in the table and checked in the caller. @@ -33,7 +38,7 @@ pub enum TableStyle { } /// Trait for implementing the interface of a Wasm table. -pub trait Table: fmt::Debug + Send + Sync + MemoryUsage { +pub trait Table: fmt::Debug + Send + Sync { /// Returns the style for this Table. fn style(&self) -> &TableStyle; @@ -148,12 +153,6 @@ fn table_element_size_test() { assert_eq!(size_of::(), size_of::()); } -impl MemoryUsage for RawTableElement { - fn size_of_val(&self, _: &mut dyn MemoryUsageTracker) -> usize { - std::mem::size_of_val(self) - } -} - impl fmt::Debug for RawTableElement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawTableElement").finish() @@ -175,7 +174,7 @@ impl Default for TableElement { } /// A table instance. -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] pub struct LinearTable { // TODO: we can remove the mutex by using atomic swaps and preallocating the max table size vec: Mutex>, @@ -189,7 +188,7 @@ pub struct LinearTable { /// A type to help manage who is responsible for the backing table of the /// `VMTableDefinition`. -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] enum VMTableDefinitionOwnership { /// The `VMTableDefinition` is owned by the `Instance` and we should use /// its table. This is how a local table that's exported should be stored. @@ -341,9 +340,9 @@ impl Table for LinearTable { let extern_ref: VMExternRef = extern_ref.into(); // We reduce the amount we increment by because `into` prevents // dropping `init_value` (which is a caller-inc'd ref). - (new_len as usize) - .checked_sub(size as usize + 1) - .map(|val| extern_ref.ref_inc_by(val)); + if let Some(val) = (new_len as usize).checked_sub(size as usize + 1) { + extern_ref.ref_inc_by(val); + } RawTableElement { extern_ref } } TableElement::FuncRef(func_ref) => RawTableElement { func_ref }, diff --git a/lib/vm/src/trap/mod.rs b/lib/vm/src/trap/mod.rs index ff7e276167..691a080f17 100644 --- a/lib/vm/src/trap/mod.rs +++ b/lib/vm/src/trap/mod.rs @@ -7,8 +7,8 @@ mod trapcode; pub mod traphandlers; pub use trapcode::TrapCode; +pub use traphandlers::resume_panic; pub use traphandlers::{ catch_traps, catch_traps_with_result, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, - TlsRestore, Trap, TrapHandler, TrapHandlerFn, + TlsRestore, Trap, }; -pub use traphandlers::{init_traps, resume_panic}; diff --git a/lib/vm/src/trap/trapcode.rs b/lib/vm/src/trap/trapcode.rs index 49d25cc402..25c93578c7 100644 --- a/lib/vm/src/trap/trapcode.rs +++ b/lib/vm/src/trap/trapcode.rs @@ -5,8 +5,6 @@ use core::fmt::{self, Display, Formatter}; use core::str::FromStr; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -14,10 +12,19 @@ use thiserror::Error; /// A trap code describing the reason for a trap. /// /// All trap instructions have an explicit trap code. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Error, MemoryUsage)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) +#[derive( + Clone, + Copy, + PartialEq, + Eq, + Debug, + Hash, + Serialize, + Deserialize, + Error, + RkyvSerialize, + RkyvDeserialize, + Archive, )] #[repr(u32)] pub enum TrapCode { @@ -115,18 +122,18 @@ impl FromStr for TrapCode { fn from_str(s: &str) -> Result { match s { - "stk_ovf" => Ok(TrapCode::StackOverflow), - "heap_get_oob" => Ok(TrapCode::HeapAccessOutOfBounds), - "heap_misaligned" => Ok(TrapCode::HeapMisaligned), - "table_get_oob" => Ok(TrapCode::TableAccessOutOfBounds), - "oob" => Ok(TrapCode::OutOfBounds), - "icall_null" => Ok(TrapCode::IndirectCallToNull), - "bad_sig" => Ok(TrapCode::BadSignature), - "int_ovf" => Ok(TrapCode::IntegerOverflow), - "int_divz" => Ok(TrapCode::IntegerDivisionByZero), - "bad_toint" => Ok(TrapCode::BadConversionToInteger), - "unreachable" => Ok(TrapCode::UnreachableCodeReached), - "unalign_atom" => Ok(TrapCode::UnalignedAtomic), + "stk_ovf" => Ok(Self::StackOverflow), + "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds), + "heap_misaligned" => Ok(Self::HeapMisaligned), + "table_get_oob" => Ok(Self::TableAccessOutOfBounds), + "oob" => Ok(Self::OutOfBounds), + "icall_null" => Ok(Self::IndirectCallToNull), + "bad_sig" => Ok(Self::BadSignature), + "int_ovf" => Ok(Self::IntegerOverflow), + "int_divz" => Ok(Self::IntegerDivisionByZero), + "bad_toint" => Ok(Self::BadConversionToInteger), + "unreachable" => Ok(Self::UnreachableCodeReached), + "unalign_atom" => Ok(Self::UnalignedAtomic), _ => Err(()), } } diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index 5fcf020230..18287d0051 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -10,22 +10,10 @@ use backtrace::Backtrace; use std::any::Any; use std::cell::{Cell, UnsafeCell}; use std::error::Error; -use std::io; use std::mem::{self, MaybeUninit}; use std::ptr; -use std::sync::Once; pub use tls::TlsRestore; -cfg_if::cfg_if! { - if #[cfg(unix)] { - /// Function which may handle custom signals while processing traps. - pub type TrapHandlerFn = dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool; - } else if #[cfg(target_os = "windows")] { - /// Function which may handle custom signals while processing traps. - pub type TrapHandlerFn = dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool; - } -} - extern "C" { fn wasmer_register_setjmp( jmp_buf: *mut *const u8, @@ -35,367 +23,6 @@ extern "C" { fn wasmer_unwind(jmp_buf: *const u8) -> !; } -cfg_if::cfg_if! { - if #[cfg(unix)] { - static mut PREV_SIGSEGV: MaybeUninit = MaybeUninit::uninit(); - static mut PREV_SIGBUS: MaybeUninit = MaybeUninit::uninit(); - static mut PREV_SIGILL: MaybeUninit = MaybeUninit::uninit(); - static mut PREV_SIGFPE: MaybeUninit = MaybeUninit::uninit(); - - unsafe fn platform_init() { - let register = |slot: &mut MaybeUninit, signal: i32| { - let mut handler: libc::sigaction = mem::zeroed(); - // The flags here are relatively careful, and they are... - // - // SA_SIGINFO gives us access to information like the program - // counter from where the fault happened. - // - // SA_ONSTACK allows us to handle signals on an alternate stack, - // so that the handler can run in response to running out of - // stack space on the main stack. Rust installs an alternate - // stack with sigaltstack, so we rely on that. - // - // SA_NODEFER allows us to reenter the signal handler if we - // crash while handling the signal, and fall through to the - // Breakpad handler by testing handlingSegFault. - handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK; - handler.sa_sigaction = trap_handler as usize; - libc::sigemptyset(&mut handler.sa_mask); - if libc::sigaction(signal, &handler, slot.as_mut_ptr()) != 0 { - panic!( - "unable to install signal handler: {}", - io::Error::last_os_error(), - ); - } - }; - - // Allow handling OOB with signals on all architectures - register(&mut PREV_SIGSEGV, libc::SIGSEGV); - - // Handle `unreachable` instructions which execute `ud2` right now - register(&mut PREV_SIGILL, libc::SIGILL); - - // x86 uses SIGFPE to report division by zero - if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { - register(&mut PREV_SIGFPE, libc::SIGFPE); - } - - // On ARM, handle Unaligned Accesses. - // On Darwin, guard page accesses are raised as SIGBUS. - if cfg!(target_arch = "arm") || cfg!(target_vendor = "apple") { - register(&mut PREV_SIGBUS, libc::SIGBUS); - } - } - - #[cfg(target_vendor = "apple")] - unsafe fn thread_stack() -> (usize, usize) { - let this_thread = libc::pthread_self(); - let stackaddr = libc::pthread_get_stackaddr_np(this_thread); - let stacksize = libc::pthread_get_stacksize_np(this_thread); - (stackaddr as usize - stacksize, stacksize) - } - - #[cfg(not(target_vendor = "apple"))] - unsafe fn thread_stack() -> (usize, usize) { - let this_thread = libc::pthread_self(); - let mut thread_attrs: libc::pthread_attr_t = mem::zeroed(); - let mut stackaddr: *mut libc::c_void = ptr::null_mut(); - let mut stacksize: libc::size_t = 0; - #[cfg(not(target_os = "freebsd"))] - let ok = libc::pthread_getattr_np(this_thread, &mut thread_attrs); - #[cfg(target_os = "freebsd")] - let ok = libc::pthread_attr_get_np(this_thread, &mut thread_attrs); - if ok == 0 { - libc::pthread_attr_getstack(&thread_attrs, &mut stackaddr, &mut stacksize); - libc::pthread_attr_destroy(&mut thread_attrs); - } - (stackaddr as usize, stacksize) - } - - unsafe extern "C" fn trap_handler( - signum: libc::c_int, - siginfo: *mut libc::siginfo_t, - context: *mut libc::c_void, - ) { - let previous = match signum { - libc::SIGSEGV => &PREV_SIGSEGV, - libc::SIGBUS => &PREV_SIGBUS, - libc::SIGFPE => &PREV_SIGFPE, - libc::SIGILL => &PREV_SIGILL, - _ => panic!("unknown signal: {}", signum), - }; - // We try to get the Code trap associated to this signal - let maybe_signal_trap = match signum { - libc::SIGSEGV | libc::SIGBUS => { - let addr = (*siginfo).si_addr() as usize; - let (stackaddr, stacksize) = thread_stack(); - // The stack and its guard page covers the - // range [stackaddr - guard pages .. stackaddr + stacksize). - // We assume the guard page is 1 page, and pages are 4KiB (or 16KiB in Apple Silicon) - if stackaddr - region::page::size() <= addr && addr < stackaddr + stacksize { - Some(TrapCode::StackOverflow) - } else { - Some(TrapCode::HeapAccessOutOfBounds) - } - } - _ => None, - }; - let handled = tls::with(|info| { - // If no wasm code is executing, we don't handle this as a wasm - // trap. - let info = match info { - Some(info) => info, - None => return false, - }; - - // If we hit an exception while handling a previous trap, that's - // quite bad, so bail out and let the system handle this - // recursive segfault. - // - // Otherwise flag ourselves as handling a trap, do the trap - // handling, and reset our trap handling flag. Then we figure - // out what to do based on the result of the trap handling. - let jmp_buf = info.handle_trap( - get_pc(context), - false, - maybe_signal_trap, - |handler| handler(signum, siginfo, context), - ); - - // Figure out what to do based on the result of this handling of - // the trap. Note that our sentinel value of 1 means that the - // exception was handled by a custom exception handler, so we - // keep executing. - if jmp_buf.is_null() { - false - } else if jmp_buf as usize == 1 { - true - } else { - wasmer_unwind(jmp_buf) - } - }); - - if handled { - return; - } - - // This signal is not for any compiled wasm code we expect, so we - // need to forward the signal to the next handler. If there is no - // next handler (SIG_IGN or SIG_DFL), then it's time to crash. To do - // this, we set the signal back to its original disposition and - // return. This will cause the faulting op to be re-executed which - // will crash in the normal way. If there is a next handler, call - // it. It will either crash synchronously, fix up the instruction - // so that execution can continue and return, or trigger a crash by - // returning the signal to it's original disposition and returning. - let previous = &*previous.as_ptr(); - if previous.sa_flags & libc::SA_SIGINFO != 0 { - mem::transmute::< - usize, - extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void), - >(previous.sa_sigaction)(signum, siginfo, context) - } else if previous.sa_sigaction == libc::SIG_DFL || - previous.sa_sigaction == libc::SIG_IGN - { - libc::sigaction(signum, previous, ptr::null_mut()); - } else { - mem::transmute::( - previous.sa_sigaction - )(signum) - } - } - - unsafe fn get_pc(cx: *mut libc::c_void) -> *const u8 { - cfg_if::cfg_if! { - if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.gregs[libc::REG_RIP as usize] as *const u8 - } else if #[cfg(all(target_os = "linux", target_arch = "x86"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.gregs[libc::REG_EIP as usize] as *const u8 - } else if #[cfg(all(target_os = "android", target_arch = "x86"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.gregs[libc::REG_EIP as usize] as *const u8 - } else if #[cfg(all(target_os = "linux", target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.pc as *const u8 - } else if #[cfg(all(target_os = "android", target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.pc as *const u8 - } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - (*cx.uc_mcontext).__ss.__rip as *const u8 - } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] { - use std::mem; - // TODO: This should be integrated into rust/libc - // Related issue: https://github.com/rust-lang/libc/issues/1977 - #[allow(non_camel_case_types)] - pub struct __darwin_arm_thread_state64 { - pub __x: [u64; 29], /* General purpose registers x0-x28 */ - pub __fp: u64, /* Frame pointer x29 */ - pub __lr: u64, /* Link register x30 */ - pub __sp: u64, /* Stack pointer x31 */ - pub __pc: u64, /* Program counter */ - pub __cpsr: u32, /* Current program status register */ - pub __pad: u32, /* Same size for 32-bit or 64-bit clients */ - } - - let cx = &*(cx as *const libc::ucontext_t); - let uc_mcontext = mem::transmute::<_, *const __darwin_arm_thread_state64>(&(*cx.uc_mcontext).__ss); - (*uc_mcontext).__pc as *const u8 - } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.mc_rip as *const u8 - } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] { - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct gpregs { - pub gp_x: [libc::register_t; 30], - pub gp_lr: libc::register_t, - pub gp_sp: libc::register_t, - pub gp_elr: libc::register_t, - pub gp_spsr: u32, - pub gp_pad: libc::c_int, - }; - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct fpregs { - pub fp_q: [u128; 32], - pub fp_sr: u32, - pub fp_cr: u32, - pub fp_flags: libc::c_int, - pub fp_pad: libc::c_int, - }; - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct mcontext_t { - pub mc_gpregs: gpregs, - pub mc_fpregs: fpregs, - pub mc_flags: libc::c_int, - pub mc_pad: libc::c_int, - pub mc_spare: [u64; 8], - }; - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct ucontext_t { - pub uc_sigmask: libc::sigset_t, - pub uc_mcontext: mcontext_t, - pub uc_link: *mut ucontext_t, - pub uc_stack: libc::stack_t, - pub uc_flags: libc::c_int, - __spare__: [libc::c_int; 4], - } - - let cx = &*(cx as *const ucontext_t); - cx.uc_mcontext.mc_gpregs.gp_elr as *const u8 - } else { - compile_error!("unsupported platform"); - } - } - } - } else if #[cfg(target_os = "windows")] { - use winapi::um::errhandlingapi::*; - use winapi::um::winnt::*; - use winapi::um::minwinbase::*; - use winapi::vc::excpt::*; - - unsafe fn platform_init() { - // our trap handler needs to go first, so that we can recover from - // wasm faults and continue execution, so pass `1` as a true value - // here. - if AddVectoredExceptionHandler(1, Some(exception_handler)).is_null() { - panic!("failed to add exception handler: {}", io::Error::last_os_error()); - } - } - - unsafe extern "system" fn exception_handler( - exception_info: PEXCEPTION_POINTERS - ) -> LONG { - // Check the kind of exception, since we only handle a subset within - // wasm code. If anything else happens we want to defer to whatever - // the rest of the system wants to do for this exception. - let record = &*(*exception_info).ExceptionRecord; - if record.ExceptionCode != EXCEPTION_ACCESS_VIOLATION && - record.ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && - record.ExceptionCode != EXCEPTION_STACK_OVERFLOW && - record.ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO && - record.ExceptionCode != EXCEPTION_INT_OVERFLOW - { - return EXCEPTION_CONTINUE_SEARCH; - } - - // FIXME: this is what the previous C++ did to make sure that TLS - // works by the time we execute this trap handling code. This isn't - // exactly super easy to call from Rust though and it's not clear we - // necessarily need to do so. Leaving this here in case we need this - // in the future, but for now we can probably wait until we see a - // strange fault before figuring out how to reimplement this in - // Rust. - // - // if (!NtCurrentTeb()->Reserved1[sThreadLocalArrayPointerIndex]) { - // return EXCEPTION_CONTINUE_SEARCH; - // } - - // This is basically the same as the unix version above, only with a - // few parameters tweaked here and there. - tls::with(|info| { - let info = match info { - Some(info) => info, - None => return EXCEPTION_CONTINUE_SEARCH, - }; - #[cfg(target_pointer_width = "32")] - let pc = (*(*exception_info).ContextRecord).Eip as *const u8; - - #[cfg(target_pointer_width = "64")] - let pc = (*(*exception_info).ContextRecord).Rip as *const u8; - - let jmp_buf = info.handle_trap( - pc, - record.ExceptionCode == EXCEPTION_STACK_OVERFLOW, - // TODO: fix the signal trap associated to memory access in Windows - None, - |handler| handler(exception_info), - ); - if jmp_buf.is_null() { - EXCEPTION_CONTINUE_SEARCH - } else if jmp_buf as usize == 1 { - EXCEPTION_CONTINUE_EXECUTION - } else { - wasmer_unwind(jmp_buf) - } - }) - } - } -} - -/// Globally-set callback to determine whether a program counter is actually a -/// wasm trap. -/// -/// This is initialized during `init_traps` below. The definition lives within -/// `wasmer` currently. -static mut IS_WASM_PC: fn(usize) -> bool = |_| false; - -/// This function is required to be called before any WebAssembly is entered. -/// This will configure global state such as signal handlers to prepare the -/// process to receive wasm traps. -/// -/// This function must not only be called globally once before entering -/// WebAssembly but it must also be called once-per-thread that enters -/// WebAssembly. Currently in wasmer's integration this function is called on -/// creation of a `Store`. -/// -/// The `is_wasm_pc` argument is used when a trap happens to determine if a -/// program counter is the pc of an actual wasm trap or not. This is then used -/// to disambiguate faults that happen due to wasm and faults that happen due to -/// bugs in Rust or elsewhere. -pub fn init_traps(is_wasm_pc: fn(usize) -> bool) { - static INIT: Once = Once::new(); - INIT.call_once(|| unsafe { - IS_WASM_PC = is_wasm_pc; - platform_init(); - }); -} - /// Raises a user-defined trap immediately. /// /// This function performs as-if a wasm trap was just executed, only the trap @@ -439,22 +66,6 @@ pub unsafe fn resume_panic(payload: Box) -> ! { tls::with(|info| info.unwrap().unwind_with(UnwindReason::Panic(payload))) } -#[cfg(target_os = "windows")] -fn reset_guard_page() { - extern "C" { - fn _resetstkoflw() -> winapi::ctypes::c_int; - } - - // We need to restore guard page under stack to handle future stack overflows properly. - // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=vs-2019 - if unsafe { _resetstkoflw() } == 0 { - panic!("failed to restore stack guard page"); - } -} - -#[cfg(not(target_os = "windows"))] -fn reset_guard_page() {} - /// Stores trace message with backtrace. #[derive(Debug)] pub enum Trap { @@ -497,7 +108,7 @@ impl Trap { /// /// Internally saves a backtrace when constructed. pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { - Trap::Wasm { + Self::Wasm { pc, backtrace, signal_trap, @@ -509,7 +120,7 @@ impl Trap { /// Internally saves a backtrace when constructed. pub fn lib(trap_code: TrapCode) -> Self { let backtrace = Backtrace::new_unresolved(); - Trap::Lib { + Self::Lib { trap_code, backtrace, } @@ -520,7 +131,7 @@ impl Trap { /// Internally saves a backtrace when constructed. pub fn oom() -> Self { let backtrace = Backtrace::new_unresolved(); - Trap::OOM { backtrace } + Self::OOM { backtrace } } } @@ -540,7 +151,6 @@ impl Trap { /// Wildly unsafe because it calls raw function pointers and reads/writes raw /// function pointers. pub unsafe fn wasmer_call_trampoline( - trap_handler: &impl TrapHandler, vmctx: VMFunctionEnvironment, trampoline: VMTrampoline, callee: *const VMFunctionBody, @@ -549,7 +159,7 @@ pub unsafe fn wasmer_call_trampoline( // `vmctx` is always `*mut VMContext` here, as we call to WASM. let ctx = vmctx.vmctx; (*ctx).instance().on_call(); - catch_traps(trap_handler, || { + catch_traps(|| { mem::transmute::<_, extern "C" fn(VMFunctionEnvironment, *const VMFunctionBody, *mut u8)>( trampoline, )(vmctx, callee, values_vec); @@ -559,12 +169,14 @@ pub unsafe fn wasmer_call_trampoline( /// Catches any wasm traps that happen within the execution of `closure`, /// returning them as a `Result`. /// -/// Highly unsafe since `closure` won't have any dtors run. -pub unsafe fn catch_traps(trap_handler: &dyn TrapHandler, mut closure: F) -> Result<(), Trap> +/// # Safety +/// +/// Soundness must not depend on `closure` destructors being run. +pub unsafe fn catch_traps(mut closure: F) -> Result<(), Trap> where F: FnMut(), { - return CallThreadState::new(trap_handler).with(|cx| { + return CallThreadState::new().with(|cx| { wasmer_register_setjmp( cx.jmp_buf.as_ptr(), call_closure::, @@ -589,15 +201,12 @@ where /// # Safety /// /// Check [`catch_traps`]. -pub unsafe fn catch_traps_with_result( - trap_handler: &dyn TrapHandler, - mut closure: F, -) -> Result +pub unsafe fn catch_traps_with_result(mut closure: F) -> Result where F: FnMut() -> R, { let mut global_results = MaybeUninit::::uninit(); - catch_traps(trap_handler, || { + catch_traps(|| { global_results.as_mut_ptr().write(closure()); })?; Ok(global_results.assume_init()) @@ -605,29 +214,10 @@ where /// Temporary state stored on the stack which is registered in the `tls` module /// below for calls into wasm. -pub struct CallThreadState<'a> { +pub struct CallThreadState { unwind: UnsafeCell>, jmp_buf: Cell<*const u8>, - reset_guard_page: Cell, prev: Cell, - trap_handler: &'a (dyn TrapHandler + 'a), - handling_trap: Cell, -} - -/// A package of functionality needed by `catch_traps` to figure out what to do -/// when handling a trap. -/// -/// Note that this is an `unsafe` trait at least because it's being run in the -/// context of a synchronous signal handler, so it needs to be careful to not -/// access too much state in answering these queries. -pub unsafe trait TrapHandler { - /// Converts this object into an `Any` to dynamically check its type. - fn as_any(&self) -> &dyn Any; - - /// Uses `call` to call a custom signal handler, if one is specified. - /// - /// Returns `true` if `call` returns true, otherwise returns `false`. - fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool; } enum UnwindReason { @@ -645,20 +235,17 @@ enum UnwindReason { }, } -impl<'a> CallThreadState<'a> { +impl<'a> CallThreadState { #[inline] - fn new(trap_handler: &'a (dyn TrapHandler + 'a)) -> CallThreadState<'a> { + fn new() -> Self { Self { unwind: UnsafeCell::new(MaybeUninit::uninit()), jmp_buf: Cell::new(ptr::null()), - reset_guard_page: Cell::new(false), prev: Cell::new(ptr::null()), - trap_handler, - handling_trap: Cell::new(false), } } - fn with(self, closure: impl FnOnce(&CallThreadState) -> i32) -> Result<(), Trap> { + fn with(self, closure: impl FnOnce(&Self) -> i32) -> Result<(), Trap> { let ret = tls::set(&self, || closure(&self))?; if ret != 0 { return Ok(()); @@ -685,86 +272,6 @@ impl<'a> CallThreadState<'a> { wasmer_unwind(self.jmp_buf.get()); } } - - /// Trap handler using our thread-local state. - /// - /// * `pc` - the program counter the trap happened at - /// * `reset_guard_page` - whether or not to reset the guard page, - /// currently Windows specific - /// * `call_handler` - a closure used to invoke the platform-specific - /// signal handler for each instance, if available. - /// - /// Attempts to handle the trap if it's a wasm trap. Returns a few - /// different things: - /// - /// * null - the trap didn't look like a wasm trap and should continue as a - /// trap - /// * 1 as a pointer - the trap was handled by a custom trap handler on an - /// instance, and the trap handler should quickly return. - /// * a different pointer - a jmp_buf buffer to longjmp to, meaning that - /// the wasm trap was succesfully handled. - fn handle_trap( - &self, - pc: *const u8, - reset_guard_page: bool, - signal_trap: Option, - call_handler: impl Fn(&TrapHandlerFn) -> bool, - ) -> *const u8 { - // If we hit a fault while handling a previous trap, that's quite bad, - // so bail out and let the system handle this recursive segfault. - // - // Otherwise flag ourselves as handling a trap, do the trap handling, - // and reset our trap handling flag. - if self.handling_trap.replace(true) { - return ptr::null(); - } - - // First up see if we have a custom trap handler, - // in which case run it. If anything handles the trap then we - // return that the trap was handled. - if self.trap_handler.custom_trap_handler(&call_handler) { - return 1 as *const _; - } - - // If this fault wasn't in wasm code, then it's not our problem - // except if it's a StackOverflow (see below) - if unsafe { !IS_WASM_PC(pc as _) } && signal_trap != Some(TrapCode::StackOverflow) { - return ptr::null(); - } - - // TODO: stack overflow can happen at any random time (i.e. in malloc() - // in memory.grow) and it's really hard to determine if the cause was - // stack overflow and if it happened in WebAssembly module. - // - // So, let's assume that any untrusted code called from WebAssembly - // doesn't trap. Then, if we have called some WebAssembly code, it - // means the trap is stack overflow. - if self.jmp_buf.get().is_null() { - self.handling_trap.set(false); - return ptr::null(); - } - let backtrace = Backtrace::new_unresolved(); - self.reset_guard_page.set(reset_guard_page); - unsafe { - (*self.unwind.get()) - .as_mut_ptr() - .write(UnwindReason::WasmTrap { - backtrace, - signal_trap, - pc: pc as usize, - }); - } - self.handling_trap.set(false); - self.jmp_buf.get() - } -} - -impl<'a> Drop for CallThreadState<'a> { - fn drop(&mut self) { - if self.reset_guard_page.get() { - reset_guard_page(); - } - } } // A private inner module for managing the TLS state that we require across @@ -797,14 +304,14 @@ mod tls { use std::cell::Cell; use std::ptr; - pub type Ptr = *const CallThreadState<'static>; + pub type Ptr = *const CallThreadState; // The first entry here is the `Ptr` which is what's used as part of the // public interface of this module. The second entry is a boolean which // allows the runtime to perform per-thread initialization if necessary // for handling traps (e.g. setting up ports on macOS and sigaltstack on // Unix). - thread_local!(static PTR: Cell<(Ptr, bool)> = Cell::new((ptr::null(), false))); + thread_local!(static PTR: Cell = Cell::new(ptr::null())); #[inline(never)] // see module docs for why this is here pub fn replace(val: Ptr) -> Result { @@ -812,19 +319,15 @@ mod tls { // When a new value is configured that means that we may be // entering WebAssembly so check to see if this thread has // performed per-thread initialization for traps. - let (prev, mut initialized) = p.get(); - if !initialized { - super::super::lazy_per_thread_init()?; - initialized = true; - } - p.set((val, initialized)); + let prev = p.get(); + p.set(val); Ok(prev) }) } #[inline(never)] // see module docs for why this is here pub fn get() -> Ptr { - PTR.with(|p| p.get().0) + PTR.with(|p| p.get()) } } @@ -840,7 +343,7 @@ mod tls { /// /// This is not a safe operation since it's intended to only be used /// with stack switching found with fibers and async wasmer. - pub unsafe fn take() -> Result { + pub unsafe fn take() -> Result { // Our tls pointer must be set at this time, and it must not be // null. We need to restore the previous pointer since we're // removing ourselves from the call-stack, and in the process we @@ -850,7 +353,7 @@ mod tls { assert!(!raw.is_null()); let prev = (*raw).prev.replace(ptr::null()); raw::replace(prev)?; - Ok(TlsRestore(raw)) + Ok(Self(raw)) } /// Restores a previous tls state back into this thread's TLS. @@ -873,10 +376,10 @@ mod tls { /// Configures thread local state such that for the duration of the /// execution of `closure` any call to `with` will yield `ptr`, unless this /// is recursively called again. - pub fn set(state: &CallThreadState<'_>, closure: impl FnOnce() -> R) -> Result { - struct Reset<'a, 'b>(&'a CallThreadState<'b>); + pub fn set(state: &CallThreadState, closure: impl FnOnce() -> R) -> Result { + struct Reset<'a>(&'a CallThreadState); - impl Drop for Reset<'_, '_> { + impl Drop for Reset<'_> { #[inline] fn drop(&mut self) { raw::replace(self.0.prev.replace(ptr::null())) @@ -887,7 +390,7 @@ mod tls { // Note that this extension of the lifetime to `'static` should be // safe because we only ever access it below with an anonymous // lifetime, meaning `'static` never leaks out of this module. - let ptr = unsafe { mem::transmute::<*const CallThreadState<'_>, _>(state) }; + let ptr = unsafe { mem::transmute::<*const CallThreadState, _>(state) }; let prev = raw::replace(ptr)?; state.prev.set(prev); let _reset = Reset(state); @@ -896,126 +399,12 @@ mod tls { /// Returns the last pointer configured with `set` above. Panics if `set` /// has not been previously called and not returned. - pub fn with(closure: impl FnOnce(Option<&CallThreadState<'_>>) -> R) -> R { + pub fn with(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R { let p = raw::get(); unsafe { closure(if p.is_null() { None } else { Some(&*p) }) } } } -/// Per-thread initialization, unneeded on Windows. -#[cfg(any(not(unix), feature = "avoid-tls-signals"))] -pub fn lazy_per_thread_init() -> Result<(), Trap> { - // Unused on Windows - Ok(()) -} - -/// A module for registering a custom alternate signal stack (sigaltstack). -/// -/// Rust's libstd installs an alternate stack with size `SIGSTKSZ`, which is not -/// always large enough for our signal handling code. Override it by creating -/// and registering our own alternate stack that is large enough and has a guard -/// page. -#[cfg(all(unix, not(feature = "avoid-tls-signals")))] -pub fn lazy_per_thread_init() -> Result<(), Trap> { - use std::cell::RefCell; - use std::ptr::null_mut; - - thread_local! { - /// Thread-local state is lazy-initialized on the first time it's used, - /// and dropped when the thread exits. - static TLS: RefCell = RefCell::new(Tls::None); - } - - /// The size of the sigaltstack (not including the guard, which will be - /// added). Make this large enough to run our signal handlers. - const MIN_STACK_SIZE: usize = 16 * 4096; - - enum Tls { - None, - Allocated { - mmap_ptr: *mut libc::c_void, - mmap_size: usize, - }, - BigEnough, - } - - return TLS.with(|slot| unsafe { - let mut slot = slot.borrow_mut(); - match *slot { - Tls::None => {} - // already checked - _ => return Ok(()), - } - // Check to see if the existing sigaltstack, if it exists, is big - // enough. If so we don't need to allocate our own. - let mut old_stack = mem::zeroed(); - let r = libc::sigaltstack(ptr::null(), &mut old_stack); - assert_eq!(r, 0, "learning about sigaltstack failed"); - if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE { - *slot = Tls::BigEnough; - return Ok(()); - } - - // ... but failing that we need to allocate our own, so do all that - // here. - let page_size: usize = region::page::size(); - let guard_size = page_size; - let alloc_size = guard_size + MIN_STACK_SIZE; - - let ptr = libc::mmap( - null_mut(), - alloc_size, - libc::PROT_NONE, - libc::MAP_PRIVATE | libc::MAP_ANON, - -1, - 0, - ); - if ptr == libc::MAP_FAILED { - return Err(Trap::oom()); - } - - // Prepare the stack with readable/writable memory and then register it - // with `sigaltstack`. - let stack_ptr = (ptr as usize + guard_size) as *mut libc::c_void; - let r = libc::mprotect( - stack_ptr, - MIN_STACK_SIZE, - libc::PROT_READ | libc::PROT_WRITE, - ); - assert_eq!(r, 0, "mprotect to configure memory for sigaltstack failed"); - let new_stack = libc::stack_t { - ss_sp: stack_ptr, - ss_flags: 0, - ss_size: MIN_STACK_SIZE, - }; - let r = libc::sigaltstack(&new_stack, ptr::null_mut()); - assert_eq!(r, 0, "registering new sigaltstack failed"); - - *slot = Tls::Allocated { - mmap_ptr: ptr, - mmap_size: alloc_size, - }; - Ok(()) - }); - - impl Drop for Tls { - fn drop(&mut self) { - let (ptr, size) = match self { - Self::Allocated { - mmap_ptr, - mmap_size, - } => (*mmap_ptr, *mmap_size), - _ => return, - }; - unsafe { - // Deallocate the stack memory. - let r = libc::munmap(ptr, size); - debug_assert_eq!(r, 0, "munmap failed during thread shutdown"); - } - } - } -} - extern "C" fn signal_less_trap_handler(pc: *const u8, trap: TrapCode) { let jmp_buf = tls::with(|info| { let backtrace = Backtrace::new_unresolved(); diff --git a/lib/vm/src/tunables.rs b/lib/vm/src/tunables.rs new file mode 100644 index 0000000000..c049b770c5 --- /dev/null +++ b/lib/vm/src/tunables.rs @@ -0,0 +1,53 @@ +use crate::MemoryError; +use crate::{Memory, Table}; +use crate::{MemoryStyle, TableStyle}; +use crate::{VMMemoryDefinition, VMTableDefinition}; +use std::ptr::NonNull; +use std::sync::Arc; +use wasmer_types::{MemoryType, TableType}; + +/// An engine delegates the creation of memories, tables, and globals +/// to a foreign implementor of this trait. +pub trait Tunables { + /// Construct a `MemoryStyle` for the provided `MemoryType` + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle; + + /// Construct a `TableStyle` for the provided `TableType` + fn table_style(&self, table: &TableType) -> TableStyle; + + /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`]. + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result, MemoryError>; + + /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid location in VM memory. + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result, MemoryError>; + + /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. + fn create_host_table( + &self, + ty: &TableType, + style: &TableStyle, + ) -> Result, String>; + + /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid location in VM memory. + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result, String>; +} diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index eae9d02d61..5eaa15ee82 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -8,14 +8,13 @@ use crate::func_data_registry::VMFuncRef; use crate::global::Global; use crate::instance::Instance; use crate::memory::Memory; +use crate::sig_registry::VMSharedSignatureIndex; use crate::table::Table; use crate::trap::{Trap, TrapCode}; use crate::VMExternRef; -use loupe::{MemoryUsage, MemoryUsageTracker, POINTER_BYTE_SIZE}; use std::any::Any; use std::convert::TryFrom; use std::fmt; -use std::mem; use std::ptr::{self, NonNull}; use std::sync::Arc; use std::u32; @@ -61,18 +60,30 @@ impl std::hash::Hash for VMFunctionEnvironment { } } -impl MemoryUsage for VMFunctionEnvironment { - fn size_of_val(&self, _: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - } +/// Represents a continuous region of executable memory starting with a function +/// entry point. +#[derive(Debug)] +#[repr(C)] +pub struct FunctionExtent { + /// Entry point for normal entry of the function. All addresses in the + /// function lie after this address. + pub address: FunctionBodyPtr, + /// Length in bytes. + pub length: usize, } /// An imported function. -#[derive(Debug, Copy, Clone, MemoryUsage)] +#[derive(Debug, Copy, Clone)] #[repr(C)] pub struct VMFunctionImport { /// A pointer to the imported function body. - pub body: *const VMFunctionBody, + pub body: FunctionBodyPtr, + + /// Function signature index within the source module. + pub signature: VMSharedSignatureIndex, + + /// Function call trampoline + pub trampoline: Option, /// A pointer to the `VMContext` that owns the function or host env data. pub environment: VMFunctionEnvironment, @@ -89,7 +100,7 @@ mod test_vmfunction_import { #[test] fn check_vmfunction_import_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmfunction_import()) @@ -105,6 +116,23 @@ mod test_vmfunction_import { } } +/// A locally defined function. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMLocalFunction { + /// A pointer to the imported function body. + pub body: FunctionBodyPtr, + + /// Length of the function code + pub length: u32, + + /// Function signature + pub signature: VMSharedSignatureIndex, + + /// Trampoline for host->VM function calls. + pub trampoline: VMTrampoline, +} + /// The `VMDynamicFunctionContext` is the context that dynamic /// functions will receive when called (rather than `vmctx`). /// A dynamic function is a function for which we don't know the signature @@ -152,7 +180,7 @@ mod test_vmdynamicfunction_import_context { #[test] fn check_vmdynamicfunction_import_context_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::>(), usize::from(offsets.size_of_vmdynamicfunction_import_context()) @@ -186,8 +214,29 @@ mod test_vmfunction_body { } } +/// A pointer to the beginning of the function body. +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct FunctionBodyPtr(pub *const VMFunctionBody); + +impl std::ops::Deref for FunctionBodyPtr { + type Target = *const VMFunctionBody; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +// SAFETY: The VMFunctionBody that this points to is opaque, so there's no data to read or write +// through this pointer. This is essentially a usize. +unsafe impl Send for FunctionBodyPtr {} + +/// SAFETY: The VMFunctionBody that this points to is opaque, so there's no data to read or write +/// through this pointer. This is essentially a usize. +unsafe impl Sync for FunctionBodyPtr {} + /// A function kind is a calling convention into and out of wasm code. -#[derive(Debug, Copy, Clone, PartialEq, MemoryUsage)] +#[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] pub enum VMFunctionKind { /// A static function has the native signature: @@ -208,7 +257,7 @@ pub enum VMFunctionKind { /// The fields compiled code needs to access to utilize a WebAssembly table /// imported from another instance. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] #[repr(C)] pub struct VMTableImport { /// A pointer to the imported table description. @@ -229,7 +278,7 @@ mod test_vmtable_import { #[test] fn check_vmtable_import_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmtable_import()) @@ -247,7 +296,7 @@ mod test_vmtable_import { /// The fields compiled code needs to access to utilize a WebAssembly linear /// memory imported from another instance. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] #[repr(C)] pub struct VMMemoryImport { /// A pointer to the imported memory description. @@ -268,7 +317,7 @@ mod test_vmmemory_import { #[test] fn check_vmmemory_import_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmmemory_import()) @@ -286,7 +335,7 @@ mod test_vmmemory_import { /// The fields compiled code needs to access to utilize a WebAssembly global /// variable imported from another instance. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] #[repr(C)] pub struct VMGlobalImport { /// A pointer to the imported global variable description. @@ -319,7 +368,7 @@ mod test_vmglobal_import { #[test] fn check_vmglobal_import_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmglobal_import()) @@ -359,16 +408,6 @@ unsafe impl Send for VMMemoryDefinition {} /// correctness in a multi-threaded context is concerned. unsafe impl Sync for VMMemoryDefinition {} -impl MemoryUsage for VMMemoryDefinition { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - if tracker.track(self.base as *const _ as *const ()) { - POINTER_BYTE_SIZE * self.current_length - } else { - 0 - } - } -} - impl VMMemoryDefinition { /// Do an unsynchronized, non-atomic `memory.copy` for the memory. /// @@ -378,6 +417,7 @@ impl VMMemoryDefinition { /// bounds. /// /// # Safety + /// /// The memory is not copied atomically and is not synchronized: it's the /// caller's responsibility to synchronize. pub(crate) unsafe fn memory_copy(&self, dst: u32, src: u32, len: u32) -> Result<(), Trap> { @@ -445,7 +485,7 @@ mod test_vmmemory_definition { #[test] fn check_vmmemory_definition_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmmemory_definition()) @@ -473,16 +513,6 @@ pub struct VMTableDefinition { pub current_elements: u32, } -impl MemoryUsage for VMTableDefinition { - fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize { - if tracker.track(self.base as *const _ as *const ()) { - POINTER_BYTE_SIZE * (self.current_elements as usize) - } else { - 0 - } - } -} - #[cfg(test)] mod test_vmtable_definition { use super::VMTableDefinition; @@ -494,7 +524,7 @@ mod test_vmtable_definition { #[test] fn check_vmtable_definition_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmtable_definition()) @@ -540,17 +570,11 @@ impl fmt::Debug for VMGlobalDefinitionStorage { } } -impl MemoryUsage for VMGlobalDefinitionStorage { - fn size_of_val(&self, _: &mut dyn MemoryUsageTracker) -> usize { - mem::size_of_val(self) - } -} - /// The storage for a WebAssembly global defined within the instance. /// /// TODO: Pack the globals more densely, rather than using the same size /// for every type. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone)] #[repr(C, align(16))] pub struct VMGlobalDefinition { storage: VMGlobalDefinitionStorage, @@ -578,7 +602,7 @@ mod test_vmglobal_definition { #[test] fn check_vmglobal_definition_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::<*const VMGlobalDefinition>(), usize::from(offsets.size_of_vmglobal_local()) @@ -588,7 +612,7 @@ mod test_vmglobal_definition { #[test] fn check_vmglobal_begins_aligned() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!(offsets.vmctx_globals_begin() % 16, 0); } } @@ -789,12 +813,6 @@ impl VMGlobalDefinition { } } -/// An index into the shared signature registry, usable for checking signatures -/// at indirect calls. -#[repr(C)] -#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, MemoryUsage)] -pub struct VMSharedSignatureIndex(u32); - #[cfg(test)] mod test_vmshared_signature_index { use super::VMSharedSignatureIndex; @@ -805,7 +823,7 @@ mod test_vmshared_signature_index { #[test] fn check_vmshared_signature_index() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmshared_signature_index()) @@ -821,23 +839,10 @@ mod test_vmshared_signature_index { } } -impl VMSharedSignatureIndex { - /// Create a new `VMSharedSignatureIndex`. - pub fn new(value: u32) -> Self { - Self(value) - } -} - -impl Default for VMSharedSignatureIndex { - fn default() -> Self { - Self::new(u32::MAX) - } -} - /// The VM caller-checked "anyfunc" record, for caller-side signature checking. /// It consists of the actual function pointer and a signature id to be checked /// by the caller. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, MemoryUsage)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[repr(C)] pub struct VMCallerCheckedAnyfunc { /// Function body. @@ -860,7 +865,7 @@ mod test_vmcaller_checked_anyfunc { #[test] fn check_vmcaller_checked_anyfunc_offsets() { let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); assert_eq!( size_of::(), usize::from(offsets.size_of_vmcaller_checked_anyfunc()) @@ -880,18 +885,6 @@ mod test_vmcaller_checked_anyfunc { } } -impl Default for VMCallerCheckedAnyfunc { - fn default() -> Self { - Self { - func_ptr: ptr::null_mut(), - type_index: Default::default(), - vmctx: VMFunctionEnvironment { - vmctx: ptr::null_mut(), - }, - } - } -} - /// An index type for builtin functions. #[derive(Copy, Clone, Debug)] pub struct VMBuiltinFunctionIndex(u32); @@ -1135,3 +1128,16 @@ pub type VMTrampoline = unsafe extern "C" fn( *const VMFunctionBody, // function we're actually calling *mut u128, // space for arguments and return values ); + +/// Pointers to section data. +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct SectionBodyPtr(pub *const u8); + +impl std::ops::Deref for SectionBodyPtr { + type Target = *const u8; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/lib/vm/src/vmoffsets.rs b/lib/vm/src/vmoffsets.rs index 110140c05c..74ca922313 100644 --- a/lib/vm/src/vmoffsets.rs +++ b/lib/vm/src/vmoffsets.rs @@ -7,7 +7,6 @@ #![deny(rustdoc::broken_intra_doc_links)] use crate::VMBuiltinFunctionIndex; -use loupe::MemoryUsage; use more_asserts::assert_lt; use std::convert::TryFrom; use wasmer_types::{ @@ -33,7 +32,7 @@ const fn align(offset: u32, width: u32) -> u32 { /// related structs that JIT code accesses directly. /// /// [`VMContext`]: crate::vmcontext::VMContext -#[derive(Clone, Debug, MemoryUsage)] +#[derive(Clone, Debug)] pub struct VMOffsets { /// The size in bytes of a pointer on the target. pub pointer_size: u8, @@ -59,26 +58,10 @@ pub struct VMOffsets { impl VMOffsets { /// Return a new `VMOffsets` instance, for a given pointer size. - pub fn new(pointer_size: u8, module: &ModuleInfo) -> Self { - Self { - pointer_size, - num_signature_ids: cast_to_u32(module.signatures.len()), - num_imported_functions: cast_to_u32(module.num_imported_functions), - num_imported_tables: cast_to_u32(module.num_imported_tables), - num_imported_memories: cast_to_u32(module.num_imported_memories), - num_imported_globals: cast_to_u32(module.num_imported_globals), - num_local_tables: cast_to_u32(module.tables.len()), - num_local_memories: cast_to_u32(module.memories.len()), - num_local_globals: cast_to_u32(module.globals.len()), - has_trap_handlers: true, - } - } - - /// Return a new `VMOffsets` instance, for a given pointer size - /// skipping the `ModuleInfo`. /// - /// Note: This should only when generating code for trampolines. - pub fn new_for_trampolines(pointer_size: u8) -> Self { + /// The returned `VMOffsets` has no entities. Add entities with other builder methods for this + /// type. + pub fn new(pointer_size: u8) -> Self { Self { pointer_size, num_signature_ids: 0, @@ -92,6 +75,44 @@ impl VMOffsets { has_trap_handlers: false, } } + + /// Return a new `VMOffsets` instance, for a host's pointer size. + /// + /// The returned `VMOffsets` has no entities. Add entities with other builder methods for this + /// type. + pub fn for_host() -> Self { + Self::new(std::mem::size_of::<*const u8>() as u8) + } + + /// Add imports and locals from the provided ModuleInfo. + pub fn with_module_info(mut self, module: &ModuleInfo) -> Self { + self.num_imported_functions = module.import_counts.functions; + self.num_imported_tables = module.import_counts.tables; + self.num_imported_memories = module.import_counts.memories; + self.num_imported_globals = module.import_counts.globals; + self.num_signature_ids = cast_to_u32(module.signatures.len()); + // FIXME = these should most likely be subtracting the corresponding imports!!? + self.num_local_tables = cast_to_u32(module.tables.len()); + self.num_local_memories = cast_to_u32(module.memories.len()); + self.num_local_globals = cast_to_u32(module.globals.len()); + self.has_trap_handlers = true; + self + } + + /// Add imports and locals from the provided ModuleInfo. + pub fn with_archived_module_info(mut self, module: &rkyv::Archived) -> Self { + self.num_imported_functions = module.import_counts.functions; + self.num_imported_tables = module.import_counts.tables; + self.num_imported_memories = module.import_counts.memories; + self.num_imported_globals = module.import_counts.globals; + self.num_signature_ids = cast_to_u32(module.signatures.len()); + // FIXME = these should most likely be subtracting the corresponding imports!!? + self.num_local_tables = cast_to_u32(module.tables.len()); + self.num_local_memories = cast_to_u32(module.memories.len()); + self.num_local_globals = cast_to_u32(module.globals.len()); + self.has_trap_handlers = true; + self + } } /// Offsets for [`VMFunctionImport`]. @@ -107,14 +128,14 @@ impl VMOffsets { /// The offset of the `vmctx` field. #[allow(clippy::identity_op)] pub const fn vmfunction_import_vmctx(&self) -> u8 { - 1 * self.pointer_size + 3 * self.pointer_size } /// Return the size of [`VMFunctionImport`]. /// /// [`VMFunctionImport`]: crate::vmcontext::VMFunctionImport pub const fn size_of_vmfunction_import(&self) -> u8 { - 2 * self.pointer_size + 4 * self.pointer_size } } @@ -352,7 +373,7 @@ impl VMOffsets { /// /// [`VMFuncRef`]: crate::func_data_registry::VMFuncRef pub const fn size_of_vm_funcref(&self) -> u8 { - 1 * self.pointer_size + self.pointer_size } } diff --git a/tests/compilers/compilation.rs b/tests/compilers/compilation.rs index c38a21ea9e..8e3e52e588 100644 --- a/tests/compilers/compilation.rs +++ b/tests/compilers/compilation.rs @@ -1,9 +1,7 @@ -use std::sync::Arc; use wasmer::*; -use wasmer_engine::Engine; +use wasmer_engine::{Engine, Executable}; use wasmer_engine_universal::Universal; -use wasmer_types::Type::I64; -use wasmer_types::{InstanceConfig, NamedFunction}; +use wasmer_vm::Artifact; fn slow_to_compile_contract(n_fns: usize, n_locals: usize) -> Vec { let fns = format!("(func (local {}))\n", "i32 ".repeat(n_locals)).repeat(n_fns); @@ -16,7 +14,7 @@ fn compile_uncached<'a>( engine: &'a dyn Engine, code: &'a [u8], time: bool, -) -> Result, CompileError> { +) -> Result, CompileError> { use std::time::Instant; let now = Instant::now(); engine.validate(code)?; @@ -42,7 +40,7 @@ fn compilation_test() { Ok(art) => { let serialized = art.serialize().unwrap(); println!( - "{}: artefact is compiled, size is {}", + "{}: artifact is compiled, size is {}", factor, serialized.len() ); @@ -71,6 +69,8 @@ fn write_perf_profiler_map(functions: &Vec) -> Result<(), Box unsafe { let serialized = art.serialize().unwrap(); - let module = wasmer::Module::deserialize(&store, serialized.as_slice()).unwrap(); - let instance = - Instance::new_with_config(&module, InstanceConfig::default(), &imports! {}); - assert!(instance.is_ok()); - let instance = instance.unwrap(); - let named = instance.named_functions(); - assert_eq!(3, named.len()); - assert_eq!("f1", named[0].name); - assert_eq!("f2", named[1].name); - assert_eq!("f3", named[2].name); + let executable = + wasmer_engine_universal::UniversalExecutableRef::deserialize(&serialized).unwrap(); + let artifact = engine.load_universal_executable_ref(&executable).unwrap(); + let info = artifact + .functions() + .iter() + .filter_map(|(idx, _)| { + let extent = artifact.function_extent(idx)?; + let idx = artifact.import_counts().function_index(idx); + let name = executable.function_name(idx)?; + Some((name, extent)) + }) + .collect::>(); + assert_eq!(4, info.len()); + assert_eq!("f0", info[0].0); + assert_eq!("f1", info[1].0); + assert_eq!("f2", info[2].0); + assert_eq!("f3", info[3].0); }, Err(_) => { assert!(false) diff --git a/tests/compilers/deterministic.rs b/tests/compilers/deterministic.rs index 10096843d5..70adc89521 100644 --- a/tests/compilers/deterministic.rs +++ b/tests/compilers/deterministic.rs @@ -1,21 +1,22 @@ use anyhow::Result; -use wasmer::{wat2wasm, Module, Store}; +use wasmer::{wat2wasm, BaseTunables, Engine}; use wasmer_compiler_singlepass::Singlepass; use wasmer_engine_universal::Universal; fn compile_and_compare(wasm: &[u8]) -> Result<()> { let compiler = Singlepass::default(); - let store = Store::new(&Universal::new(compiler).engine()); + let engine = Universal::new(compiler).engine(); + let tunables = BaseTunables::for_target(engine.target()); // compile for first time - let module = Module::new(&store, wasm)?; - let first = module.serialize()?; + let executable = engine.compile(wasm, &tunables).unwrap(); + let serialized1 = executable.serialize().unwrap(); // compile for second time - let module = Module::new(&store, wasm)?; - let second = module.serialize()?; + let executable = engine.compile(wasm, &tunables).unwrap(); + let serialized2 = executable.serialize().unwrap(); - assert!(first == second); + assert_eq!(serialized1, serialized2); Ok(()) } diff --git a/tests/compilers/fast_gas_metering.rs b/tests/compilers/fast_gas_metering.rs index 2fb9d5f7dd..5f75ce94e2 100644 --- a/tests/compilers/fast_gas_metering.rs +++ b/tests/compilers/fast_gas_metering.rs @@ -166,16 +166,13 @@ fn test_gas_intrinsic_regular() { assert!(instance.is_ok()); let instance = instance.unwrap(); let foo_func = instance - .exports - .get_function("foo") + .lookup_function("foo") .expect("expected function foo"); let bar_func = instance - .exports - .get_function("bar") + .lookup_function("bar") .expect("expected function bar"); let zoo_func = instance - .exports - .get_function("zoo") + .lookup_function("zoo") .expect("expected function zoo"); // Ensure "func" was not called. assert_eq!(HITS.load(SeqCst), 0); @@ -223,12 +220,10 @@ fn test_gas_intrinsic_default() { assert!(instance.is_ok()); let instance = instance.unwrap(); let foo_func = instance - .exports - .get_function("foo") + .lookup_function("foo") .expect("expected function foo"); let bar_func = instance - .exports - .get_function("bar") + .lookup_function("bar") .expect("expected function bar"); // Ensure "func" was called. assert_eq!(HITS.load(SeqCst), 0); @@ -274,8 +269,7 @@ fn test_gas_intrinsic_tricky() { assert!(instance.is_ok()); let instance = instance.unwrap(); let foo_func = instance - .exports - .get_function("foo") + .lookup_function("foo") .expect("expected function foo"); let _e = foo_func.call(&[]); @@ -285,8 +279,7 @@ fn test_gas_intrinsic_tricky() { assert_eq!(HITS.load(SeqCst), 1); let zoo_func = instance - .exports - .get_function("zoo") + .lookup_function("zoo") .expect("expected function zoo"); let _e = zoo_func.call(&[]); diff --git a/tests/compilers/imports.rs b/tests/compilers/imports.rs index c66286b827..6c65f0e936 100644 --- a/tests/compilers/imports.rs +++ b/tests/compilers/imports.rs @@ -323,112 +323,122 @@ fn dynamic_function_with_env_wasmer_env_init_works(config: crate::Config) -> Res #[allow(dead_code)] #[derive(WasmerEnv, Clone)] struct Env { - #[wasmer(export)] - memory: LazyInit, + memory: Memory, } - let env: Env = Env { - memory: LazyInit::default(), + memory: Memory::new( + &store, + MemoryType { + minimum: 0.into(), + maximum: None, + shared: false, + }, + )?, }; + let function_fn = Function::new_with_env( + &store, + FunctionType::new(vec![], vec![]), + env.clone(), + |env, _values| Ok(vec![]), + ); let instance = Instance::new( &module, &imports! { "host" => { - "fn" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { - assert!(env.memory_ref().is_some()); - Ok(vec![]) - }), + "fn" => function_fn, }, }, )?; - let f: NativeFunc<(), ()> = instance.exports.get_native_function("main")?; + let f: NativeFunc<(), ()> = instance.get_native_function("main")?; f.call()?; Ok(()) } -#[compiler_test(imports)] -fn multi_use_host_fn_manages_memory_correctly(config: crate::Config) -> Result<()> { - let store = config.store(); - let module = get_module2(&store)?; - - #[allow(dead_code)] - #[derive(Clone)] - struct Env { - memory: LazyInit, - } - - impl WasmerEnv for Env { - fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { - let memory = instance.exports.get_memory("memory")?.clone(); - self.memory.initialize(memory); - Ok(()) - } - } - - let env: Env = Env { - memory: LazyInit::default(), - }; - fn host_fn(env: &Env) { - assert!(env.memory.get_ref().is_some()); - println!("Hello, world!"); - } - - let imports = imports! { - "host" => { - "fn" => Function::new_native_with_env(&store, env.clone(), host_fn), - }, - }; - let instance1 = Instance::new(&module, &imports)?; - let instance2 = Instance::new(&module, &imports)?; - { - let f1: NativeFunc<(), ()> = instance1.exports.get_native_function("main")?; - f1.call()?; - } - drop(instance1); - { - let f2: NativeFunc<(), ()> = instance2.exports.get_native_function("main")?; - f2.call()?; - } - drop(instance2); - Ok(()) -} - -#[compiler_test(imports)] -fn instance_local_memory_lifetime(config: crate::Config) -> Result<()> { - let store = config.store(); - - let memory: Memory = { - let wat = r#"(module - (memory $mem 1) - (export "memory" (memory $mem)) -)"#; - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &imports! {})?; - instance.exports.get_memory("memory")?.clone() - }; - - let wat = r#"(module - (import "env" "memory" (memory $mem 1) ) - (func $get_at (type $get_at_t) (param $idx i32) (result i32) - (i32.load (local.get $idx))) - (type $get_at_t (func (param i32) (result i32))) - (type $set_at_t (func (param i32) (param i32))) - (func $set_at (type $set_at_t) (param $idx i32) (param $val i32) - (i32.store (local.get $idx) (local.get $val))) - (export "get_at" (func $get_at)) - (export "set_at" (func $set_at)) -)"#; - let module = Module::new(&store, wat)?; - let imports = imports! { - "env" => { - "memory" => memory, - }, - }; - let instance = Instance::new(&module, &imports)?; - let set_at: NativeFunc<(i32, i32), ()> = instance.exports.get_native_function("set_at")?; - let get_at: NativeFunc = instance.exports.get_native_function("get_at")?; - set_at.call(200, 123)?; - assert_eq!(get_at.call(200)?, 123); - - Ok(()) -} +// TODO(0-copy): no longer possible to get references to exported entities other than functions +// (we don't need that functionality) +// #[compiler_test(imports)] +// fn multi_use_host_fn_manages_memory_correctly(config: crate::Config) -> Result<()> { +// let store = config.store(); +// let module = get_module2(&store)?; +// +// #[allow(dead_code)] +// #[derive(Clone)] +// struct Env { +// memory: LazyInit, +// } +// +// impl WasmerEnv for Env { +// fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { +// let memory = instance.exports.get_memory("memory")?.clone(); +// self.memory.initialize(memory); +// Ok(()) +// } +// } +// +// let env: Env = Env { +// memory: LazyInit::default(), +// }; +// fn host_fn(env: &Env) { +// assert!(env.memory.get_ref().is_some()); +// println!("Hello, world!"); +// } +// +// let imports = imports! { +// "host" => { +// "fn" => Function::new_native_with_env(&store, env.clone(), host_fn), +// }, +// }; +// let instance1 = Instance::new(&module, &imports)?; +// let instance2 = Instance::new(&module, &imports)?; +// { +// let f1: NativeFunc<(), ()> = instance1.get_native_function("main")?; +// f1.call()?; +// } +// drop(instance1); +// { +// let f2: NativeFunc<(), ()> = instance2.get_native_function("main")?; +// f2.call()?; +// } +// drop(instance2); +// Ok(()) +// } +// +// #[compiler_test(imports)] +// fn instance_local_memory_lifetime(config: crate::Config) -> Result<()> { +// let store = config.store(); +// +// let memory: Memory = { +// let wat = r#"(module +// (memory $mem 1) +// (export "memory" (memory $mem)) +// )"#; +// let module = Module::new(&store, wat)?; +// let instance = Instance::new(&module, &imports! {})?; +// instance.exports.get_memory("memory")?.clone() +// }; +// +// let wat = r#"(module +// (import "env" "memory" (memory $mem 1) ) +// (func $get_at (type $get_at_t) (param $idx i32) (result i32) +// (i32.load (local.get $idx))) +// (type $get_at_t (func (param i32) (result i32))) +// (type $set_at_t (func (param i32) (param i32))) +// (func $set_at (type $set_at_t) (param $idx i32) (param $val i32) +// (i32.store (local.get $idx) (local.get $val))) +// (export "get_at" (func $get_at)) +// (export "set_at" (func $set_at)) +// )"#; +// let module = Module::new(&store, wat)?; +// let imports = imports! { +// "env" => { +// "memory" => memory, +// }, +// }; +// let instance = Instance::new(&module, &imports)?; +// let set_at: NativeFunc<(i32, i32), ()> = instance.get_native_function("set_at")?; +// let get_at: NativeFunc = instance.get_native_function("get_at")?; +// set_at.call(200, 123)?; +// assert_eq!(get_at.call(200)?, 123); +// +// Ok(()) +// } diff --git a/tests/compilers/issues.rs b/tests/compilers/issues.rs index 34c4f1b93a..a862c2b6c3 100644 --- a/tests/compilers/issues.rs +++ b/tests/compilers/issues.rs @@ -14,7 +14,6 @@ fn issue_2329(mut config: crate::Config) -> Result<()> { #[derive(Clone, Default, WasmerEnv)] pub struct Env { - #[wasmer(export)] memory: LazyInit, } @@ -27,7 +26,7 @@ fn issue_2329(mut config: crate::Config) -> Result<()> { } pub fn read_memory(env: &Env, guest_ptr: u32) -> u32 { - dbg!(env.memory_ref()); + dbg!(env.memory.get_ref()); dbg!(guest_ptr); 0 } @@ -73,7 +72,7 @@ fn issue_2329(mut config: crate::Config) -> Result<()> { } }; let instance = Instance::new(&module, &imports)?; - instance.exports.get_function("read_memory")?.call(&[])?; + instance.lookup_function("read_memory").unwrap().call(&[])?; Ok(()) } diff --git a/tests/compilers/middlewares.rs b/tests/compilers/middlewares.rs index cff4ce8f83..a253b45a35 100644 --- a/tests/compilers/middlewares.rs +++ b/tests/compilers/middlewares.rs @@ -1,11 +1,10 @@ use anyhow::Result; -use loupe::MemoryUsage; use std::sync::Arc; use wasmer::wasmparser::Operator; use wasmer::*; -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] struct Add2MulGen { value_off: i32, } @@ -47,7 +46,7 @@ impl FunctionMiddleware for Add2Mul { } } -#[derive(Debug, MemoryUsage)] +#[derive(Debug)] struct FusionGen; #[derive(Debug)] @@ -105,7 +104,7 @@ fn middleware_basic(mut config: crate::Config) -> Result<()> { let instance = Instance::new(&module, &import_object)?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("add")?; let result = f.call(4, 6)?; assert_eq!(result, 24); Ok(()) @@ -128,7 +127,7 @@ fn middleware_one_to_multi(mut config: crate::Config) -> Result<()> { let instance = Instance::new(&module, &import_object)?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("add")?; let result = f.call(4, 6)?; assert_eq!(result, 25); Ok(()) @@ -152,7 +151,7 @@ fn middleware_multi_to_one(mut config: crate::Config) -> Result<()> { let instance = Instance::new(&module, &import_object)?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("testfunc")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("testfunc")?; let result = f.call(10, 20)?; assert_eq!(result, 10); Ok(()) @@ -176,7 +175,7 @@ fn middleware_chain_order_1(mut config: crate::Config) -> Result<()> { let instance = Instance::new(&module, &import_object)?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("add")?; let result = f.call(4, 6)?; assert_eq!(result, 24); Ok(()) @@ -200,7 +199,7 @@ fn middleware_chain_order_2(mut config: crate::Config) -> Result<()> { let instance = Instance::new(&module, &import_object)?; - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("add")?; let result = f.call(4, 6)?; assert_eq!(result, 48); Ok(()) diff --git a/tests/compilers/native_functions.rs b/tests/compilers/native_functions.rs index 371d904c9a..06111f1f7d 100644 --- a/tests/compilers/native_functions.rs +++ b/tests/compilers/native_functions.rs @@ -55,19 +55,23 @@ fn native_function_works_for_wasm(config: crate::Config) -> anyhow::Result<()> { let instance = Instance::new(&module, &import_object)?; { - let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?; + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("add")?; let result = f.call(4, 6)?; assert_eq!(result, 10); } { - let f: &Function = instance.exports.get("double_then_add")?; + let f: Function = instance + .lookup_function("double_then_add") + .expect("lookup function"); let result = f.call(&[Val::I32(4), Val::I32(6)])?; assert_eq!(result[0], Val::I32(20)); } { - let dyn_f: &Function = instance.exports.get("double_then_add")?; + let dyn_f: Function = instance + .lookup_function("double_then_add") + .expect("lookup function"); let f: NativeFunc<(i32, i32), i32> = dyn_f.native().unwrap(); let result = f.call(4, 6)?; assert_eq!(result, 20); @@ -151,8 +155,7 @@ fn non_native_functions_and_closures_with_no_env_work(config: crate::Config) -> let instance = Instance::new(&module, &import_object)?; - let test: NativeFunc<(i32, i32, i32, i32, i32), i32> = - instance.exports.get_native_function("test")?; + let test: NativeFunc<(i32, i32, i32, i32, i32), i32> = instance.get_native_function("test")?; let result = test.call(2, 3, 4, 5, 6)?; let manually_computed_result = 6 * (5 * (4 * (3 * 2 * 20) * 10 * 20)) * 10; @@ -181,14 +184,14 @@ fn native_function_works_for_wasm_function_manyparams(config: crate::Config) -> let instance = Instance::new(&module, &import_object)?; { - let dyn_f: &Function = instance.exports.get("longf")?; + let dyn_f: Function = instance.lookup_function("longf").unwrap(); let f: NativeFunc<(), i64> = dyn_f.native().unwrap(); let result = f.call()?; assert_eq!(result, 1234567890); } { - let dyn_f: &Function = instance.exports.get("longf_pure")?; + let dyn_f: Function = instance.lookup_function("longf_pure").unwrap(); let f: NativeFunc<(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), i64> = dyn_f.native().unwrap(); let result = f.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?; @@ -221,14 +224,14 @@ fn native_function_works_for_wasm_function_manyparams_dynamic( let instance = Instance::new(&module, &import_object)?; { - let dyn_f: &Function = instance.exports.get("longf")?; + let dyn_f: Function = instance.lookup_function("longf").unwrap(); let f: NativeFunc<(), i64> = dyn_f.native().unwrap(); let result = f.call()?; assert_eq!(result, 1234567890); } { - let dyn_f: &Function = instance.exports.get("longf_pure")?; + let dyn_f: Function = instance.lookup_function("longf_pure").unwrap(); let f: NativeFunc<(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), i64> = dyn_f.native().unwrap(); let result = f.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?; diff --git a/tests/compilers/serialize.rs b/tests/compilers/serialize.rs index 9dfbdabca3..ccf7f542aa 100644 --- a/tests/compilers/serialize.rs +++ b/tests/compilers/serialize.rs @@ -4,73 +4,84 @@ use wasmer::*; #[compiler_test(serialize)] fn test_serialize(config: crate::Config) -> Result<()> { let store = config.store(); - let wat = r#" + let wasm = wat2wasm( + r#" (module (func $hello (import "" "hello")) (func (export "run") (call $hello)) ) - "#; - - let module = Module::new(&store, wat)?; - let serialized_bytes = module.serialize()?; - assert!(!serialized_bytes.is_empty()); + "# + .as_bytes(), + ) + .unwrap(); + let engine = store.engine(); + let tunables = BaseTunables::for_target(engine.target()); + let executable = engine.compile(&wasm, &tunables).unwrap(); + let serialized = executable.serialize().unwrap(); + assert!(!serialized.is_empty()); Ok(()) } -#[compiler_test(serialize)] -fn test_deserialize(config: crate::Config) -> Result<()> { - let store = config.store(); - let wat = r#" - (module $name - (import "host" "sum_part" (func (param i32 i64 i32 f32 f64) (result i64))) - (func (export "test_call") (result i64) - i32.const 100 - i64.const 200 - i32.const 300 - f32.const 400 - f64.const 500 - call 0 - ) - ) - "#; - - let module = Module::new(&store, wat)?; - let serialized_bytes = module.serialize()?; - - let headless_store = config.headless_store(); - let deserialized_module = unsafe { Module::deserialize(&headless_store, &serialized_bytes)? }; - assert_eq!(deserialized_module.name(), Some("name")); - assert_eq!( - deserialized_module.exports().collect::>(), - module.exports().collect::>() - ); - assert_eq!( - deserialized_module.imports().collect::>(), - module.imports().collect::>() - ); - - let func_type = FunctionType::new( - vec![Type::I32, Type::I64, Type::I32, Type::F32, Type::F64], - vec![Type::I64], - ); - let instance = Instance::new( - &module, - &imports! { - "host" => { - "sum_part" => Function::new(&store, &func_type, |params| { - let param_0: i64 = params[0].unwrap_i32() as i64; - let param_1: i64 = params[1].unwrap_i64() as i64; - let param_2: i64 = params[2].unwrap_i32() as i64; - let param_3: i64 = params[3].unwrap_f32() as i64; - let param_4: i64 = params[4].unwrap_f64() as i64; - Ok(vec![Value::I64(param_0 + param_1 + param_2 + param_3 + param_4)]) - }) - } - }, - )?; - - let test_call = instance.exports.get_function("test_call")?; - let result = test_call.call(&[])?; - assert_eq!(result.to_vec(), vec![Value::I64(1500)]); - Ok(()) -} +// #[compiler_test(serialize)] +// fn test_deserialize(config: crate::Config) -> Result<()> { +// let store = config.store(); +// let wasm = wat2wasm(r#" +// (module $name +// (import "host" "sum_part" (func (param i32 i64 i32 f32 f64) (result i64))) +// (func (export "test_call") (result i64) +// i32.const 100 +// i64.const 200 +// i32.const 300 +// f32.const 400 +// f64.const 500 +// call 0 +// ) +// ) +// "#.as_bytes()).unwrap(); +// +// let engine = store.engine(); +// let tunables = BaseTunables::for_target(engine.target()); +// let executable = engine.compile(&wasm, &tunables).unwrap(); +// let writer = std::io::Cursor::new(vec![]); +// executable.serialize(&mut writer).unwrap(); +// let serialized_bytes = writer.into_inner(); +// +// let headless_store = config.headless_store(); +// +// +// let deserialized_module = unsafe { Module::deserialize(&headless_store, &serialized_bytes)? }; +// assert_eq!(deserialized_module.name(), Some("name")); +// assert_eq!( +// deserialized_module.artifact().module_ref().exports().collect::>(), +// module.exports().collect::>() +// ); +// assert_eq!( +// deserialized_module.artifact().module_ref().imports().collect::>(), +// module.imports().collect::>() +// ); +// +// let func_type = FunctionType::new( +// vec![Type::I32, Type::I64, Type::I32, Type::F32, Type::F64], +// vec![Type::I64], +// ); +// let instance = Instance::new( +// &module, +// &imports! { +// "host" => { +// "sum_part" => Function::new(&store, &func_type, |params| { +// let param_0: i64 = params[0].unwrap_i32() as i64; +// let param_1: i64 = params[1].unwrap_i64() as i64; +// let param_2: i64 = params[2].unwrap_i32() as i64; +// let param_3: i64 = params[3].unwrap_f32() as i64; +// let param_4: i64 = params[4].unwrap_f64() as i64; +// Ok(vec![Value::I64(param_0 + param_1 + param_2 + param_3 + param_4)]) +// }) +// } +// }, +// )?; +// +// let test_call = instance.exports.get_function("test_call")?; +// let result = test_call.call(&[])?; +// assert_eq!(result.to_vec(), vec![Value::I64(1500)]); +// Ok(()) +// } diff --git a/tests/compilers/stack_limiter.rs b/tests/compilers/stack_limiter.rs index e6480f387f..5a32cb7aa5 100644 --- a/tests/compilers/stack_limiter.rs +++ b/tests/compilers/stack_limiter.rs @@ -41,8 +41,7 @@ fn stack_limit_hit() { assert!(instance.is_ok()); let instance = instance.unwrap(); let main_func = instance - .exports - .get_function("main") + .lookup_function("main") .expect("expected function main"); match main_func.call(&[]) { Err(err) => { @@ -88,8 +87,7 @@ fn stack_limit_operand_stack() { assert!(instance.is_ok()); let instance = instance.unwrap(); let main_func = instance - .exports - .get_function("main") + .lookup_function("main") .expect("expected function main"); match main_func.call(&[]) { Err(err) => { @@ -140,8 +138,7 @@ fn stack_limit_ok() { assert!(instance.is_ok()); let instance = instance.unwrap(); let main_func = instance - .exports - .get_function("main") + .lookup_function("main") .expect("expected function main"); let e = main_func.call(&[]); assert!(e.is_ok()); @@ -160,8 +157,7 @@ fn stack_limit_huge_limit() { assert!(instance.is_ok()); let instance = instance.unwrap(); let main_func = instance - .exports - .get_function("main") + .lookup_function("main") .expect("expected function main"); main_func.call(&[]).unwrap(); } @@ -187,8 +183,7 @@ fn stack_limit_no_args() { assert!(instance.is_ok()); let instance = instance.unwrap(); let main_func = instance - .exports - .get_function("main") + .lookup_function("main") .expect("expected function main"); match main_func.call(&[]) { Err(err) => { @@ -231,8 +226,7 @@ fn deep_but_sane() { assert!(instance.is_ok()); let instance = instance.unwrap(); let main_func = instance - .exports - .get_function("main") + .lookup_function("main") .expect("expected function main"); let e = main_func.call(&[]); diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index 493090c7e9..4031e29267 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -25,8 +25,7 @@ fn test_trap_return(config: crate::Config) -> Result<()> { }, )?; let run_func = instance - .exports - .get_function("run") + .lookup_function("run") .expect("expected function export"); let e = run_func.call(&[]).err().expect("error calling function"); @@ -50,8 +49,7 @@ fn test_trap_trace(config: crate::Config) -> Result<()> { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; let run_func = instance - .exports - .get_function("run") + .lookup_function("run") .expect("expected function export"); let e = run_func.call(&[]).err().expect("error calling function"); @@ -97,8 +95,7 @@ fn test_trap_trace_cb(config: crate::Config) -> Result<()> { }, )?; let run_func = instance - .exports - .get_function("run") + .lookup_function("run") .expect("expected function export"); let e = run_func.call(&[]).err().expect("error calling function"); @@ -129,8 +126,7 @@ fn test_trap_stack_overflow(config: crate::Config) -> Result<()> { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; let run_func = instance - .exports - .get_function("run") + .lookup_function("run") .expect("expected function export"); let e = run_func.call(&[]).err().expect("error calling function"); @@ -163,8 +159,7 @@ fn trap_display_pretty(config: crate::Config) -> Result<()> { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; let run_func = instance - .exports - .get_function("bar") + .lookup_function("bar") .expect("expected function export"); let e = run_func.call(&[]).err().expect("error calling function"); @@ -195,7 +190,7 @@ fn trap_display_multi_module(config: crate::Config) -> Result<()> { let module = Module::new(&store, wat)?; let instance = Instance::new(&module, &imports! {})?; - let bar = instance.exports.get_function("bar")?.clone(); + let bar = instance.lookup_function("bar").unwrap(); let wat = r#" (module $b @@ -214,8 +209,7 @@ fn trap_display_multi_module(config: crate::Config) -> Result<()> { }, )?; let bar2 = instance - .exports - .get_function("bar2") + .lookup_function("bar2") .expect("expected function export"); let e = bar2.call(&[]).err().expect("error calling function"); @@ -257,14 +251,12 @@ fn trap_start_function_import(config: crate::Config) -> Result<()> { .err() .unwrap(); match err { - InstantiationError::Link(_) - | InstantiationError::HostEnvInitialization(_) - | InstantiationError::CpuFeature(_) => { - panic!("It should be a start error") - } InstantiationError::Start(err) => { assert_eq!(err.message(), "user trap"); } + _ => { + panic!("It should be a start error") + } } Ok(()) @@ -294,7 +286,7 @@ fn rust_panic_import(config: crate::Config) -> Result<()> { } }, )?; - let func = instance.exports.get_function("foo")?.clone(); + let func = instance.lookup_function("foo").unwrap(); let err = panic::catch_unwind(AssertUnwindSafe(|| { drop(func.call(&[])); })) @@ -303,7 +295,7 @@ fn rust_panic_import(config: crate::Config) -> Result<()> { // TODO: Reenable this (disabled as it was not working with llvm/singlepass) // It doesn't work either with cranelift and `--test-threads=1`. - // let func = instance.exports.get_function("bar")?.clone(); + // let func = instance.lookup_function("bar")?.clone(); // let err = panic::catch_unwind(AssertUnwindSafe(|| { // drop(func.call(&[])); // })) @@ -371,7 +363,7 @@ fn mismatched_arguments(config: crate::Config) -> Result<()> { let module = Module::new(&store, &binary)?; let instance = Instance::new(&module, &imports! {})?; - let func: &Function = instance.exports.get("foo")?; + let func: Function = instance.lookup_function("foo").unwrap(); assert_eq!( func.call(&[]).unwrap_err().message(), "Parameters of type [] did not match signature [I32] -> []" @@ -457,7 +449,7 @@ fn present_after_module_drop(config: crate::Config) -> Result<()> { let store = config.store(); let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?; let instance = Instance::new(&module, &imports! {})?; - let func: Function = instance.exports.get_function("foo")?.clone(); + let func: Function = instance.lookup_function("foo").unwrap(); println!("asserting before we drop modules"); assert_trap(func.call(&[]).unwrap_err()); diff --git a/tests/lib/compiler-test-derive/src/lib.rs b/tests/lib/compiler-test-derive/src/lib.rs index f9dbc7d5f4..75be483c4e 100644 --- a/tests/lib/compiler-test-derive/src/lib.rs +++ b/tests/lib/compiler-test-derive/src/lib.rs @@ -85,7 +85,7 @@ pub fn compiler_test(attrs: TokenStream, input: TokenStream) -> TokenStream { new_sig.ident = test_name; new_sig.inputs = ::syn::punctuated::Punctuated::new(); let f = quote! { - #[test_env_log::test] + #[test_log::test] #attrs #[cfg(feature = #engine_feature_name)] #new_sig { diff --git a/tests/lib/compiler-test-derive/src/tests.rs b/tests/lib/compiler-test-derive/src/tests.rs index ae44f2d1e3..1833479762 100644 --- a/tests/lib/compiler-test-derive/src/tests.rs +++ b/tests/lib/compiler-test-derive/src/tests.rs @@ -54,7 +54,7 @@ gen_tests! { #[cfg(feature = "singlepass")] mod singlepass { use super::*; - #[test_env_log::test] + #[test_log::test] #[cold] #[cfg(feature = "universal")] fn universal() { @@ -63,7 +63,7 @@ gen_tests! { crate::Compiler::Singlepass )) } - #[test_env_log::test] + #[test_log::test] #[cold] #[cfg(feature = "dylib")] fn dylib() { @@ -77,7 +77,7 @@ gen_tests! { #[cfg(feature = "cranelift")] mod cranelift { use super::*; - #[test_env_log::test] + #[test_log::test] #[cold] #[cfg(feature = "universal")] fn universal() { @@ -86,7 +86,7 @@ gen_tests! { crate::Compiler::Cranelift )) } - #[test_env_log::test] + #[test_log::test] #[cold] #[cfg(feature = "dylib")] fn dylib() { @@ -100,7 +100,7 @@ gen_tests! { #[cfg(feature = "llvm")] mod llvm { use super::*; - #[test_env_log::test] + #[test_log::test] #[cold] #[cfg(feature = "universal")] fn universal() { @@ -109,7 +109,7 @@ gen_tests! { crate::Compiler::LLVM )) } - #[test_env_log::test] + #[test_log::test] #[cold] #[cfg(feature = "dylib")] fn dylib() { diff --git a/tests/lib/engine-dummy/Cargo.toml b/tests/lib/engine-dummy/Cargo.toml deleted file mode 100644 index fd7a5b8bbb..0000000000 --- a/tests/lib/engine-dummy/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "wasmer-engine-dummy" -version = "2.1.0" -authors = ["Wasmer Engineering Team "] -description = "Wasmer placeholder engine" -license = "MIT" -edition = "2018" -publish = false - -[dependencies] -wasmer-types = { path = "../../../lib/types", package = "wasmer-types-near", version = "2.0.3" } -wasmer-compiler = { path = "../../../lib/compiler", package = "wasmer-compiler-near", version = "2.0.3" } -wasmer-vm = { path = "../../../lib/vm", package = "wasmer-vm-near", version = "2.0.3" } -wasmer-engine = { path = "../../../lib/engine", package = "wasmer-engine-near", version = "2.0.3" } -serde = { version = "1.0", features = ["derive", "rc"], optional = true } -serde_bytes = { version = "0.11", optional = true } -bincode = { version = "1.2", optional = true } -loupe = "0.1" -enumset = "1.0" - -[features] -# Enable the `compiler` feature if you want the engine to compile -# and not be only on headless mode. -default = ["serialize", "compiler"] -compiler = ["wasmer-compiler/translator"] -serialize = ["serde", "serde_bytes", "bincode"] - -[badges] -# TODO: publish this crate again and deprecate it -maintenance = { status = "actively-developed" } diff --git a/tests/lib/engine-dummy/README.md b/tests/lib/engine-dummy/README.md deleted file mode 100644 index 50811ddedd..0000000000 --- a/tests/lib/engine-dummy/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Wasmer Dummy Engine - -The Dummy engine is mainly using for testing and learning proposes. -We use it for testing compiler-less code on the `wasmer` API -to make sure the API behaves as we expect. - -It can also be used to learn on how to implement a custom engine for Wasmer. - -A dummy engine, can't instantiate a Module. However it can inspect the -information related to `ModuleInfo`. \ No newline at end of file diff --git a/tests/lib/engine-dummy/src/artifact.rs b/tests/lib/engine-dummy/src/artifact.rs deleted file mode 100644 index ec0082bcba..0000000000 --- a/tests/lib/engine-dummy/src/artifact.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! Define `DummyArtifact` to allow compiling and instantiating to be -//! done as separate steps. - -use crate::engine::DummyEngine; -use enumset::EnumSet; -use loupe::MemoryUsage; -#[cfg(feature = "serialize")] -use serde::{Deserialize, Serialize}; -use std::sync::Arc; -#[cfg(feature = "compiler")] -use wasmer_compiler::ModuleEnvironment; -use wasmer_compiler::{CompileError, CpuFeature}; -use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables}; -use wasmer_types::entity::{BoxedSlice, PrimaryMap}; -use wasmer_types::{ - Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer, - SignatureIndex, TableIndex, -}; -use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMContext, VMFunctionBody, - VMSharedSignatureIndex, VMTrampoline, -}; - -/// Serializable struct for the artifact -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -#[derive(MemoryUsage)] -pub struct DummyArtifactMetadata { - pub module: Arc, - pub features: Features, - pub data_initializers: Box<[OwnedDataInitializer]>, - // Plans for that module - pub memory_styles: PrimaryMap, - pub table_styles: PrimaryMap, - pub cpu_features: u64, -} - -/// A Dummy artifact. -/// -/// This artifact will point to fake finished functions and trampolines -/// as no functions are really compiled. -#[derive(MemoryUsage)] -pub struct DummyArtifact { - metadata: DummyArtifactMetadata, - finished_functions: BoxedSlice, - #[loupe(skip)] - finished_function_call_trampolines: BoxedSlice, - finished_dynamic_function_trampolines: BoxedSlice, - signatures: BoxedSlice, - func_data_registry: Arc, -} - -extern "C" fn dummy_function(_context: *mut VMContext) { - panic!("Dummy engine can't generate functions") -} - -extern "C" fn dummy_trampoline( - _context: *mut VMContext, - _callee: *const VMFunctionBody, - _values: *mut u128, -) { - panic!("Dummy engine can't generate trampolines") -} - -impl DummyArtifact { - const MAGIC_HEADER: &'static [u8] = b"\0wasmer-dummy"; - - /// Check if the provided bytes look like a serialized `DummyArtifact`. - pub fn is_deserializable(bytes: &[u8]) -> bool { - bytes.starts_with(Self::MAGIC_HEADER) - } - - #[cfg(feature = "compiler")] - /// Compile a data buffer into a `DummyArtifact`, which may then be instantiated. - pub fn new( - engine: &DummyEngine, - data: &[u8], - tunables: &dyn Tunables, - ) -> Result { - let environ = ModuleEnvironment::new(); - - let translation = environ.translate(data).map_err(CompileError::Wasm)?; - - let memory_styles: PrimaryMap = translation - .module - .memories - .values() - .map(|memory_type| tunables.memory_style(memory_type)) - .collect(); - let table_styles: PrimaryMap = translation - .module - .tables - .values() - .map(|table_type| tunables.table_style(table_type)) - .collect(); - - let data_initializers = translation - .data_initializers - .iter() - .map(OwnedDataInitializer::new) - .collect::>() - .into_boxed_slice(); - - let metadata = DummyArtifactMetadata { - module: Arc::new(translation.module), - features: Features::default(), - data_initializers, - memory_styles, - table_styles, - cpu_features: engine.target().cpu_features().as_u64(), - }; - Self::from_parts(&engine, metadata) - } - - #[cfg(not(feature = "compiler"))] - pub fn new(engine: &DummyEngine, data: &[u8]) -> Result { - CompileError::Generic("The compiler feature is not enabled in the DummyEngine") - } - - #[cfg(feature = "serialize")] - /// Deserialize a DummyArtifact - pub fn deserialize(engine: &DummyEngine, bytes: &[u8]) -> Result { - if !Self::is_deserializable(bytes) { - return Err(DeserializeError::Incompatible( - "The provided bytes are not of the dummy engine".to_string(), - )); - } - - let inner_bytes = &bytes[Self::MAGIC_HEADER.len()..]; - - let metadata: DummyArtifactMetadata = bincode::deserialize(inner_bytes) - .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?; - - Self::from_parts(&engine, metadata).map_err(DeserializeError::Compiler) - } - - #[cfg(not(feature = "serialize"))] - pub fn deserialize(engine: &DummyEngine, bytes: &[u8]) -> Result { - Err(DeserializeError::Generic( - "The serializer feature is not enabled in the DummyEngine", - )) - } - - /// Construct a `DummyArtifact` from component parts. - pub fn from_parts( - engine: &DummyEngine, - metadata: DummyArtifactMetadata, - ) -> Result { - let num_local_functions = - metadata.module.functions.len() - metadata.module.num_imported_functions; - // We prepare the pointers for the finished functions. - let finished_functions: PrimaryMap = (0 - ..num_local_functions) - .map(|_| FunctionBodyPtr(dummy_function as _)) - .collect::>(); - - // We prepare the pointers for the finished function call trampolines. - let finished_function_call_trampolines: PrimaryMap = (0 - ..metadata.module.signatures.len()) - .map(|_| dummy_trampoline as VMTrampoline) - .collect::>(); - - // We prepare the pointers for the finished dynamic function trampolines. - let finished_dynamic_function_trampolines: PrimaryMap = (0 - ..metadata.module.num_imported_functions) - .map(|_| FunctionBodyPtr(dummy_function as _)) - .collect::>(); - - // Compute indices into the shared signature table. - let signatures = { - metadata - .module - .signatures - .values() - .map(|sig| engine.register_signature(sig)) - .collect::>() - }; - - let finished_functions = finished_functions.into_boxed_slice(); - let finished_function_call_trampolines = - finished_function_call_trampolines.into_boxed_slice(); - let finished_dynamic_function_trampolines = - finished_dynamic_function_trampolines.into_boxed_slice(); - let signatures = signatures.into_boxed_slice(); - - Ok(Self { - metadata, - finished_functions, - finished_function_call_trampolines, - finished_dynamic_function_trampolines, - signatures, - func_data_registry: engine.func_data().clone(), - }) - } -} - -impl Artifact for DummyArtifact { - fn module(&self) -> Arc { - self.metadata.module.clone() - } - - fn module_ref(&self) -> &ModuleInfo { - &self.metadata.module - } - - fn module_mut(&mut self) -> Option<&mut ModuleInfo> { - Arc::get_mut(&mut self.metadata.module) - } - - fn register_frame_info(&self) { - // Do nothing, since functions are not generated for the dummy engine - } - - fn features(&self) -> &Features { - &self.metadata.features - } - - fn cpu_features(&self) -> EnumSet { - EnumSet::from_u64(self.metadata.cpu_features) - } - - fn data_initializers(&self) -> &[OwnedDataInitializer] { - &*self.metadata.data_initializers - } - - fn memory_styles(&self) -> &PrimaryMap { - &self.metadata.memory_styles - } - - fn table_styles(&self) -> &PrimaryMap { - &self.metadata.table_styles - } - - fn finished_functions(&self) -> &BoxedSlice { - &self.finished_functions - } - - fn finished_functions_lengths(&self) -> &BoxedSlice { - unimplemented!(); - } - - fn finished_function_call_trampolines(&self) -> &BoxedSlice { - &self.finished_function_call_trampolines - } - - fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice { - &self.finished_dynamic_function_trampolines - } - - fn signatures(&self) -> &BoxedSlice { - &self.signatures - } - - fn func_data_registry(&self) -> &FuncDataRegistry { - &self.func_data_registry - } - - #[cfg(feature = "serialize")] - fn serialize(&self) -> Result, SerializeError> { - let bytes = bincode::serialize(&self.metadata) - .map_err(|e| SerializeError::Generic(format!("{:?}", e)))?; - - // Prepend the header. - let mut serialized = Self::MAGIC_HEADER.to_vec(); - serialized.extend(bytes); - Ok(serialized) - } - - #[cfg(not(feature = "serialize"))] - fn serialize(&self) -> Result, SerializeError> { - Err(SerializeError::Generic( - "The serializer feature is not enabled in the DummyEngine", - )) - } -} diff --git a/tests/lib/engine-dummy/src/engine.rs b/tests/lib/engine-dummy/src/engine.rs deleted file mode 100644 index d48c273b43..0000000000 --- a/tests/lib/engine-dummy/src/engine.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! Dummy Engine. - -use crate::DummyArtifact; -use loupe::MemoryUsage; -use std::sync::Arc; -use wasmer_compiler::{CompileError, Features, Target}; -use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables}; -use wasmer_types::FunctionType; -use wasmer_vm::{ - FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMContext, VMFuncRef, - VMFunctionBody, VMSharedSignatureIndex, -}; - -#[allow(dead_code)] -extern "C" fn dummy_trampoline( - _context: *mut VMContext, - _body: *const VMFunctionBody, - _values: *mut u128, -) { - panic!("Dummy engine can't call functions, since Wasm function bodies are not really compiled") -} - -/// A WebAssembly `Dummy` Engine. -#[derive(Clone, MemoryUsage)] -pub struct DummyEngine { - signatures: Arc, - func_data: Arc, - features: Arc, - target: Arc, - engine_id: EngineId, -} - -impl DummyEngine { - #[cfg(feature = "compiler")] - pub fn new() -> Self { - Self { - signatures: Arc::new(SignatureRegistry::new()), - func_data: Arc::new(FuncDataRegistry::new()), - features: Arc::new(Default::default()), - target: Arc::new(Default::default()), - engine_id: EngineId::default(), - } - } - - pub fn features(&self) -> &Features { - &self.features - } - - /// Shared func metadata registry. - pub(crate) fn func_data(&self) -> &Arc { - &self.func_data - } -} - -impl Engine for DummyEngine { - fn use_signals(&self) -> bool { - true - } - - /// Get the tunables - fn target(&self) -> &Target { - &self.target - } - - /// Register a signature - fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex { - self.signatures.register(func_type) - } - - fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { - self.func_data.register(func_data) - } - - /// Lookup a signature - fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { - self.signatures.lookup(sig) - } - - #[cfg(feature = "compiler")] - /// Validates a WebAssembly module - fn validate(&self, binary: &[u8]) -> Result<(), CompileError> { - use wasmer_compiler::wasmparser::{Validator, WasmFeatures}; - - let features = self.features(); - let mut validator = Validator::new(); - let wasm_features = WasmFeatures { - bulk_memory: features.bulk_memory, - threads: features.threads, - reference_types: features.reference_types, - multi_value: features.multi_value, - simd: features.simd, - tail_call: features.tail_call, - module_linking: features.module_linking, - multi_memory: features.multi_memory, - memory64: features.memory64, - exceptions: features.exceptions, - deterministic_only: false, - }; - validator.wasm_features(wasm_features); - validator - .validate_all(binary) - .map_err(|e| CompileError::Validate(format!("{}", e)))?; - Ok(()) - } - - #[cfg(not(feature = "compiler"))] - /// Validates a WebAssembly module - fn validate(&self, binary: &[u8]) -> Result<(), CompileError> { - // We mark all Wasm modules as valid - Ok(()) - } - - /// Compile a WebAssembly binary - fn compile( - &self, - binary: &[u8], - tunables: &dyn Tunables, - ) -> Result, CompileError> { - Ok(Arc::new(DummyArtifact::new(&self, binary, tunables)?)) - } - - /// Deserializes a WebAssembly module (binary content of a Shared Object file) - unsafe fn deserialize(&self, bytes: &[u8]) -> Result, DeserializeError> { - Ok(Arc::new(DummyArtifact::deserialize(&self, &bytes)?)) - } - - fn id(&self) -> &EngineId { - &self.engine_id - } - - fn cloned(&self) -> Arc { - Arc::new(self.clone()) - } -} diff --git a/tests/lib/engine-dummy/src/lib.rs b/tests/lib/engine-dummy/src/lib.rs deleted file mode 100644 index ed7e39b95e..0000000000 --- a/tests/lib/engine-dummy/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod artifact; -mod engine; - -pub use artifact::DummyArtifact; -pub use engine::DummyEngine; diff --git a/tests/lib/wast/src/lib.rs b/tests/lib/wast/src/lib.rs index 2cd8ec4420..099430e5f9 100644 --- a/tests/lib/wast/src/lib.rs +++ b/tests/lib/wast/src/lib.rs @@ -10,8 +10,8 @@ clippy::float_arithmetic, clippy::mut_mut, clippy::nonminimal_bool, - clippy::option_map_unwrap_or, - clippy::option_map_unwrap_or_else, + clippy::map_unwrap_or, + clippy::map_unwrap_or_else, clippy::print_stdout, clippy::unicode_not_nfc, clippy::use_self diff --git a/tests/lib/wast/src/wast.rs b/tests/lib/wast/src/wast.rs index bde09dc849..94ad929310 100644 --- a/tests/lib/wast/src/wast.rs +++ b/tests/lib/wast/src/wast.rs @@ -100,7 +100,16 @@ impl Wast { let result = self.instantiate(&binary); result.map(|_| Vec::new()) } - wast::WastExecute::Get { module, global } => self.get(module.map(|s| s.name()), global), + wast::WastExecute::Get { module, global } => { + let instance_name = module.map(|i| i.name()); + let instance = self.get_instance(instance_name.as_deref())?; + let global = if let Some(Export::Global(global)) = instance.lookup(global) { + global + } else { + bail!("could not find the global: {}", global) + }; + Ok(vec![global.from.get(&self.store)]) + } } } @@ -209,7 +218,7 @@ impl Wast { Err(e) => e, }; let error_message = format!("{:?}", err); - if !Self::matches_message_assert_invalid(&message, &error_message) { + if !Self::matches_message_assert_invalid(message, &error_message) { bail!( "assert_invalid: expected \"{}\", got \"{}\"", message, @@ -250,7 +259,7 @@ impl Wast { Err(e) => e, }; let error_message = format!("{:?}", err); - if !Self::matches_message_assert_unlinkable(&message, &error_message) { + if !Self::matches_message_assert_unlinkable(message, &error_message) { bail!( "assert_unlinkable: expected {}, got {}", message, @@ -278,7 +287,7 @@ impl Wast { let mut errors = Vec::with_capacity(ast.directives.len()); for directive in ast.directives { let sp = directive.span(); - if let Err(e) = self.run_directive(&test, directive) { + if let Err(e) = self.run_directive(test, directive) { let message = format!("{}", e); // If depends on an instance that doesn't exist if message.contains("no previous instance found") { @@ -317,7 +326,7 @@ impl Wast { Ok(s) => ret.push_str(s), Err(_) => bail!("malformed UTF-8 encoding"), } - ret.push_str(" "); + ret.push(' '); } let buf = wast::parser::ParseBuffer::new(&ret)?; let mut wat = wast::parser::parse::(&buf)?; @@ -371,21 +380,7 @@ impl Wast { fn instantiate(&self, module: &[u8]) -> Result { let module = Module::new(&self.store, module)?; - let mut imports = self.import_object.clone(); - - for import in module.imports() { - let module_name = import.module(); - if imports.contains_namespace(module_name) { - continue; - } - let instance = self - .instances - .get(module_name) - .ok_or_else(|| anyhow!("constant expression required"))?; - imports.register(module_name, instance.exports.clone()); - } - - let instance = Instance::new(&module, &imports)?; + let instance = Instance::new(&module, &self)?; Ok(instance) } @@ -404,20 +399,15 @@ impl Wast { args: &[Val], ) -> Result> { let instance = self.get_instance(instance_name.as_deref())?; - let func: &Function = instance.exports.get(field)?; + let func: Function = instance + .lookup_function(field) + .expect("should find the function"); match func.call(args) { Ok(result) => Ok(result.into()), Err(e) => Err(e.into()), } } - /// Get the value of an exported global from an instance. - fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result> { - let instance = self.get_instance(instance_name.as_deref())?; - let global: &Global = instance.exports.get(field)?; - Ok(vec![global.get()]) - } - /// Translate from a `script::Value` to a `Val`. fn runtime_value(&mut self, v: &wast::Expression<'_>) -> Result { use wast::Instruction::*; @@ -473,8 +463,7 @@ impl Wast { || self .match_trap_messages .get(expected) - .map(|alternative| actual.contains(alternative)) - .unwrap_or(false) + .map_or(false, |alternative| actual.contains(alternative)) } fn val_matches(&self, actual: &Val, expected: &wast::AssertExpression) -> Result { @@ -519,6 +508,19 @@ impl Wast { } } +impl NamedResolver for Wast { + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + let imports = self.import_object.clone(); + + if imports.contains_namespace(module) { + imports.resolve_by_name(module, field) + } else { + let instance = self.instances.get(module)?; + instance.lookup(field) + } + } +} + fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 { (bytes >> (lane * 8)) as i8 }