mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-06 02:50:18 +00:00
Merge branch 'master' into feature/debug-prototype2
This commit is contained in:
commit
64cc59179a
@ -2,6 +2,13 @@
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder.
|
||||
- [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix.
|
||||
- [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type.
|
||||
- [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types.
|
||||
- [#1213](https://github.com/wasmerio/wasmer/pull/1213) Fixed WASI `fdstat` to detect `isatty` properly.
|
||||
- [#1192](https://github.com/wasmerio/wasmer/pull/1192) Use `ExceptionCode` for error representation.
|
||||
- [#1191](https://github.com/wasmerio/wasmer/pull/1191) Fix singlepass miscompilation on `Operator::CallIndirect`.
|
||||
- [#1180](https://github.com/wasmerio/wasmer/pull/1180) Fix compilation for target `x86_64-unknown-linux-musl`.
|
||||
- [#1170](https://github.com/wasmerio/wasmer/pull/1170) Improve the WasiFs builder API with convenience methods for overriding stdin, stdout, and stderr as well as a new sub-builder for controlling the permissions and properties of preopened directories. Also breaks that implementations of `WasiFile` must be `Send` -- please file an issue if this change causes you any issues.
|
||||
- [#1161](https://github.com/wasmerio/wasmer/pull/1161) Require imported functions to be `Send`. This is a breaking change that fixes a soundness issue in the API.
|
||||
|
342
Cargo.lock
generated
342
Cargo.lock
generated
@ -2,9 +2,9 @@
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.6"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||
checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -26,9 +26,18 @@ checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
@ -38,10 +47,11 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.13"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
@ -76,22 +86,24 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "0.1.3"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41b598ce988ade113c05bc970a8c9102b59dfac0b318289971cba3379471cae4"
|
||||
checksum = "46080006c1505f12f64dd2a09264b343381ed3190fa02c8005d5d662ac571c63"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"arrayvec 0.5.1",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"constant_time_eq",
|
||||
"crypto-mac",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.8"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
|
||||
checksum = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
@ -101,9 +113,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.2"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "c2-chacha"
|
||||
@ -131,11 +143,11 @@ checksum = "9daec6140ab4dcd38c3dd57e580b59a621172a526ac79f1527af760a55afeafd"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
@ -234,7 +246,7 @@ dependencies = [
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-entity",
|
||||
"log",
|
||||
"smallvec 1.1.0",
|
||||
"smallvec 1.2.0",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
]
|
||||
@ -336,10 +348,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700"
|
||||
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
@ -355,10 +368,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.1"
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
|
||||
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
@ -369,9 +392,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
|
||||
checksum = "076bbef4255ffbc67b0358f2c92b5635928260c4cd28f3a65fa4b07c7a4f546d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -383,7 +406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc"
|
||||
dependencies = [
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -405,9 +428,9 @@ dependencies = [
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"owning_ref",
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -428,9 +451,9 @@ checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.3.9"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60"
|
||||
checksum = "cd7d80305c9bd8cd78e3c753eb9fb110f83621e5211f1a3afffcc812b104daf9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -537,9 +560,9 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -601,24 +624,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7"
|
||||
checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e"
|
||||
checksum = "76cdda6bf525062a0c9e8f14ee2b37935c86b8efb6c8b69b3c83dfb518a914af"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc"
|
||||
checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"serde",
|
||||
@ -650,9 +673,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "inventory"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4cece20baea71d9f3435e7bbe9adf4765f091c5fe404975f844006964a71299"
|
||||
checksum = "2bf98296081bd2cb540acc09ef9c97f22b7e487841520350293605db1b2c7a27"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"ghost",
|
||||
@ -661,13 +684,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "inventory-impl"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9"
|
||||
checksum = "0a8e30575afe28eea36a9a39136b70b2fb6b0dd0a212a5bd1f30a498395c0274"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -681,9 +704,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
|
||||
[[package]]
|
||||
name = "kernel-net"
|
||||
@ -695,6 +718,19 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.12",
|
||||
"cfg-if",
|
||||
"rustc_version",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
@ -716,9 +752,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586"
|
||||
checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
@ -746,12 +782,9 @@ checksum = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.2.1"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
@ -805,6 +838,23 @@ dependencies = [
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c433f4d505fe6ce7ff78523d2fa13a0b9f2690e181fc26168bcbe5ccc5d14e07"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.1.42"
|
||||
@ -818,39 +868,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.41"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"autocfg 1.0.0",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.39"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"
|
||||
checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"autocfg 1.0.0",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
|
||||
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"autocfg 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.11.1"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
|
||||
checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
@ -858,9 +908,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.2.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed"
|
||||
checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
|
||||
|
||||
[[package]]
|
||||
name = "orbclient"
|
||||
@ -929,7 +979,7 @@ dependencies = [
|
||||
"cloudabi",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.1.0",
|
||||
"smallvec 1.2.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -957,27 +1007,27 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "0.4.4"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428"
|
||||
checksum = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"rustversion",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "0.4.3"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c"
|
||||
checksum = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"rustversion",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
"syn-mid",
|
||||
]
|
||||
|
||||
@ -992,9 +1042,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||
checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0",
|
||||
]
|
||||
@ -1014,7 +1064,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1038,9 +1088,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
@ -1232,9 +1282,9 @@ checksum = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.3"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87"
|
||||
checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -1253,9 +1303,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.12"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
|
||||
checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
@ -1277,13 +1327,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986"
|
||||
checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1294,9 +1344,9 @@ checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
@ -1421,16 +1471,16 @@ version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.44"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
|
||||
checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -1448,9 +1498,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4"
|
||||
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
@ -1459,13 +1509,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
|
||||
[[package]]
|
||||
name = "string-interner"
|
||||
version = "0.7.1"
|
||||
name = "static_assertions"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
@ -1475,9 +1522,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df136b42d76b1fbea72e2ab3057343977b04b4a2e00836c3c7c0673829572713"
|
||||
checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
@ -1486,17 +1533,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd50a87d2f7b8958055f3e73a963d78feaccca3836767a9069844e34b5b03c0a"
|
||||
checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
@ -1510,24 +1563,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.11"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
||||
checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"unicode-xid 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn-mid"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0"
|
||||
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1544,7 +1597,7 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rand 0.7.2",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
@ -1561,22 +1614,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e"
|
||||
checksum = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef"
|
||||
checksum = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1601,9 +1654,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20"
|
||||
checksum = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -1611,9 +1664,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.5"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
|
||||
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -1643,9 +1696,9 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b63fd4799e4d0ec5cf0b055ebb8e2c3a657bbf76a84f6edc77ca60780e000204"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6",
|
||||
"proc-macro2 1.0.8",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.11",
|
||||
"syn 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1678,6 +1731,12 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
@ -1709,9 +1768,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.2.9"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
|
||||
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
@ -1804,7 +1863,7 @@ checksum = "6d2e13201ef9ef527ad30a6bf1b08e3e024a40cf2731f393d80375dc88506207"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
"smallvec 1.1.0",
|
||||
"smallvec 1.2.0",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
@ -1856,6 +1915,13 @@ dependencies = [
|
||||
"wasmer-singlepass-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-interface-types"
|
||||
version = "0.13.1"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-kernel-loader"
|
||||
version = "0.1.0"
|
||||
@ -2067,15 +2133,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.39.3"
|
||||
version = "0.45.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c702914acda5feeeffbc29e4d953e5b9ce79d8b98da4dbf18a77086e116c5470"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd242c848b25027c3e29fb2bd34c8648755b33fef66a22f65b942d06562d3020"
|
||||
checksum = "8b4eab1d9971d0803729cba3617b56eb04fcb4bd25361cb63880ed41a42f20d5"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
@ -2095,9 +2155,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||
checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
@ -2110,9 +2170,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.18.4"
|
||||
version = "2.18.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be65e1342a3baae65439cd03306778831a3d133b0d20243a7fb83fd5cf403c58"
|
||||
checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
@ -64,6 +64,7 @@ members = [
|
||||
"lib/wasi-tests",
|
||||
"lib/emscripten-tests",
|
||||
"lib/middleware-common-tests",
|
||||
"lib/interface-types",
|
||||
"examples/parallel",
|
||||
"examples/plugin-for-example",
|
||||
"examples/parallel-guest",
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM circleci/rust:1.39.0-stretch as wasmer-build-env
|
||||
FROM circleci/rust:1.40.0-stretch as wasmer-build-env
|
||||
RUN sudo apt-get update && \
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
|
4
Makefile
4
Makefile
@ -201,7 +201,7 @@ check-bench: check-bench-singlepass check-bench-llvm
|
||||
|
||||
# TODO: We wanted `--workspace --exclude wasmer-runtime`, but can't due
|
||||
# to https://github.com/rust-lang/cargo/issues/6745 .
|
||||
NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-dev-utils -p wasmer-wasi-tests -p wasmer-middleware-common-tests -p wasmer-emscripten-tests
|
||||
NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-dev-utils -p wasmer-wasi-tests -p wasmer-middleware-common-tests -p wasmer-emscripten-tests -p wasmer-interface-types
|
||||
RUNTIME_CHECK = cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features
|
||||
check: check-bench
|
||||
cargo check $(NOT_RUNTIME_CRATES)
|
||||
@ -308,7 +308,7 @@ dep-graph:
|
||||
cargo deps --optional-deps --filter wasmer-wasi wasmer-wasi-tests wasmer-kernel-loader wasmer-dev-utils wasmer-llvm-backend wasmer-emscripten wasmer-emscripten-tests wasmer-runtime-core wasmer-runtime wasmer-middleware-common wasmer-middleware-common-tests wasmer-singlepass-backend wasmer-clif-backend wasmer --manifest-path Cargo.toml | dot -Tpng > wasmer_depgraph.png
|
||||
|
||||
docs:
|
||||
cargo doc --features=backend-singlepass,backend-cranelift,backend-llvm,docs,wasi,managed --all --no-deps
|
||||
cargo doc --features=backend-singlepass,backend-cranelift,backend-llvm,docs,wasi,managed --workspace --document-private-items --no-deps
|
||||
cd lib/runtime-c-api/ && doxygen doxyfile && cd ..
|
||||
mkdir -p api-docs
|
||||
mkdir -p api-docs/c
|
||||
|
@ -174,7 +174,7 @@ nginx and Lua do not work on Windows - you can track the progress on [this issue
|
||||
|
||||
## Building
|
||||
|
||||
[![Rustc Version 1.39+](https://img.shields.io/badge/rustc-1.39+-red.svg?style=flat-square)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html)
|
||||
[![Rustc Version 1.40+](https://img.shields.io/badge/rustc-1.40+-red.svg?style=flat-square)](https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html)
|
||||
|
||||
Wasmer is built with [Cargo](https://crates.io/), the Rust package manager.
|
||||
|
||||
|
@ -21,7 +21,7 @@ jobs:
|
||||
- script: cargo fmt --all -- --check
|
||||
displayName: Lint
|
||||
variables:
|
||||
rust_toolchain: '1.39.0'
|
||||
rust_toolchain: '1.40.0'
|
||||
|
||||
- job: clippy_lint
|
||||
pool:
|
||||
@ -57,7 +57,7 @@ jobs:
|
||||
CARGO_HTTP_CHECK_REVOKE: false
|
||||
windows:
|
||||
imageName: "vs2017-win2016"
|
||||
rust_toolchain: '1.39.0'
|
||||
rust_toolchain: '1.40.0'
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying')
|
||||
@ -118,7 +118,7 @@ jobs:
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.10
|
||||
windows:
|
||||
imageName: "vs2017-win2016"
|
||||
rust_toolchain: '1.39.0'
|
||||
rust_toolchain: '1.40.0'
|
||||
# RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
@ -188,7 +188,7 @@ jobs:
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.10
|
||||
windows:
|
||||
imageName: "vs2017-win2016"
|
||||
rust_toolchain: '1.39.0'
|
||||
rust_toolchain: '1.40.0'
|
||||
# RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
@ -235,7 +235,7 @@ jobs:
|
||||
pool:
|
||||
vmImage: "ubuntu-18.04"
|
||||
variables:
|
||||
rust_toolchain: nightly-2019-08-15
|
||||
rust_toolchain: nightly-2019-12-19
|
||||
condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying')
|
||||
steps:
|
||||
- checkout: self
|
||||
|
@ -1 +0,0 @@
|
||||
<meta http-equiv="refresh" content="0; url=rust/wasmer_runtime/index.html">
|
@ -145,7 +145,7 @@ wasmer_link() {
|
||||
printf "$cyan> Adding to bash profile...$reset\n"
|
||||
WASMER_PROFILE="$(wasmer_detect_profile)"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\"\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$PATH:\$WASMER_DIR/globals/wapm_packages/.bin\"\n"
|
||||
|
||||
# We create the wasmer.sh file
|
||||
printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/wasmer.sh"
|
||||
|
@ -24,6 +24,7 @@ Wasmer intends to support different integrations:
|
||||
|
||||
- [WASI](./wasi): run WebAssembly files with the [WASI ABI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/).
|
||||
- [Emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm).
|
||||
- **Your own ABI**: Do you want to create your own ABI? Here's a [repo showcasing how](https://github.com/wasmerio/wasmer-rust-customabi-example)!
|
||||
- Go ABI: _we will work on this soon! Want to give us a hand? ✋_
|
||||
- Blazor: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
|
||||
|
||||
|
@ -42,7 +42,9 @@ fn get_isa() -> Box<dyn isa::TargetIsa> {
|
||||
builder.set("opt_level", "speed_and_size").unwrap();
|
||||
builder.set("jump_tables_enabled", "false").unwrap();
|
||||
|
||||
if cfg!(not(test)) && cfg!(not(debug)) {
|
||||
if cfg!(test) || cfg!(debug_assertions) {
|
||||
builder.set("enable_verifier", "true").unwrap();
|
||||
} else {
|
||||
builder.set("enable_verifier", "false").unwrap();
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,9 @@ pub extern "C" fn nearbyintf64(x: f64) -> f64 {
|
||||
}
|
||||
|
||||
// FIXME: Is there a replacement on AArch64?
|
||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
||||
#[cfg(all(
|
||||
any(target_os = "freebsd", target_os = "linux"),
|
||||
target_arch = "aarch64"
|
||||
))]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn __rust_probestack() {}
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use cranelift_codegen::{
|
||||
binemit::{Stackmap, StackmapSink},
|
||||
ir, isa, Context, ValueLabelsRanges,
|
||||
ir, isa,CodegenError, Context, ValueLabelsRanges,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use std::{
|
||||
@ -141,7 +141,12 @@ impl FuncResolverBuilder {
|
||||
&mut local_trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
|
||||
.map_err(|e| match e {
|
||||
CodegenError::Verifier(v) => CompileError::InternalError {
|
||||
msg: format!("Verifier error: {}", v),
|
||||
},
|
||||
_ => CompileError::InternalError { msg: e.to_string() },
|
||||
})?;
|
||||
|
||||
let debug_entry = if generate_debug_info {
|
||||
let func = &ctx.func;
|
||||
|
@ -8,7 +8,7 @@ use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc};
|
||||
use wasmer_runtime_core::{
|
||||
backend::RunnableModule,
|
||||
module::ModuleInfo,
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm,
|
||||
};
|
||||
@ -29,10 +29,7 @@ thread_local! {
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any + Send>>> = Cell::new(None);
|
||||
}
|
||||
|
||||
pub enum CallProtError {
|
||||
Trap(WasmTrapInfo),
|
||||
Error(Box<dyn Any + Send>),
|
||||
}
|
||||
pub struct CallProtError(pub Box<dyn Any + Send>);
|
||||
|
||||
pub struct Caller {
|
||||
handler_data: HandlerData,
|
||||
@ -66,8 +63,7 @@ impl RunnableModule for Caller {
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
|
||||
@ -84,10 +80,7 @@ impl RunnableModule for Caller {
|
||||
|
||||
match res {
|
||||
Err(err) => {
|
||||
match err {
|
||||
CallProtError::Trap(info) => *trap_info = info,
|
||||
CallProtError::Error(data) => *user_error = Some(data),
|
||||
}
|
||||
*error_out = Some(err.0);
|
||||
false
|
||||
}
|
||||
Ok(()) => true,
|
||||
|
@ -18,7 +18,7 @@ use nix::sys::signal::{
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
use wasmer_runtime_core::backend::ExceptionCode;
|
||||
|
||||
extern "C" fn signal_trap_handler(
|
||||
signum: ::nix::libc::c_int,
|
||||
@ -79,7 +79,7 @@ pub fn call_protected<T>(
|
||||
*jmp_buf = prev_jmp_buf;
|
||||
|
||||
if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(CallProtError::Error(data))
|
||||
Err(CallProtError(data))
|
||||
} else {
|
||||
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
||||
|
||||
@ -88,21 +88,31 @@ pub fn call_protected<T>(
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(inst_ptr)
|
||||
{
|
||||
Err(CallProtError::Trap(match Signal::from_c_int(signum) {
|
||||
Err(CallProtError(Box::new(match Signal::from_c_int(signum) {
|
||||
Ok(SIGILL) => match trapcode {
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
TrapCode::StackOverflow => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::OutOfBounds => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::IndirectCallToNull => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::BadSignature => ExceptionCode::IncorrectCallIndirectSignature,
|
||||
TrapCode::IntegerOverflow => ExceptionCode::IllegalArithmetic,
|
||||
TrapCode::IntegerDivisionByZero => ExceptionCode::IllegalArithmetic,
|
||||
TrapCode::BadConversionToInteger => ExceptionCode::IllegalArithmetic,
|
||||
TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable,
|
||||
_ => {
|
||||
return Err(CallProtError(Box::new(
|
||||
"unknown clif trap code".to_string(),
|
||||
)))
|
||||
}
|
||||
},
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds,
|
||||
Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic,
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => ExceptionCode::MemoryOutOfBounds,
|
||||
Ok(SIGFPE) => ExceptionCode::IllegalArithmetic,
|
||||
_ => unimplemented!(
|
||||
"WasmTrapInfo::Unknown signal:{:?}",
|
||||
"ExceptionCode::Unknown signal:{:?}",
|
||||
Signal::from_c_int(signum)
|
||||
),
|
||||
}))
|
||||
})))
|
||||
} else {
|
||||
let signal = match Signal::from_c_int(signum) {
|
||||
Ok(SIGFPE) => "floating-point exception",
|
||||
@ -114,7 +124,7 @@ pub fn call_protected<T>(
|
||||
};
|
||||
// When the trap-handler is fully implemented, this will return more information.
|
||||
let s = format!("unknown trap at {:p} - {}", faulting_addr, signal);
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
Err(CallProtError(Box::new(s)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -141,6 +151,84 @@ pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_
|
||||
longjmp(jmp_buf as *mut ::nix::libc::c_void, signum)
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "freebsd", target_arch = "aarch64"))]
|
||||
unsafe fn get_faulting_addr_and_ip(
|
||||
_siginfo: *const c_void,
|
||||
_ucontext: *const c_void,
|
||||
) -> (*const c_void, *const c_void) {
|
||||
(::std::ptr::null(), ::std::ptr::null())
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
|
||||
unsafe fn get_faulting_addr_and_ip(
|
||||
siginfo: *const c_void,
|
||||
ucontext: *const c_void,
|
||||
) -> (*const c_void, *const c_void) {
|
||||
#[repr(C)]
|
||||
pub struct ucontext_t {
|
||||
uc_sigmask: libc::sigset_t,
|
||||
uc_mcontext: mcontext_t,
|
||||
uc_link: *mut ucontext_t,
|
||||
uc_stack: libc::stack_t,
|
||||
uc_flags: i32,
|
||||
__spare__: [i32; 4],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct mcontext_t {
|
||||
mc_onstack: u64,
|
||||
mc_rdi: u64,
|
||||
mc_rsi: u64,
|
||||
mc_rdx: u64,
|
||||
mc_rcx: u64,
|
||||
mc_r8: u64,
|
||||
mc_r9: u64,
|
||||
mc_rax: u64,
|
||||
mc_rbx: u64,
|
||||
mc_rbp: u64,
|
||||
mc_r10: u64,
|
||||
mc_r11: u64,
|
||||
mc_r12: u64,
|
||||
mc_r13: u64,
|
||||
mc_r14: u64,
|
||||
mc_r15: u64,
|
||||
mc_trapno: u32,
|
||||
mc_fs: u16,
|
||||
mc_gs: u16,
|
||||
mc_addr: u64,
|
||||
mc_flags: u32,
|
||||
mc_es: u16,
|
||||
mc_ds: u16,
|
||||
mc_err: u64,
|
||||
mc_rip: u64,
|
||||
mc_cs: u64,
|
||||
mc_rflags: u64,
|
||||
mc_rsp: u64,
|
||||
mc_ss: u64,
|
||||
mc_len: i64,
|
||||
|
||||
mc_fpformat: i64,
|
||||
mc_ownedfp: i64,
|
||||
mc_fpstate: [i64; 64], // mc_fpstate[0] is a pointer to savefpu
|
||||
|
||||
mc_fsbase: u64,
|
||||
mc_gsbase: u64,
|
||||
|
||||
mc_xfpustate: u64,
|
||||
mc_xfpustate_len: u64,
|
||||
|
||||
mc_spare: [i64; 4],
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *const ucontext_t;
|
||||
let rip = (*ucontext).uc_mcontext.mc_rip;
|
||||
|
||||
(si_addr, rip as _)
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
||||
unsafe fn get_faulting_addr_and_ip(
|
||||
_siginfo: *const c_void,
|
||||
@ -239,6 +327,8 @@ unsafe fn get_faulting_addr_and_ip(
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
all(target_os = "freebsd", target_arch = "aarch64"),
|
||||
all(target_os = "freebsd", target_arch = "x86_64"),
|
||||
all(target_os = "macos", target_arch = "x86_64"),
|
||||
all(target_os = "linux", target_arch = "x86_64"),
|
||||
all(target_os = "linux", target_arch = "aarch64"),
|
||||
|
@ -8,7 +8,8 @@ use std::{
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
typed_func::{Trampoline, WasmTrapInfo},
|
||||
backend::ExceptionCode,
|
||||
typed_func::Trampoline,
|
||||
vm::{Ctx, Func},
|
||||
};
|
||||
use wasmer_win_exception_handler::CallProtectedData;
|
||||
@ -62,22 +63,26 @@ pub fn call_protected(
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(instruction_pointer as _)
|
||||
{
|
||||
Err(CallProtError::Trap(match code as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds,
|
||||
Err(CallProtError(Box::new(match code as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => ExceptionCode::MemoryOutOfBounds,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::UnreachableCodeReached => WasmTrapInfo::Unreachable,
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
TrapCode::BadSignature => ExceptionCode::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable,
|
||||
_ => return Err(CallProtError(Box::new("unknown trap code".to_string()))),
|
||||
},
|
||||
EXCEPTION_STACK_OVERFLOW => WasmTrapInfo::Unknown,
|
||||
EXCEPTION_STACK_OVERFLOW => ExceptionCode::MemoryOutOfBounds,
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => {
|
||||
WasmTrapInfo::IllegalArithmetic
|
||||
ExceptionCode::IllegalArithmetic
|
||||
}
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
}))
|
||||
_ => {
|
||||
return Err(CallProtError(Box::new(
|
||||
"unknown exception code".to_string(),
|
||||
)))
|
||||
}
|
||||
})))
|
||||
} else {
|
||||
let signal = match code as DWORD {
|
||||
EXCEPTION_FLT_DENORMAL_OPERAND
|
||||
@ -110,7 +115,7 @@ pub fn call_protected(
|
||||
exception_address, code, signal,
|
||||
);
|
||||
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
Err(CallProtError(Box::new(s)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,19 @@ extern "C" {
|
||||
pub fn lstat64(path: *const libc::c_char, buf: *mut c_void) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
// Linking to functions that are not provided by rust libc
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[link(name = "c")]
|
||||
extern "C" {
|
||||
pub fn wait4(pid: pid_t, status: *mut c_int, options: c_int, rusage: *mut rusage) -> pid_t;
|
||||
pub fn fdatasync(fd: c_int) -> c_int;
|
||||
pub fn ftruncate(fd: c_int, length: i64) -> c_int;
|
||||
pub fn lstat(path: *const libc::c_char, buf: *mut stat) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
use libc::madvise;
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
|
||||
use libc::{fallocate, fdatasync, ftruncate64, lstat, madvise, wait4};
|
||||
|
||||
// Another conditional constant for name resolution: Macos et iOS use
|
||||
@ -255,10 +267,14 @@ pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
debug!("emscripten::___syscall194 (ftruncate64) {}", _which);
|
||||
let _fd: c_int = varargs.get(ctx);
|
||||
let _length: i64 = varargs.get(ctx);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
|
||||
unsafe {
|
||||
ftruncate64(_fd, _length)
|
||||
}
|
||||
#[cfg(target_os = "freebsd")]
|
||||
unsafe {
|
||||
ftruncate(_fd, _length)
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which)
|
||||
}
|
||||
@ -621,7 +637,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
let mut host_address: sockaddr = sockaddr {
|
||||
sa_family: Default::default(),
|
||||
sa_data: Default::default(),
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
sa_len: Default::default(),
|
||||
};
|
||||
let fd = unsafe { accept(socket, &mut host_address, address_len_addr) };
|
||||
@ -655,7 +671,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
let mut sock_addr_host: sockaddr = sockaddr {
|
||||
sa_family: Default::default(),
|
||||
sa_data: Default::default(),
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
sa_len: Default::default(),
|
||||
};
|
||||
let ret = unsafe {
|
||||
@ -1056,8 +1072,17 @@ pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
|
||||
break;
|
||||
}
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
unsafe {
|
||||
*(dirp.add(pos) as *mut u32) = (*dirent).d_ino as u32;
|
||||
}
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
unsafe {
|
||||
*(dirp.add(pos) as *mut u32) = (*dirent).d_fileno as u32;
|
||||
}
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
unsafe {
|
||||
*(dirp.add(pos + 4) as *mut u32) = pos as u32;
|
||||
*(dirp.add(pos + 8) as *mut u16) = offset as u16;
|
||||
*(dirp.add(pos + 10) as *mut u8) = (*dirent).d_type;
|
||||
@ -1106,11 +1131,11 @@ pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
let _mode: c_int = varargs.get(ctx);
|
||||
let _offset: off_t = varargs.get(ctx);
|
||||
let _len: off_t = varargs.get(ctx);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
|
||||
unsafe {
|
||||
fallocate(_fd, _mode, _offset, _len)
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
{
|
||||
unimplemented!("emscripten::___syscall324 (fallocate) {}", _which)
|
||||
}
|
||||
|
@ -31,6 +31,11 @@ use wasmer_runtime_core::vm::Ctx;
|
||||
#[cfg(target_os = "linux")]
|
||||
use libc::{CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_REALTIME};
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME};
|
||||
#[cfg(target_os = "freebsd")]
|
||||
const CLOCK_MONOTONIC_COARSE: clockid_t = 6;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use libc::CLOCK_REALTIME;
|
||||
#[cfg(target_os = "macos")]
|
||||
|
11
lib/interface-types/Cargo.toml
Normal file
11
lib/interface-types/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "wasmer-interface-types"
|
||||
version = "0.13.1"
|
||||
description = "WebAssembly Interface Types library for Wasmer"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
nom = "5.1"
|
32
lib/interface-types/README.md
Normal file
32
lib/interface-types/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
<p align="center">
|
||||
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
|
||||
<img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
|
||||
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
|
||||
</a>
|
||||
<a href="https://spectrum.chat/wasmer">
|
||||
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
|
||||
</a>
|
||||
<a href="https://crates.io/crates/wasmer-interface-types">
|
||||
<img src="https://img.shields.io/crates/d/wasmer-interface-types.svg?style=flat-square" alt="Number of downloads from crates.io">
|
||||
</a>
|
||||
<a href="https://docs.rs/wasmer-interface-types">
|
||||
<img src="https://docs.rs/wasmer-interface-types/badge.svg" alt="Read our API documentation">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# Wasmer Interface Types
|
||||
|
||||
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
|
||||
compatible with WASI, Emscripten, Rust and Go. [Learn
|
||||
more](https://github.com/wasmerio/wasmer).
|
||||
|
||||
This crate is an implementation of [the living WebAssembly Interface
|
||||
Types standard](https://github.com/WebAssembly/interface-types).
|
209
lib/interface-types/src/ast.rs
Normal file
209
lib/interface-types/src/ast.rs
Normal file
@ -0,0 +1,209 @@
|
||||
//! Represents the WIT language as a tree. This is the central
|
||||
//! representation of the language.
|
||||
|
||||
use crate::interpreter::Instruction;
|
||||
use std::str;
|
||||
|
||||
/// Represents the types supported by WIT.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum InterfaceType {
|
||||
/// An integer.
|
||||
Int,
|
||||
|
||||
/// A float.
|
||||
Float,
|
||||
|
||||
/// Opaque reference.
|
||||
Any,
|
||||
|
||||
/// A string.
|
||||
String,
|
||||
|
||||
/// A sequence.
|
||||
Seq,
|
||||
|
||||
/// A 32-bits integer.
|
||||
I32,
|
||||
|
||||
/// A 64-bits integer.
|
||||
I64,
|
||||
|
||||
/// A 32-bits float.
|
||||
F32,
|
||||
|
||||
/// A 64-bits float.
|
||||
F64,
|
||||
|
||||
/// An `any` reference.
|
||||
AnyRef,
|
||||
}
|
||||
|
||||
/// Represents the kind of adapter.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) enum AdapterKind {
|
||||
/// An adapter defined for an imported function of a WebAssembly instance.
|
||||
Import,
|
||||
|
||||
/// An adapter defined for an exported function of a WebAssembly instance.
|
||||
Export,
|
||||
|
||||
/// A helper function.
|
||||
HelperFunction,
|
||||
}
|
||||
|
||||
/// Represents an exported function signature.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Export<'input> {
|
||||
/// The function name.
|
||||
pub name: &'input str,
|
||||
|
||||
/// The function input types.
|
||||
pub input_types: Vec<InterfaceType>,
|
||||
|
||||
/// The function output types.
|
||||
pub output_types: Vec<InterfaceType>,
|
||||
}
|
||||
|
||||
/// Represents an imported function signature.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Import<'input> {
|
||||
/// The function namespace.
|
||||
pub namespace: &'input str,
|
||||
|
||||
/// The function name.
|
||||
pub name: &'input str,
|
||||
|
||||
/// The function input types.
|
||||
pub input_types: Vec<InterfaceType>,
|
||||
|
||||
/// The function output types.
|
||||
pub output_types: Vec<InterfaceType>,
|
||||
}
|
||||
|
||||
/// Represents a structural type.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Type<'input> {
|
||||
/// The type name.
|
||||
pub name: &'input str,
|
||||
|
||||
/// The field names.
|
||||
field_names: Vec<&'input str>,
|
||||
|
||||
/// The field types.
|
||||
field_types: Vec<InterfaceType>,
|
||||
}
|
||||
|
||||
impl<'input> Type<'input> {
|
||||
/// Creates a new `Type`.
|
||||
///
|
||||
/// The constructor panics if there is the length of `names` is
|
||||
/// different than the length of `types`.
|
||||
pub fn new(type_name: &'input str, names: Vec<&'input str>, types: Vec<InterfaceType>) -> Self {
|
||||
assert_eq!(
|
||||
names.len(),
|
||||
types.len(),
|
||||
"There must be the same number of field names than field types."
|
||||
);
|
||||
|
||||
Self {
|
||||
name: type_name,
|
||||
field_names: names,
|
||||
field_types: types,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new field to the type.
|
||||
pub fn add_field(&mut self, name: &'input str, ty: InterfaceType) {
|
||||
self.field_names.push(name);
|
||||
self.field_types.push(ty);
|
||||
}
|
||||
|
||||
/// Returns the field names.
|
||||
pub fn field_names(&self) -> &Vec<&'input str> {
|
||||
&self.field_names
|
||||
}
|
||||
|
||||
/// Returns the field types.
|
||||
pub fn field_types(&self) -> &Vec<InterfaceType> {
|
||||
&self.field_types
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an adapter.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Adapter<'input> {
|
||||
/// An adapter for an imported function.
|
||||
Import {
|
||||
/// The function namespace.
|
||||
namespace: &'input str,
|
||||
|
||||
/// The function name.
|
||||
name: &'input str,
|
||||
|
||||
/// The function input types.
|
||||
input_types: Vec<InterfaceType>,
|
||||
|
||||
/// The function output types.
|
||||
output_types: Vec<InterfaceType>,
|
||||
|
||||
/// The instructions of the adapter.
|
||||
instructions: Vec<Instruction<'input>>,
|
||||
},
|
||||
|
||||
/// An adapter for an exported function.
|
||||
Export {
|
||||
/// The function name.
|
||||
name: &'input str,
|
||||
|
||||
/// The function input types.
|
||||
input_types: Vec<InterfaceType>,
|
||||
|
||||
/// The function output types.
|
||||
output_types: Vec<InterfaceType>,
|
||||
|
||||
/// The instructions of the adapter.
|
||||
instructions: Vec<Instruction<'input>>,
|
||||
},
|
||||
|
||||
/// An adapter for a helper function.
|
||||
HelperFunction {
|
||||
/// The helper name.
|
||||
name: &'input str,
|
||||
|
||||
/// The helper input types.
|
||||
input_types: Vec<InterfaceType>,
|
||||
|
||||
/// The helper output types.
|
||||
output_types: Vec<InterfaceType>,
|
||||
|
||||
/// The instructions of the adapter.
|
||||
instructions: Vec<Instruction<'input>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represented a forwarded export.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Forward<'input> {
|
||||
/// The forwarded export name.
|
||||
pub name: &'input str,
|
||||
}
|
||||
|
||||
/// Represents a set of interfaces, i.e. it entirely describes a WIT
|
||||
/// definition.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Interfaces<'input> {
|
||||
/// All the exported functions.
|
||||
pub exports: Vec<Export<'input>>,
|
||||
|
||||
/// All the types.
|
||||
pub types: Vec<Type<'input>>,
|
||||
|
||||
/// All the imported functions.
|
||||
pub imports: Vec<Import<'input>>,
|
||||
|
||||
/// All the adapters.
|
||||
pub adapters: Vec<Adapter<'input>>,
|
||||
|
||||
/// All the forwarded functions.
|
||||
pub forwards: Vec<Forward<'input>>,
|
||||
}
|
990
lib/interface-types/src/decoders/binary.rs
Normal file
990
lib/interface-types/src/decoders/binary.rs
Normal file
@ -0,0 +1,990 @@
|
||||
//! Parse the WIT binary representation into an AST.
|
||||
|
||||
use crate::{ast::*, interpreter::Instruction};
|
||||
use nom::{
|
||||
error::{make_error, ErrorKind, ParseError},
|
||||
Err, IResult,
|
||||
};
|
||||
use std::{convert::TryFrom, str};
|
||||
|
||||
/// Parse an `InterfaceType`.
|
||||
impl TryFrom<u64> for InterfaceType {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(code: u64) -> Result<Self, Self::Error> {
|
||||
Ok(match code {
|
||||
0x7fff => Self::Int,
|
||||
0x7ffe => Self::Float,
|
||||
0x7ffd => Self::Any,
|
||||
0x7ffc => Self::String,
|
||||
0x7ffb => Self::Seq,
|
||||
0x7f => Self::I32,
|
||||
0x7e => Self::I64,
|
||||
0x7d => Self::F32,
|
||||
0x7c => Self::F64,
|
||||
0x6f => Self::AnyRef,
|
||||
_ => return Err("Unknown interface type code."),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an adapter kind.
|
||||
impl TryFrom<u8> for AdapterKind {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(code: u8) -> Result<Self, Self::Error> {
|
||||
Ok(match code {
|
||||
0x0 => Self::Import,
|
||||
0x1 => Self::Export,
|
||||
0x2 => Self::HelperFunction,
|
||||
_ => return Err("Unknown adapter kind code."),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a byte.
|
||||
fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
Ok((&input[1..], input[0]))
|
||||
}
|
||||
|
||||
/// Parse an unsigned Little Endian Based (LEB) with value no larger
|
||||
/// than a 64-bits number. Read
|
||||
/// [LEB128](https://en.wikipedia.org/wiki/LEB128) to learn more, or
|
||||
/// the Variable Length Data Section from the [DWARF 4
|
||||
/// standard](http://dwarfstd.org/doc/DWARF4.pdf).
|
||||
fn uleb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) {
|
||||
Some(length) if length <= 8 => (&input[length + 1..], &input[..=length]),
|
||||
Some(_) => return Err(Err::Error(make_error(input, ErrorKind::TooLarge))),
|
||||
None => return Err(Err::Error(make_error(input, ErrorKind::Eof))),
|
||||
};
|
||||
|
||||
Ok((
|
||||
output,
|
||||
bytes
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0, |acc, byte| (acc << 7) | u64::from(byte & 0x7f)),
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a UTF-8 string.
|
||||
fn string<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
) -> IResult<&'input [u8], &'input str, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
let length = input[0] as usize;
|
||||
let input = &input[1..];
|
||||
|
||||
if input.len() < length {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
Ok((
|
||||
&input[length..],
|
||||
str::from_utf8(&input[..length])
|
||||
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a list, with a item parser.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn list<'input, I, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>,
|
||||
) -> IResult<&'input [u8], Vec<I>, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
let length = input[0] as usize;
|
||||
let mut input = &input[1..];
|
||||
|
||||
if input.len() < length {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
let mut items = Vec::with_capacity(length as usize);
|
||||
|
||||
for _ in 0..length {
|
||||
consume!((input, item) = item_parser(input)?);
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
Ok((input, items))
|
||||
}
|
||||
|
||||
/// Parse a type.
|
||||
fn ty<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
) -> IResult<&'input [u8], InterfaceType, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
let (output, ty) = uleb(input)?;
|
||||
|
||||
match InterfaceType::try_from(ty) {
|
||||
Ok(ty) => Ok((output, ty)),
|
||||
Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an instruction with its arguments.
|
||||
fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
) -> IResult<&'input [u8], Instruction, E> {
|
||||
let (mut input, opcode) = byte(input)?;
|
||||
|
||||
Ok(match opcode {
|
||||
0x00 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
(input, Instruction::ArgumentGet { index: argument_0 })
|
||||
}
|
||||
|
||||
0x01 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
(
|
||||
input,
|
||||
Instruction::Call {
|
||||
function_index: argument_0 as usize,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x02 => {
|
||||
consume!((input, argument_0) = string(input)?);
|
||||
(
|
||||
input,
|
||||
Instruction::CallExport {
|
||||
export_name: argument_0,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x03 => (input, Instruction::ReadUtf8),
|
||||
|
||||
0x04 => {
|
||||
consume!((input, argument_0) = string(input)?);
|
||||
(
|
||||
input,
|
||||
Instruction::WriteUtf8 {
|
||||
allocator_name: argument_0,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x05 => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
(input, Instruction::AsWasm(argument_0))
|
||||
}
|
||||
|
||||
0x06 => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
(input, Instruction::AsInterface(argument_0))
|
||||
}
|
||||
|
||||
0x07 => (input, Instruction::TableRefAdd),
|
||||
|
||||
0x08 => (input, Instruction::TableRefGet),
|
||||
|
||||
0x09 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
(input, Instruction::CallMethod(argument_0))
|
||||
}
|
||||
|
||||
0x0a => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
(input, Instruction::MakeRecord(argument_0))
|
||||
}
|
||||
|
||||
0x0c => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
consume!((input, argument_1) = uleb(input)?);
|
||||
(input, Instruction::GetField(argument_0, argument_1))
|
||||
}
|
||||
|
||||
0x0d => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
consume!((input, argument_1) = uleb(input)?);
|
||||
(input, Instruction::Const(argument_0, argument_1))
|
||||
}
|
||||
|
||||
0x0e => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
(input, Instruction::FoldSeq(argument_0))
|
||||
}
|
||||
|
||||
0x0f => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
(input, Instruction::Add(argument_0))
|
||||
}
|
||||
|
||||
0x10 => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
consume!((input, argument_1) = string(input)?);
|
||||
(input, Instruction::MemToSeq(argument_0, argument_1))
|
||||
}
|
||||
|
||||
0x11 => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
consume!((input, argument_1) = string(input)?);
|
||||
(input, Instruction::Load(argument_0, argument_1))
|
||||
}
|
||||
|
||||
0x12 => {
|
||||
consume!((input, argument_0) = ty(input)?);
|
||||
(input, Instruction::SeqNew(argument_0))
|
||||
}
|
||||
|
||||
0x13 => (input, Instruction::ListPush),
|
||||
|
||||
0x14 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
consume!((input, argument_1) = uleb(input)?);
|
||||
(input, Instruction::RepeatUntil(argument_0, argument_1))
|
||||
}
|
||||
|
||||
_ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a list of exports.
|
||||
fn exports<'input, E: ParseError<&'input [u8]>>(
|
||||
mut input: &'input [u8],
|
||||
) -> IResult<&'input [u8], Vec<Export>, E> {
|
||||
consume!((input, number_of_exports) = uleb(input)?);
|
||||
|
||||
let mut exports = Vec::with_capacity(number_of_exports as usize);
|
||||
|
||||
for _ in 0..number_of_exports {
|
||||
consume!((input, export_name) = string(input)?);
|
||||
consume!((input, export_input_types) = list(input, ty)?);
|
||||
consume!((input, export_output_types) = list(input, ty)?);
|
||||
|
||||
exports.push(Export {
|
||||
name: export_name,
|
||||
input_types: export_input_types,
|
||||
output_types: export_output_types,
|
||||
});
|
||||
}
|
||||
|
||||
Ok((input, exports))
|
||||
}
|
||||
|
||||
/// Parse a list of types.
|
||||
fn types<'input, E: ParseError<&'input [u8]>>(
|
||||
mut input: &'input [u8],
|
||||
) -> IResult<&'input [u8], Vec<Type>, E> {
|
||||
consume!((input, number_of_types) = uleb(input)?);
|
||||
|
||||
let mut types = Vec::with_capacity(number_of_types as usize);
|
||||
|
||||
for _ in 0..number_of_types {
|
||||
consume!((input, type_name) = string(input)?);
|
||||
consume!((input, type_fields) = list(input, string)?);
|
||||
consume!((input, type_types) = list(input, ty)?);
|
||||
|
||||
types.push(Type::new(type_name, type_fields, type_types));
|
||||
}
|
||||
|
||||
Ok((input, types))
|
||||
}
|
||||
|
||||
/// Parse a list of imports.
|
||||
fn imports<'input, E: ParseError<&'input [u8]>>(
|
||||
mut input: &'input [u8],
|
||||
) -> IResult<&'input [u8], Vec<Import>, E> {
|
||||
consume!((input, number_of_imports) = uleb(input)?);
|
||||
|
||||
let mut imports = Vec::with_capacity(number_of_imports as usize);
|
||||
|
||||
for _ in 0..number_of_imports {
|
||||
consume!((input, import_namespace) = string(input)?);
|
||||
consume!((input, import_name) = string(input)?);
|
||||
consume!((input, import_input_types) = list(input, ty)?);
|
||||
consume!((input, import_output_types) = list(input, ty)?);
|
||||
|
||||
imports.push(Import {
|
||||
namespace: import_namespace,
|
||||
name: import_name,
|
||||
input_types: import_input_types,
|
||||
output_types: import_output_types,
|
||||
});
|
||||
}
|
||||
|
||||
Ok((input, imports))
|
||||
}
|
||||
|
||||
/// Parse a list of adapters.
|
||||
fn adapters<'input, E: ParseError<&'input [u8]>>(
|
||||
mut input: &'input [u8],
|
||||
) -> IResult<&'input [u8], Vec<Adapter>, E> {
|
||||
consume!((input, number_of_adapters) = uleb(input)?);
|
||||
|
||||
let mut adapters = Vec::with_capacity(number_of_adapters as usize);
|
||||
|
||||
for _ in 0..number_of_adapters {
|
||||
consume!((input, adapter_kind) = byte(input)?);
|
||||
let adapter_kind = AdapterKind::try_from(adapter_kind)
|
||||
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?;
|
||||
|
||||
match adapter_kind {
|
||||
AdapterKind::Import => {
|
||||
consume!((input, adapter_namespace) = string(input)?);
|
||||
consume!((input, adapter_name) = string(input)?);
|
||||
consume!((input, adapter_input_types) = list(input, ty)?);
|
||||
consume!((input, adapter_output_types) = list(input, ty)?);
|
||||
consume!((input, adapter_instructions) = list(input, instruction)?);
|
||||
|
||||
adapters.push(Adapter::Import {
|
||||
namespace: adapter_namespace,
|
||||
name: adapter_name,
|
||||
input_types: adapter_input_types,
|
||||
output_types: adapter_output_types,
|
||||
instructions: adapter_instructions,
|
||||
});
|
||||
}
|
||||
|
||||
AdapterKind::Export => {
|
||||
consume!((input, adapter_name) = string(input)?);
|
||||
consume!((input, adapter_input_types) = list(input, ty)?);
|
||||
consume!((input, adapter_output_types) = list(input, ty)?);
|
||||
consume!((input, adapter_instructions) = list(input, instruction)?);
|
||||
|
||||
adapters.push(Adapter::Export {
|
||||
name: adapter_name,
|
||||
input_types: adapter_input_types,
|
||||
output_types: adapter_output_types,
|
||||
instructions: adapter_instructions,
|
||||
});
|
||||
}
|
||||
|
||||
AdapterKind::HelperFunction => {
|
||||
consume!((input, adapter_name) = string(input)?);
|
||||
consume!((input, adapter_input_types) = list(input, ty)?);
|
||||
consume!((input, adapter_output_types) = list(input, ty)?);
|
||||
consume!((input, adapter_instructions) = list(input, instruction)?);
|
||||
|
||||
adapters.push(Adapter::HelperFunction {
|
||||
name: adapter_name,
|
||||
input_types: adapter_input_types,
|
||||
output_types: adapter_output_types,
|
||||
instructions: adapter_instructions,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((input, adapters))
|
||||
}
|
||||
|
||||
/// Parse a list of forwarded exports.
|
||||
fn forwards<'input, E: ParseError<&'input [u8]>>(
|
||||
mut input: &'input [u8],
|
||||
) -> IResult<&'input [u8], Vec<Forward>, E> {
|
||||
consume!((input, number_of_forwards) = uleb(input)?);
|
||||
|
||||
let mut forwards = Vec::with_capacity(number_of_forwards as usize);
|
||||
|
||||
for _ in 0..number_of_forwards {
|
||||
consume!((input, forward_name) = string(input)?);
|
||||
|
||||
forwards.push(Forward { name: forward_name });
|
||||
}
|
||||
|
||||
Ok((input, forwards))
|
||||
}
|
||||
|
||||
/// Parse complete interfaces.
|
||||
fn interfaces<'input, E: ParseError<&'input [u8]>>(
|
||||
bytes: &'input [u8],
|
||||
) -> IResult<&'input [u8], Interfaces, E> {
|
||||
let mut input = bytes;
|
||||
|
||||
consume!((input, exports) = exports(input)?);
|
||||
consume!((input, types) = types(input)?);
|
||||
consume!((input, imports) = imports(input)?);
|
||||
consume!((input, adapters) = adapters(input)?);
|
||||
consume!((input, forwards) = forwards(input)?);
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Interfaces {
|
||||
exports,
|
||||
types,
|
||||
imports,
|
||||
adapters,
|
||||
forwards,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a sequence of bytes, expecting it to be a valid WIT binary
|
||||
/// representation, into an `ast::Interfaces`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use wasmer_interface_types::{
|
||||
/// ast::*,
|
||||
/// decoders::binary::parse,
|
||||
/// interpreter::Instruction,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let input = &[
|
||||
/// 0x01, // 1 export
|
||||
/// 0x02, // string of 2 bytes
|
||||
/// 0x61, 0x62, // "a", "b"
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x7f, // I32
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x7f, // I32
|
||||
/// 0x01, // 1 type
|
||||
/// 0x02, // string of 2 bytes
|
||||
/// 0x61, 0x62, // "a", "b"
|
||||
/// 0x02, // list of 2 items
|
||||
/// 0x02, // string of 2 bytes
|
||||
/// 0x63, 0x64, // "c", "d"
|
||||
/// 0x01, // string of 1 byte
|
||||
/// 0x65, // "e"
|
||||
/// 0x02, // list of 2 items
|
||||
/// 0x7f, // I32
|
||||
/// 0x7f, // I32
|
||||
/// 0x01, // 1 import
|
||||
/// 0x01, // string of 1 byte
|
||||
/// 0x61, // "a"
|
||||
/// 0x01, // string of 1 byte
|
||||
/// 0x62, // "b"
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x7f, // I32
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x7e, // I64
|
||||
/// 0x01, // 1 adapter
|
||||
/// 0x00, // adapter kind: import
|
||||
/// 0x01, // string of 1 byte
|
||||
/// 0x61, // "a"
|
||||
/// 0x01, // string of 1 byte
|
||||
/// 0x62, // "b"
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x7f, // I32
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x7f, // I32
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
/// 0x01, // 1 adapter
|
||||
/// 0x01, // string of 1 byte
|
||||
/// 0x61, // "a"
|
||||
/// ];
|
||||
/// let output = Ok((
|
||||
/// &[] as &[u8],
|
||||
/// Interfaces {
|
||||
/// exports: vec![Export {
|
||||
/// name: "ab",
|
||||
/// input_types: vec![InterfaceType::I32],
|
||||
/// output_types: vec![InterfaceType::I32],
|
||||
/// }],
|
||||
/// types: vec![Type::new(
|
||||
/// "ab",
|
||||
/// vec!["cd", "e"],
|
||||
/// vec![InterfaceType::I32, InterfaceType::I32],
|
||||
/// )],
|
||||
/// imports: vec![Import {
|
||||
/// namespace: "a",
|
||||
/// name: "b",
|
||||
/// input_types: vec![InterfaceType::I32],
|
||||
/// output_types: vec![InterfaceType::I64],
|
||||
/// }],
|
||||
/// adapters: vec![Adapter::Import {
|
||||
/// namespace: "a",
|
||||
/// name: "b",
|
||||
/// input_types: vec![InterfaceType::I32],
|
||||
/// output_types: vec![InterfaceType::I32],
|
||||
/// instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
/// }],
|
||||
/// forwards: vec![Forward { name: "a" }],
|
||||
/// },
|
||||
/// ));
|
||||
///
|
||||
/// assert_eq!(parse::<()>(input), output);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn parse<'input, E: ParseError<&'input [u8]>>(
|
||||
bytes: &'input [u8],
|
||||
) -> IResult<&'input [u8], Interfaces, E> {
|
||||
interfaces(bytes)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nom::{error, Err};
|
||||
|
||||
#[test]
|
||||
fn test_byte() {
|
||||
let input = &[0x01, 0x02, 0x03];
|
||||
let output = Ok((&[0x02, 0x03][..], 0x01u8));
|
||||
|
||||
assert_eq!(byte::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uleb_1_byte() {
|
||||
let input = &[0x01, 0x02, 0x03];
|
||||
let output = Ok((&[0x02, 0x03][..], 0x01u64));
|
||||
|
||||
assert_eq!(uleb::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uleb_3_bytes() {
|
||||
let input = &[0xfc, 0xff, 0x01, 0x02];
|
||||
let output = Ok((&[0x02][..], 0x7ffcu64));
|
||||
|
||||
assert_eq!(uleb::<()>(input), output);
|
||||
}
|
||||
|
||||
// Examples from Figure 22 of [DWARF 4
|
||||
// standard](http://dwarfstd.org/doc/DWARF4.pdf).
|
||||
#[test]
|
||||
fn test_uleb_from_dwarf_standard() {
|
||||
macro_rules! assert_uleb {
|
||||
($to_parse:expr => $expected_result:expr) => {
|
||||
assert_eq!(uleb::<()>($to_parse), Ok((&[][..], $expected_result)));
|
||||
};
|
||||
}
|
||||
|
||||
assert_uleb!(&[2u8] => 2u64);
|
||||
assert_uleb!(&[127u8] => 127u64);
|
||||
assert_uleb!(&[0x80, 1u8] => 128u64);
|
||||
assert_uleb!(&[1u8 | 0x80, 1] => 129u64);
|
||||
assert_uleb!(&[2u8 | 0x80, 1] => 130u64);
|
||||
assert_uleb!(&[57u8 | 0x80, 100] => 12857u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uleb_eof() {
|
||||
let input = &[0x80];
|
||||
|
||||
assert_eq!(
|
||||
uleb::<(&[u8], error::ErrorKind)>(input),
|
||||
Err(Err::Error((&input[..], error::ErrorKind::Eof))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uleb_overflow() {
|
||||
let input = &[
|
||||
0x01 | 0x80,
|
||||
0x02 | 0x80,
|
||||
0x03 | 0x80,
|
||||
0x04 | 0x80,
|
||||
0x05 | 0x80,
|
||||
0x06 | 0x80,
|
||||
0x07 | 0x80,
|
||||
0x08 | 0x80,
|
||||
0x09 | 0x80,
|
||||
0x0a,
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
uleb::<(&[u8], error::ErrorKind)>(input),
|
||||
Err(Err::Error((&input[..], error::ErrorKind::TooLarge))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let input = &[
|
||||
0x03, // string of 3 bytes
|
||||
0x61, // "a"
|
||||
0x62, // "b"
|
||||
0x63, // "c"
|
||||
0x64, 0x65,
|
||||
];
|
||||
let output = Ok((&[0x64, 0x65][..], "abc"));
|
||||
|
||||
assert_eq!(string::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list() {
|
||||
let input = &[
|
||||
0x02, // list of 2 items
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x02, // string of 2 bytes
|
||||
0x62, // "b"
|
||||
0x63, // "c"
|
||||
0x07,
|
||||
];
|
||||
let output = Ok((&[0x07][..], vec!["a", "bc"]));
|
||||
|
||||
assert_eq!(list::<&str, ()>(input, string), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ty() {
|
||||
let input = &[
|
||||
0x0a, // list of 10 items
|
||||
0xff, 0xff, 0x01, // Int
|
||||
0xfe, 0xff, 0x01, // Float
|
||||
0xfd, 0xff, 0x01, // Any
|
||||
0xfc, 0xff, 0x01, // String
|
||||
0xfb, 0xff, 0x01, // Seq
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
0x7d, // F32
|
||||
0x7c, // F64
|
||||
0x6f, // AnyRef
|
||||
0x01,
|
||||
];
|
||||
let output = Ok((
|
||||
&[0x01][..],
|
||||
vec![
|
||||
InterfaceType::Int,
|
||||
InterfaceType::Float,
|
||||
InterfaceType::Any,
|
||||
InterfaceType::String,
|
||||
InterfaceType::Seq,
|
||||
InterfaceType::I32,
|
||||
InterfaceType::I64,
|
||||
InterfaceType::F32,
|
||||
InterfaceType::F64,
|
||||
InterfaceType::AnyRef,
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(list::<InterfaceType, ()>(input, ty), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instructions() {
|
||||
let input = &[
|
||||
0x14, // list of 20 items
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, 0x01, // Call { function_index: 1 }
|
||||
0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" }
|
||||
0x03, // ReadUtf8
|
||||
0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" }
|
||||
0x05, 0xff, 0xff, 0x01, // AsWasm(Int)
|
||||
0x06, 0x7e, // AsInterface(I64)
|
||||
0x07, // TableRefAdd
|
||||
0x08, // TableRefGet
|
||||
0x09, 0x01, // CallMethod(1)
|
||||
0x0a, 0x7f, // MakeRecord(I32)
|
||||
0x0c, 0xff, 0xff, 0x01, 0x02, // GetField(Int, 2)
|
||||
0x0d, 0x7f, 0x01, // Const(I32, 1)
|
||||
0x0e, 0x01, // FoldSeq(1)
|
||||
0x0f, 0x7f, // Add(I32)
|
||||
0x10, 0x7f, 0x03, 0x61, 0x62, 0x63, // MemToSeq(I32, "abc")
|
||||
0x11, 0x7f, 0x03, 0x61, 0x62, 0x63, // Load(I32, "abc")
|
||||
0x12, 0x7f, // SeqNew(I32)
|
||||
0x13, // ListPush
|
||||
0x14, 0x01, 0x02, // RepeatUntil(1, 2)
|
||||
0x0a,
|
||||
];
|
||||
let output = Ok((
|
||||
&[0x0a][..],
|
||||
vec![
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::Call { function_index: 1 },
|
||||
Instruction::CallExport { export_name: "abc" },
|
||||
Instruction::ReadUtf8,
|
||||
Instruction::WriteUtf8 {
|
||||
allocator_name: "abc",
|
||||
},
|
||||
Instruction::AsWasm(InterfaceType::Int),
|
||||
Instruction::AsInterface(InterfaceType::I64),
|
||||
Instruction::TableRefAdd,
|
||||
Instruction::TableRefGet,
|
||||
Instruction::CallMethod(1),
|
||||
Instruction::MakeRecord(InterfaceType::I32),
|
||||
Instruction::GetField(InterfaceType::Int, 2),
|
||||
Instruction::Const(InterfaceType::I32, 1),
|
||||
Instruction::FoldSeq(1),
|
||||
Instruction::Add(InterfaceType::I32),
|
||||
Instruction::MemToSeq(InterfaceType::I32, "abc"),
|
||||
Instruction::Load(InterfaceType::I32, "abc"),
|
||||
Instruction::SeqNew(InterfaceType::I32),
|
||||
Instruction::ListPush,
|
||||
Instruction::RepeatUntil(1, 2),
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(list::<Instruction, ()>(input, instruction), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exports() {
|
||||
let input = &[
|
||||
0x02, // 2 exports
|
||||
0x02, // string of 2 bytes
|
||||
0x61, 0x62, // "a", "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x02, // string of 2 bytes
|
||||
0x63, 0x64, // "c", "d"
|
||||
0x00, // list of 0 item
|
||||
0x00, // list of 0 item
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
vec![
|
||||
Export {
|
||||
name: "ab",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
},
|
||||
Export {
|
||||
name: "cd",
|
||||
input_types: vec![],
|
||||
output_types: vec![],
|
||||
},
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(exports::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_types() {
|
||||
let input = &[
|
||||
0x01, // 1 type
|
||||
0x02, // string of 2 bytes
|
||||
0x61, 0x62, // "a", "b"
|
||||
0x02, // list of 2 items
|
||||
0x02, // string of 2 bytes
|
||||
0x63, 0x64, // "c", "d"
|
||||
0x01, // string of 1 byte
|
||||
0x65, // "e"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7f, // I32
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
vec![Type::new(
|
||||
"ab",
|
||||
vec!["cd", "e"],
|
||||
vec![InterfaceType::I32, InterfaceType::I32],
|
||||
)],
|
||||
));
|
||||
|
||||
assert_eq!(types::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imports() {
|
||||
let input = &[
|
||||
0x02, // 2 imports
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7e, // I64
|
||||
0x01, // string of 1 byte
|
||||
0x63, // "c"
|
||||
0x01, // string of 1 byte
|
||||
0x64, // "d"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7e, // I64
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
vec![
|
||||
Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I64],
|
||||
},
|
||||
Import {
|
||||
namespace: "c",
|
||||
name: "d",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I64],
|
||||
},
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(imports::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adapters() {
|
||||
let input = &[
|
||||
0x03, // 3 adapters
|
||||
0x00, // adapter kind: import
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, // adapter kind: export
|
||||
0x01, // string of 1 byte
|
||||
0x63, // "c"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x02, // adapter kind: helper function
|
||||
0x01, // string of 1 byte
|
||||
0x64, // "d"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
vec![
|
||||
Adapter::Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
},
|
||||
Adapter::Export {
|
||||
name: "c",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
},
|
||||
Adapter::HelperFunction {
|
||||
name: "d",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
},
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(adapters::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_forwards() {
|
||||
let input = &[
|
||||
0x02, // 2 adapters
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x02, // string of 2 bytes
|
||||
0x62, 0x63, // "b", "c"
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
vec![Forward { name: "a" }, Forward { name: "bc" }],
|
||||
));
|
||||
|
||||
assert_eq!(forwards::<()>(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let input = &[
|
||||
0x01, // 1 export
|
||||
0x02, // string of 2 bytes
|
||||
0x61, 0x62, // "a", "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // 1 type
|
||||
0x02, // string of 2 bytes
|
||||
0x61, 0x62, // "a", "b"
|
||||
0x02, // list of 2 items
|
||||
0x02, // string of 2 bytes
|
||||
0x63, 0x64, // "c", "d"
|
||||
0x01, // string of 1 byte
|
||||
0x65, // "e"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7f, // I32
|
||||
0x01, // 1 import
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7e, // I64
|
||||
0x01, // 1 adapter
|
||||
0x00, // adapter kind: import
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, // 1 adapter
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
Interfaces {
|
||||
exports: vec![Export {
|
||||
name: "ab",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
}],
|
||||
types: vec![Type::new(
|
||||
"ab",
|
||||
vec!["cd", "e"],
|
||||
vec![InterfaceType::I32, InterfaceType::I32],
|
||||
)],
|
||||
imports: vec![Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I64],
|
||||
}],
|
||||
adapters: vec![Adapter::Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
}],
|
||||
forwards: vec![Forward { name: "a" }],
|
||||
},
|
||||
));
|
||||
|
||||
assert_eq!(interfaces::<()>(input), output);
|
||||
}
|
||||
}
|
4
lib/interface-types/src/decoders/mod.rs
Normal file
4
lib/interface-types/src/decoders/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! Reads the AST from a particular data representation; for instance,
|
||||
//! `decoders::binary` reads the AST from a binary.
|
||||
|
||||
pub mod binary;
|
749
lib/interface-types/src/encoders/binary.rs
Normal file
749
lib/interface-types/src/encoders/binary.rs
Normal file
@ -0,0 +1,749 @@
|
||||
//! Writes the AST into bytes representing WIT with its binary format.
|
||||
|
||||
use crate::{
|
||||
ast::{Adapter, AdapterKind, Export, Forward, Import, InterfaceType, Interfaces, Type},
|
||||
interpreter::Instruction,
|
||||
};
|
||||
use std::io::{self, Write};
|
||||
|
||||
/// A trait for converting a value to bytes.
|
||||
pub trait ToBytes<W>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
/// Converts the given value into `&[u8]` in the given `writer`.
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()>;
|
||||
}
|
||||
|
||||
/// Encode a `u8` into a byte (well, it's already a byte!).
|
||||
impl<W> ToBytes<W> for u8
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&[*self])
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `u64` into bytes with a LEB128 representation.
|
||||
///
|
||||
/// Decoder is `decoders::binary::uleb`.
|
||||
impl<W> ToBytes<W> for u64
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
let mut value = *self;
|
||||
|
||||
// Code adapted from the Rust' `serialize` library.
|
||||
loop {
|
||||
if value < 0x80 {
|
||||
writer.write_all(&[value as u8])?;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
writer.write_all(&[((value & 0x7f) | 0x80) as u8])?;
|
||||
value >>= 7;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `str` into bytes.
|
||||
///
|
||||
/// Decoder is `decoders::binary::string`.
|
||||
impl<W> ToBytes<W> for &str
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
// Size first.
|
||||
writer.write_all(&[self.len() as u8])?;
|
||||
|
||||
// Then the string.
|
||||
writer.write_all(self.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a vector into bytes.
|
||||
///
|
||||
/// Decoder is `decoders::binary::list`.
|
||||
impl<W, I> ToBytes<W> for Vec<I>
|
||||
where
|
||||
W: Write,
|
||||
I: ToBytes<W>,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
// Size first.
|
||||
(self.len() as u64).to_bytes(writer)?;
|
||||
|
||||
// Then the items.
|
||||
for item in self {
|
||||
item.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `InterfaceType` into bytes.
|
||||
impl<W> ToBytes<W> for InterfaceType
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
InterfaceType::Int => 0x7fff_u64.to_bytes(writer),
|
||||
InterfaceType::Float => 0x7ffe_u64.to_bytes(writer),
|
||||
InterfaceType::Any => 0x7ffd_u64.to_bytes(writer),
|
||||
InterfaceType::String => 0x7ffc_u64.to_bytes(writer),
|
||||
InterfaceType::Seq => 0x7ffb_u64.to_bytes(writer),
|
||||
InterfaceType::I32 => 0x7f_u64.to_bytes(writer),
|
||||
InterfaceType::I64 => 0x7e_u64.to_bytes(writer),
|
||||
InterfaceType::F32 => 0x7d_u64.to_bytes(writer),
|
||||
InterfaceType::F64 => 0x7c_u64.to_bytes(writer),
|
||||
InterfaceType::AnyRef => 0x6f_u64.to_bytes(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `AdapterKind` into bytes.
|
||||
impl<W> ToBytes<W> for AdapterKind
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
AdapterKind::Import => 0x00_u8.to_bytes(writer),
|
||||
AdapterKind::Export => 0x01_u8.to_bytes(writer),
|
||||
AdapterKind::HelperFunction => 0x02_u8.to_bytes(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Export` into bytes.
|
||||
///
|
||||
/// Decoder is in `decoders::binary::exports`.
|
||||
impl<W> ToBytes<W> for Export<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.name.to_bytes(writer)?;
|
||||
self.input_types.to_bytes(writer)?;
|
||||
self.output_types.to_bytes(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `Type` into bytes.
|
||||
///
|
||||
/// Decoder is in `decoders::binary::types`.
|
||||
impl<W> ToBytes<W> for Type<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.name.to_bytes(writer)?;
|
||||
self.field_names().to_bytes(writer)?;
|
||||
self.field_types().to_bytes(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Import` into bytes.
|
||||
///
|
||||
/// Decoder is in `decoders::binary::imports`.
|
||||
impl<W> ToBytes<W> for Import<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.namespace.to_bytes(writer)?;
|
||||
self.name.to_bytes(writer)?;
|
||||
self.input_types.to_bytes(writer)?;
|
||||
self.output_types.to_bytes(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Adapter` into bytes.
|
||||
///
|
||||
/// Decoder is in `decoders::binary::imports`.
|
||||
impl<W> ToBytes<W> for Adapter<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Adapter::Import {
|
||||
namespace,
|
||||
name,
|
||||
input_types,
|
||||
output_types,
|
||||
instructions,
|
||||
} => {
|
||||
AdapterKind::Import.to_bytes(writer)?;
|
||||
namespace.to_bytes(writer)?;
|
||||
name.to_bytes(writer)?;
|
||||
input_types.to_bytes(writer)?;
|
||||
output_types.to_bytes(writer)?;
|
||||
instructions.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Adapter::Export {
|
||||
name,
|
||||
input_types,
|
||||
output_types,
|
||||
instructions,
|
||||
} => {
|
||||
AdapterKind::Export.to_bytes(writer)?;
|
||||
name.to_bytes(writer)?;
|
||||
input_types.to_bytes(writer)?;
|
||||
output_types.to_bytes(writer)?;
|
||||
instructions.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Adapter::HelperFunction {
|
||||
name,
|
||||
input_types,
|
||||
output_types,
|
||||
instructions,
|
||||
} => {
|
||||
AdapterKind::HelperFunction.to_bytes(writer)?;
|
||||
name.to_bytes(writer)?;
|
||||
input_types.to_bytes(writer)?;
|
||||
output_types.to_bytes(writer)?;
|
||||
instructions.to_bytes(writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Forward` into bytes.
|
||||
///
|
||||
/// Decoder is `decoders::binary::forwards`.
|
||||
impl<W> ToBytes<W> for Forward<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.name.to_bytes(writer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Interfaces` into bytes.
|
||||
///
|
||||
/// Decoder is `decoders::binary::parse`.
|
||||
impl<W> ToBytes<W> for Interfaces<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.exports.to_bytes(writer)?;
|
||||
self.types.to_bytes(writer)?;
|
||||
self.imports.to_bytes(writer)?;
|
||||
self.adapters.to_bytes(writer)?;
|
||||
self.forwards.to_bytes(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Instruction` into bytes.
|
||||
///
|
||||
/// Decoder is `decoders::binary::instruction`.
|
||||
impl<W> ToBytes<W> for Instruction<'_>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Instruction::ArgumentGet { index } => {
|
||||
0x00_u8.to_bytes(writer)?;
|
||||
index.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::Call { function_index } => {
|
||||
0x01_u8.to_bytes(writer)?;
|
||||
(*function_index as u64).to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::CallExport { export_name } => {
|
||||
0x02_u8.to_bytes(writer)?;
|
||||
export_name.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::ReadUtf8 => 0x03_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::WriteUtf8 { allocator_name } => {
|
||||
0x04_u8.to_bytes(writer)?;
|
||||
allocator_name.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::AsWasm(interface_type) => {
|
||||
0x05_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::AsInterface(interface_type) => {
|
||||
0x06_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::TableRefAdd => 0x07_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::TableRefGet => 0x08_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::CallMethod(function_index) => {
|
||||
0x09_u8.to_bytes(writer)?;
|
||||
function_index.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::MakeRecord(interface_type) => {
|
||||
0x0a_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::GetField(interface_type, field_index) => {
|
||||
0x0c_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
field_index.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::Const(interface_type, index) => {
|
||||
0x0d_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
index.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::FoldSeq(index) => {
|
||||
0x0e_u8.to_bytes(writer)?;
|
||||
index.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::Add(interface_type) => {
|
||||
0x0f_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::MemToSeq(interface_type, string) => {
|
||||
0x10_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
string.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::Load(interface_type, string) => {
|
||||
0x11_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
string.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::SeqNew(interface_type) => {
|
||||
0x12_u8.to_bytes(writer)?;
|
||||
interface_type.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::ListPush => 0x13_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::RepeatUntil(index1, index2) => {
|
||||
0x14_u8.to_bytes(writer)?;
|
||||
index1.to_bytes(writer)?;
|
||||
index2.to_bytes(writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! assert_to_bytes {
|
||||
($expr:expr, $expected_output:expr) => {{
|
||||
let mut output = vec![];
|
||||
|
||||
$expr.to_bytes(&mut output).expect(concat!(
|
||||
"Unable to encode the expression `",
|
||||
stringify!($expr),
|
||||
"` to bytes."
|
||||
));
|
||||
|
||||
assert_eq!(output.as_slice(), &$expected_output[..]);
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u8() {
|
||||
assert_to_bytes!(0x01_u8, &[0x01]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uleb_1_byte() {
|
||||
assert_to_bytes!(0x01_u64, &[0x01]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uleb_3_bytes() {
|
||||
assert_to_bytes!(0x7ffc_u64, &[0xfc, 0xff, 0x01]);
|
||||
}
|
||||
|
||||
// Examples from Figure 22 of [DWARF 4
|
||||
// standard](http://dwarfstd.org/doc/DWARF4.pdf).
|
||||
#[test]
|
||||
fn test_uleb_from_dward_standard() {
|
||||
assert_to_bytes!(2u64, &[2u8]);
|
||||
assert_to_bytes!(127u64, &[127u8]);
|
||||
assert_to_bytes!(128u64, &[0x80, 1u8]);
|
||||
assert_to_bytes!(129u64, &[1u8 | 0x80, 1]);
|
||||
assert_to_bytes!(130u64, &[2u8 | 0x80, 1]);
|
||||
assert_to_bytes!(12857u64, &[57u8 | 0x80, 100]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_str() {
|
||||
assert_to_bytes!("", &[0x00]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str() {
|
||||
assert_to_bytes!("abc", &[0x03, 0x61, 0x62, 0x63]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_vec() {
|
||||
assert_to_bytes!(Vec::<u8>::new(), &[0x00]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec() {
|
||||
assert_to_bytes!(
|
||||
vec!["a", "b", "c"],
|
||||
&[
|
||||
0x03, // list of 3 items
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // string of 1 byte
|
||||
0x63, // "c"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interface_type() {
|
||||
assert_to_bytes!(InterfaceType::Int, &[0xff, 0xff, 0x01]);
|
||||
assert_to_bytes!(InterfaceType::Float, &[0xfe, 0xff, 0x01]);
|
||||
assert_to_bytes!(InterfaceType::Any, &[0xfd, 0xff, 0x01]);
|
||||
assert_to_bytes!(InterfaceType::String, &[0xfc, 0xff, 0x01]);
|
||||
assert_to_bytes!(InterfaceType::Seq, &[0xfb, 0xff, 0x01]);
|
||||
assert_to_bytes!(InterfaceType::I32, &[0x7f]);
|
||||
assert_to_bytes!(InterfaceType::I64, &[0x7e]);
|
||||
assert_to_bytes!(InterfaceType::F32, &[0x7d]);
|
||||
assert_to_bytes!(InterfaceType::F64, &[0x7c]);
|
||||
assert_to_bytes!(InterfaceType::AnyRef, &[0x6f]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adapter_kind() {
|
||||
assert_to_bytes!(AdapterKind::Import, &[0x00]);
|
||||
assert_to_bytes!(AdapterKind::Export, &[0x01]);
|
||||
assert_to_bytes!(AdapterKind::HelperFunction, &[0x02]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_export() {
|
||||
assert_to_bytes!(
|
||||
Export {
|
||||
name: "abc",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::I64],
|
||||
output_types: vec![InterfaceType::I32]
|
||||
},
|
||||
&[
|
||||
0x03, // string of length 3
|
||||
0x61, // "a"
|
||||
0x62, // "b"
|
||||
0x63, // "c"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
0x01, // list of 1 items
|
||||
0x7f, // I32
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type() {
|
||||
assert_to_bytes!(
|
||||
Type::new(
|
||||
"a",
|
||||
vec!["b", "c"],
|
||||
vec![InterfaceType::I32, InterfaceType::I64],
|
||||
),
|
||||
&[
|
||||
0x01, // string of length 1
|
||||
0x61, // "a"
|
||||
0x02, // list of 2 items
|
||||
0x01, // string of length 1
|
||||
0x62, // "b"
|
||||
0x01, // string of length 1
|
||||
0x63, // "c"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import() {
|
||||
assert_to_bytes!(
|
||||
Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::I64],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
},
|
||||
&[
|
||||
0x01, // string of length 1
|
||||
0x61, // "a"
|
||||
0x01, // string of length 1
|
||||
0x62, // "b"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
0x01, // list of 1 items
|
||||
0x7f, // I32
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adapter_import() {
|
||||
assert_to_bytes!(
|
||||
Adapter::Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::I64],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
},
|
||||
&[
|
||||
0x00, // AdapterKind::Import
|
||||
0x01, // string of length 1
|
||||
0x61, // "a"
|
||||
0x01, // string of length 1
|
||||
0x62, // "b"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
0x01, // list of 1 items
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adapter_export() {
|
||||
assert_to_bytes!(
|
||||
Adapter::Export {
|
||||
name: "a",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::I64],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
},
|
||||
&[
|
||||
0x01, // AdapterKind::Export
|
||||
0x01, // string of length 1
|
||||
0x61, // "a"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
0x01, // list of 1 items
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adapter_helper_function() {
|
||||
assert_to_bytes!(
|
||||
Adapter::HelperFunction {
|
||||
name: "a",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::I64],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
},
|
||||
&[
|
||||
0x02, // AdapterKind::HelperFunction
|
||||
0x01, // string of length 1
|
||||
0x61, // "a"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7e, // I64
|
||||
0x01, // list of 1 items
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_forward() {
|
||||
assert_to_bytes!(
|
||||
Forward { name: "ab" },
|
||||
&[
|
||||
0x02, // string of length 2
|
||||
0x61, // "a"
|
||||
0x62, // "b"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interfaces() {
|
||||
assert_to_bytes!(
|
||||
Interfaces {
|
||||
exports: vec![Export {
|
||||
name: "ab",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
}],
|
||||
types: vec![Type::new(
|
||||
"ab",
|
||||
vec!["cd", "e"],
|
||||
vec![InterfaceType::I32, InterfaceType::I32],
|
||||
)],
|
||||
imports: vec![Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I64],
|
||||
}],
|
||||
adapters: vec![Adapter::Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
}],
|
||||
forwards: vec![Forward { name: "a" }],
|
||||
},
|
||||
&[
|
||||
0x01, // 1 export
|
||||
0x02, // string of 2 bytes
|
||||
0x61, 0x62, // "a", "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // 1 type
|
||||
0x02, // string of 2 bytes
|
||||
0x61, 0x62, // "a", "b"
|
||||
0x02, // list of 2 items
|
||||
0x02, // string of 2 bytes
|
||||
0x63, 0x64, // "c", "d"
|
||||
0x01, // string of 1 byte
|
||||
0x65, // "e"
|
||||
0x02, // list of 2 items
|
||||
0x7f, // I32
|
||||
0x7f, // I32
|
||||
0x01, // 1 import
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7e, // I64
|
||||
0x01, // 1 adapter
|
||||
0x00, // adapter kind: import
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
0x01, // string of 1 byte
|
||||
0x62, // "b"
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x7f, // I32
|
||||
0x01, // list of 1 item
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, // 1 adapter
|
||||
0x01, // string of 1 byte
|
||||
0x61, // "a"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instructions() {
|
||||
assert_to_bytes!(
|
||||
vec![
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::Call { function_index: 1 },
|
||||
Instruction::CallExport { export_name: "abc" },
|
||||
Instruction::ReadUtf8,
|
||||
Instruction::WriteUtf8 {
|
||||
allocator_name: "abc",
|
||||
},
|
||||
Instruction::AsWasm(InterfaceType::Int),
|
||||
Instruction::AsInterface(InterfaceType::I64),
|
||||
Instruction::TableRefAdd,
|
||||
Instruction::TableRefGet,
|
||||
Instruction::CallMethod(1),
|
||||
Instruction::MakeRecord(InterfaceType::I32),
|
||||
Instruction::GetField(InterfaceType::Int, 2),
|
||||
Instruction::Const(InterfaceType::I32, 1),
|
||||
Instruction::FoldSeq(1),
|
||||
Instruction::Add(InterfaceType::I32),
|
||||
Instruction::MemToSeq(InterfaceType::I32, "abc"),
|
||||
Instruction::Load(InterfaceType::I32, "abc"),
|
||||
Instruction::SeqNew(InterfaceType::I32),
|
||||
Instruction::ListPush,
|
||||
Instruction::RepeatUntil(1, 2),
|
||||
],
|
||||
&[
|
||||
0x14, // list of 20 items
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, 0x01, // Call { function_index: 1 }
|
||||
0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" }
|
||||
0x03, // ReadUtf8
|
||||
0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" }
|
||||
0x05, 0xff, 0xff, 0x01, // AsWasm(Int)
|
||||
0x06, 0x7e, // AsInterface(I64)
|
||||
0x07, // TableRefAdd
|
||||
0x08, // TableRefGet
|
||||
0x09, 0x01, // CallMethod(1)
|
||||
0x0a, 0x7f, // MakeRecord(I32)
|
||||
0x0c, 0xff, 0xff, 0x01, 0x02, // GetField(Int, 2)
|
||||
0x0d, 0x7f, 0x01, // Const(I32, 1)
|
||||
0x0e, 0x01, // FoldSeq(1)
|
||||
0x0f, 0x7f, // Add(I32)
|
||||
0x10, 0x7f, 0x03, 0x61, 0x62, 0x63, // MemToSeq(I32, "abc")
|
||||
0x11, 0x7f, 0x03, 0x61, 0x62, 0x63, // Load(I32, "abc")
|
||||
0x12, 0x7f, // SeqNew(I32)
|
||||
0x13, // ListPush
|
||||
0x14, 0x01, 0x02, // RepeatUntil(1, 2)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
6
lib/interface-types/src/encoders/mod.rs
Normal file
6
lib/interface-types/src/encoders/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! Writes the AST into a particular format; for instance,
|
||||
//! `encoders::wat` writes the AST into a string representing WIT with
|
||||
//! its textual format.
|
||||
|
||||
pub mod binary;
|
||||
pub mod wat;
|
713
lib/interface-types/src/encoders/wat.rs
Normal file
713
lib/interface-types/src/encoders/wat.rs
Normal file
@ -0,0 +1,713 @@
|
||||
//! Writes the AST into a string representing WIT with its textual format.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use wasmer_interface_types::{
|
||||
//! ast::*,
|
||||
//! encoders::wat::*,
|
||||
//! interpreter::Instruction,
|
||||
//! };
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let input: String = (&Interfaces {
|
||||
//! exports: vec![
|
||||
//! Export {
|
||||
//! name: "foo",
|
||||
//! input_types: vec![InterfaceType::I32],
|
||||
//! output_types: vec![],
|
||||
//! },
|
||||
//! Export {
|
||||
//! name: "bar",
|
||||
//! input_types: vec![],
|
||||
//! output_types: vec![],
|
||||
//! },
|
||||
//! ],
|
||||
//! types: vec![],
|
||||
//! imports: vec![
|
||||
//! Import {
|
||||
//! namespace: "ns",
|
||||
//! name: "foo",
|
||||
//! input_types: vec![],
|
||||
//! output_types: vec![InterfaceType::I32],
|
||||
//! },
|
||||
//! Import {
|
||||
//! namespace: "ns",
|
||||
//! name: "bar",
|
||||
//! input_types: vec![],
|
||||
//! output_types: vec![],
|
||||
//! },
|
||||
//! ],
|
||||
//! adapters: vec![
|
||||
//! Adapter::Import {
|
||||
//! namespace: "ns",
|
||||
//! name: "foo",
|
||||
//! input_types: vec![InterfaceType::I32],
|
||||
//! output_types: vec![],
|
||||
//! instructions: vec![Instruction::ArgumentGet { index: 42 }],
|
||||
//! },
|
||||
//! Adapter::Export {
|
||||
//! name: "bar",
|
||||
//! input_types: vec![],
|
||||
//! output_types: vec![],
|
||||
//! instructions: vec![Instruction::ArgumentGet { index: 42 }],
|
||||
//! },
|
||||
//! ],
|
||||
//! forwards: vec![Forward { name: "main" }],
|
||||
//! })
|
||||
//! .to_string();
|
||||
//! let output = r#";; Interfaces
|
||||
//!
|
||||
//! ;; Interface, Export foo
|
||||
//! (@interface export "foo"
|
||||
//! (param i32))
|
||||
//!
|
||||
//! ;; Interface, Export bar
|
||||
//! (@interface export "bar")
|
||||
//!
|
||||
//! ;; Interface, Import ns.foo
|
||||
//! (@interface func $ns_foo (import "ns" "foo")
|
||||
//! (result i32))
|
||||
//!
|
||||
//! ;; Interface, Import ns.bar
|
||||
//! (@interface func $ns_bar (import "ns" "bar"))
|
||||
//!
|
||||
//! ;; Interface, Adapter ns.foo
|
||||
//! (@interface adapt (import "ns" "foo")
|
||||
//! (param i32)
|
||||
//! arg.get 42)
|
||||
//!
|
||||
//! ;; Interface, Adapter bar
|
||||
//! (@interface adapt (export "bar")
|
||||
//! arg.get 42)
|
||||
//!
|
||||
//! ;; Interface, Forward main
|
||||
//! (@interface forward (export "main"))"#;
|
||||
//!
|
||||
//! assert_eq!(input, output);
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use crate::{
|
||||
ast::{Adapter, Export, Forward, Import, InterfaceType, Interfaces, Type},
|
||||
interpreter::Instruction,
|
||||
};
|
||||
use std::string::ToString;
|
||||
|
||||
/// Encode an `InterfaceType` into a string.
|
||||
impl ToString for &InterfaceType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
InterfaceType::Int => "Int".into(),
|
||||
InterfaceType::Float => "Float".into(),
|
||||
InterfaceType::Any => "Any".into(),
|
||||
InterfaceType::String => "String".into(),
|
||||
InterfaceType::Seq => "Seq".into(),
|
||||
InterfaceType::I32 => "i32".into(),
|
||||
InterfaceType::I64 => "i64".into(),
|
||||
InterfaceType::F32 => "f32".into(),
|
||||
InterfaceType::F64 => "f64".into(),
|
||||
InterfaceType::AnyRef => "anyref".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Instruction` into a string.
|
||||
impl<'input> ToString for &Instruction<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Instruction::ArgumentGet { index } => format!("arg.get {}", index),
|
||||
Instruction::Call { function_index } => format!("call {}", function_index),
|
||||
Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name),
|
||||
Instruction::ReadUtf8 => "read-utf8".into(),
|
||||
Instruction::WriteUtf8 { allocator_name } => {
|
||||
format!(r#"write-utf8 "{}""#, allocator_name)
|
||||
}
|
||||
Instruction::AsWasm(interface_type) => {
|
||||
format!("as-wasm {}", interface_type.to_string())
|
||||
}
|
||||
Instruction::AsInterface(interface_type) => {
|
||||
format!("as-interface {}", interface_type.to_string())
|
||||
}
|
||||
Instruction::TableRefAdd => "table-ref-add".into(),
|
||||
Instruction::TableRefGet => "table-ref-get".into(),
|
||||
Instruction::CallMethod(index) => format!("call-method {}", index),
|
||||
Instruction::MakeRecord(interface_type) => {
|
||||
format!("make-record {}", interface_type.to_string())
|
||||
}
|
||||
Instruction::GetField(interface_type, field_index) => {
|
||||
format!("get-field {} {}", interface_type.to_string(), field_index)
|
||||
}
|
||||
Instruction::Const(interface_type, value) => {
|
||||
format!("const {} {}", interface_type.to_string(), value)
|
||||
}
|
||||
Instruction::FoldSeq(import_index) => format!("fold-seq {}", import_index),
|
||||
Instruction::Add(interface_type) => format!("add {}", interface_type.to_string()),
|
||||
Instruction::MemToSeq(interface_type, memory) => {
|
||||
format!(r#"mem-to-seq {} "{}""#, interface_type.to_string(), memory)
|
||||
}
|
||||
Instruction::Load(interface_type, memory) => {
|
||||
format!(r#"load {} "{}""#, interface_type.to_string(), memory)
|
||||
}
|
||||
Instruction::SeqNew(interface_type) => {
|
||||
format!("seq.new {}", interface_type.to_string())
|
||||
}
|
||||
Instruction::ListPush => "list.push".into(),
|
||||
Instruction::RepeatUntil(condition_index, step_index) => {
|
||||
format!("repeat-until {} {}", condition_index, step_index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a list of `InterfaceType` representing inputs into a
|
||||
/// string.
|
||||
fn input_types_to_param(input_types: &[InterfaceType]) -> String {
|
||||
if input_types.is_empty() {
|
||||
"".into()
|
||||
} else {
|
||||
format!(
|
||||
"\n (param{})",
|
||||
input_types
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, interface_type| {
|
||||
accumulator.push(' ');
|
||||
accumulator.push_str(&interface_type.to_string());
|
||||
accumulator
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a list of `InterfaceType` representing outputs into a
|
||||
/// string.
|
||||
fn output_types_to_result(output_types: &[InterfaceType]) -> String {
|
||||
if output_types.is_empty() {
|
||||
"".into()
|
||||
} else {
|
||||
format!(
|
||||
"\n (result{})",
|
||||
output_types
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, interface_type| {
|
||||
accumulator.push(' ');
|
||||
accumulator.push_str(&interface_type.to_string());
|
||||
accumulator
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Export` into a string.
|
||||
impl<'input> ToString for &Export<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
r#"(@interface export "{name}"{inputs}{outputs})"#,
|
||||
name = self.name,
|
||||
inputs = input_types_to_param(&self.input_types),
|
||||
outputs = output_types_to_result(&self.output_types),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `Type` into a string.
|
||||
impl<'input> ToString for &Type<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
todo!("To be implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Import` into a string.
|
||||
impl<'input> ToString for &Import<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
r#"(@interface func ${namespace}_{name} (import "{namespace}" "{name}"){inputs}{outputs})"#,
|
||||
namespace = self.namespace,
|
||||
name = self.name,
|
||||
inputs = input_types_to_param(&self.input_types),
|
||||
outputs = output_types_to_result(&self.output_types),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Adapter` into a string.
|
||||
impl<'input> ToString for &Adapter<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Adapter::Import {
|
||||
namespace,
|
||||
name,
|
||||
input_types,
|
||||
output_types,
|
||||
instructions,
|
||||
} => format!(
|
||||
r#"(@interface adapt (import "{namespace}" "{name}"){inputs}{outputs}{instructions})"#,
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
inputs = input_types_to_param(&input_types),
|
||||
outputs = output_types_to_result(&output_types),
|
||||
instructions =
|
||||
instructions
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, instruction| {
|
||||
accumulator.push_str("\n ");
|
||||
accumulator.push_str(&instruction.to_string());
|
||||
accumulator
|
||||
}),
|
||||
),
|
||||
|
||||
Adapter::Export {
|
||||
name,
|
||||
input_types,
|
||||
output_types,
|
||||
instructions,
|
||||
} => format!(
|
||||
r#"(@interface adapt (export "{name}"){inputs}{outputs}{instructions})"#,
|
||||
name = name,
|
||||
inputs = input_types_to_param(&input_types),
|
||||
outputs = output_types_to_result(&output_types),
|
||||
instructions =
|
||||
instructions
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, instruction| {
|
||||
accumulator.push_str("\n ");
|
||||
accumulator.push_str(&instruction.to_string());
|
||||
accumulator
|
||||
}),
|
||||
),
|
||||
|
||||
_ => todo!("To be implemented."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `Forward` into a string.
|
||||
impl<'input> ToString for &Forward<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
r#"(@interface forward (export "{name}"))"#,
|
||||
name = self.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Interfaces` into a string.
|
||||
impl<'input> ToString for &Interfaces<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
let mut output = String::from(";; Interfaces");
|
||||
|
||||
let exports = self
|
||||
.exports
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, export| {
|
||||
accumulator.push_str(&format!("\n\n;; Interface, Export {}\n", export.name));
|
||||
accumulator.push_str(&export.to_string());
|
||||
accumulator
|
||||
});
|
||||
|
||||
let types = self
|
||||
.types
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, ty| {
|
||||
accumulator.push_str(&format!("\n\n;; Interface, Ty {}\n", ty.name));
|
||||
accumulator.push_str(&ty.to_string());
|
||||
accumulator
|
||||
});
|
||||
|
||||
let imports = self
|
||||
.imports
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, import| {
|
||||
accumulator.push_str(&format!(
|
||||
"\n\n;; Interface, Import {}.{}\n",
|
||||
import.namespace, import.name
|
||||
));
|
||||
accumulator.push_str(&import.to_string());
|
||||
accumulator
|
||||
});
|
||||
|
||||
let adapters = self
|
||||
.adapters
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, adapter| {
|
||||
match adapter {
|
||||
Adapter::Import {
|
||||
namespace, name, ..
|
||||
} => accumulator.push_str(&format!(
|
||||
"\n\n;; Interface, Adapter {}.{}\n",
|
||||
namespace, name
|
||||
)),
|
||||
|
||||
Adapter::Export { name, .. } => {
|
||||
accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name))
|
||||
}
|
||||
|
||||
_ => todo!("To be implemented."),
|
||||
}
|
||||
accumulator.push_str(&adapter.to_string());
|
||||
accumulator
|
||||
});
|
||||
|
||||
let forwards = self
|
||||
.forwards
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, forward| {
|
||||
accumulator.push_str(&format!("\n\n;; Interface, Forward {}\n", forward.name));
|
||||
accumulator.push_str(&forward.to_string());
|
||||
accumulator
|
||||
});
|
||||
|
||||
output.push_str(&exports);
|
||||
output.push_str(&types);
|
||||
output.push_str(&imports);
|
||||
output.push_str(&adapters);
|
||||
output.push_str(&forwards);
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{ast::*, interpreter::Instruction};
|
||||
|
||||
#[test]
|
||||
fn test_interface_types() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&InterfaceType::Int).to_string(),
|
||||
(&InterfaceType::Float).to_string(),
|
||||
(&InterfaceType::Any).to_string(),
|
||||
(&InterfaceType::String).to_string(),
|
||||
(&InterfaceType::Seq).to_string(),
|
||||
(&InterfaceType::I32).to_string(),
|
||||
(&InterfaceType::I64).to_string(),
|
||||
(&InterfaceType::F32).to_string(),
|
||||
(&InterfaceType::F64).to_string(),
|
||||
(&InterfaceType::AnyRef).to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
"Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref",
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instructions() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&Instruction::ArgumentGet { index: 7 }).to_string(),
|
||||
(&Instruction::Call { function_index: 7 }).to_string(),
|
||||
(&Instruction::CallExport { export_name: "foo" }).to_string(),
|
||||
(&Instruction::ReadUtf8).to_string(),
|
||||
(&Instruction::WriteUtf8 {
|
||||
allocator_name: "foo",
|
||||
})
|
||||
.to_string(),
|
||||
(&Instruction::AsWasm(InterfaceType::Int)).to_string(),
|
||||
(&Instruction::AsInterface(InterfaceType::AnyRef)).to_string(),
|
||||
(&Instruction::TableRefAdd).to_string(),
|
||||
(&Instruction::TableRefGet).to_string(),
|
||||
(&Instruction::CallMethod(7)).to_string(),
|
||||
(&Instruction::MakeRecord(InterfaceType::Int)).to_string(),
|
||||
(&Instruction::GetField(InterfaceType::Int, 7)).to_string(),
|
||||
(&Instruction::Const(InterfaceType::I32, 7)).to_string(),
|
||||
(&Instruction::FoldSeq(7)).to_string(),
|
||||
(&Instruction::Add(InterfaceType::Int)).to_string(),
|
||||
(&Instruction::MemToSeq(InterfaceType::Int, "foo")).to_string(),
|
||||
(&Instruction::Load(InterfaceType::Int, "foo")).to_string(),
|
||||
(&Instruction::SeqNew(InterfaceType::Int)).to_string(),
|
||||
(&Instruction::ListPush).to_string(),
|
||||
(&Instruction::RepeatUntil(1, 2)).to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
"arg.get 7",
|
||||
"call 7",
|
||||
r#"call-export "foo""#,
|
||||
"read-utf8",
|
||||
r#"write-utf8 "foo""#,
|
||||
"as-wasm Int",
|
||||
"as-interface anyref",
|
||||
"table-ref-add",
|
||||
"table-ref-get",
|
||||
"call-method 7",
|
||||
"make-record Int",
|
||||
"get-field Int 7",
|
||||
"const i32 7",
|
||||
"fold-seq 7",
|
||||
"add Int",
|
||||
r#"mem-to-seq Int "foo""#,
|
||||
r#"load Int "foo""#,
|
||||
"seq.new Int",
|
||||
"list.push",
|
||||
"repeat-until 1 2",
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exports() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&Export {
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::F32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
})
|
||||
.to_string(),
|
||||
(&Export {
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![],
|
||||
})
|
||||
.to_string(),
|
||||
(&Export {
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
})
|
||||
.to_string(),
|
||||
(&Export {
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![],
|
||||
})
|
||||
.to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
r#"(@interface export "foo"
|
||||
(param i32 f32)
|
||||
(result i32))"#,
|
||||
r#"(@interface export "foo"
|
||||
(param i32))"#,
|
||||
r#"(@interface export "foo"
|
||||
(result i32))"#,
|
||||
r#"(@interface export "foo")"#,
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imports() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::Int, InterfaceType::String],
|
||||
output_types: vec![InterfaceType::String],
|
||||
})
|
||||
.to_string(),
|
||||
(&Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::String],
|
||||
output_types: vec![],
|
||||
})
|
||||
.to_string(),
|
||||
(&Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![InterfaceType::String],
|
||||
})
|
||||
.to_string(),
|
||||
(&Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![],
|
||||
})
|
||||
.to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
r#"(@interface func $ns_foo (import "ns" "foo")
|
||||
(param Int String)
|
||||
(result String))"#,
|
||||
r#"(@interface func $ns_foo (import "ns" "foo")
|
||||
(param String))"#,
|
||||
r#"(@interface func $ns_foo (import "ns" "foo")
|
||||
(result String))"#,
|
||||
r#"(@interface func $ns_foo (import "ns" "foo"))"#,
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adapters() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&Adapter::Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::F32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::WriteUtf8 {
|
||||
allocator_name: "hello",
|
||||
},
|
||||
Instruction::CallExport { export_name: "f" },
|
||||
],
|
||||
})
|
||||
.to_string(),
|
||||
(&Adapter::Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![],
|
||||
instructions: vec![Instruction::CallExport { export_name: "f" }],
|
||||
})
|
||||
.to_string(),
|
||||
(&Adapter::Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::CallExport { export_name: "f" }],
|
||||
})
|
||||
.to_string(),
|
||||
(&Adapter::Export {
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32, InterfaceType::F32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::WriteUtf8 {
|
||||
allocator_name: "hello",
|
||||
},
|
||||
Instruction::CallExport { export_name: "f" },
|
||||
],
|
||||
})
|
||||
.to_string(),
|
||||
(&Adapter::Export {
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![],
|
||||
instructions: vec![Instruction::CallExport { export_name: "f" }],
|
||||
})
|
||||
.to_string(),
|
||||
(&Adapter::Export {
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::CallExport { export_name: "f" }],
|
||||
})
|
||||
.to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
r#"(@interface adapt (import "ns" "foo")
|
||||
(param i32 f32)
|
||||
(result i32)
|
||||
arg.get 0
|
||||
write-utf8 "hello"
|
||||
call-export "f")"#,
|
||||
r#"(@interface adapt (import "ns" "foo")
|
||||
(param i32)
|
||||
call-export "f")"#,
|
||||
r#"(@interface adapt (import "ns" "foo")
|
||||
(result i32)
|
||||
call-export "f")"#,
|
||||
r#"(@interface adapt (export "foo")
|
||||
(param i32 f32)
|
||||
(result i32)
|
||||
arg.get 0
|
||||
write-utf8 "hello"
|
||||
call-export "f")"#,
|
||||
r#"(@interface adapt (export "foo")
|
||||
(param i32)
|
||||
call-export "f")"#,
|
||||
r#"(@interface adapt (export "foo")
|
||||
(result i32)
|
||||
call-export "f")"#,
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_forward() {
|
||||
let input: String = (&Forward { name: "main" }).to_string();
|
||||
let output = r#"(@interface forward (export "main"))"#;
|
||||
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interfaces() {
|
||||
let input: String = (&Interfaces {
|
||||
exports: vec![
|
||||
Export {
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![],
|
||||
},
|
||||
Export {
|
||||
name: "bar",
|
||||
input_types: vec![],
|
||||
output_types: vec![],
|
||||
},
|
||||
],
|
||||
types: vec![],
|
||||
imports: vec![
|
||||
Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
},
|
||||
Import {
|
||||
namespace: "ns",
|
||||
name: "bar",
|
||||
input_types: vec![],
|
||||
output_types: vec![],
|
||||
},
|
||||
],
|
||||
adapters: vec![
|
||||
Adapter::Import {
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 42 }],
|
||||
},
|
||||
Adapter::Export {
|
||||
name: "bar",
|
||||
input_types: vec![],
|
||||
output_types: vec![],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 42 }],
|
||||
},
|
||||
],
|
||||
forwards: vec![Forward { name: "main" }],
|
||||
})
|
||||
.to_string();
|
||||
let output = r#";; Interfaces
|
||||
|
||||
;; Interface, Export foo
|
||||
(@interface export "foo"
|
||||
(param i32))
|
||||
|
||||
;; Interface, Export bar
|
||||
(@interface export "bar")
|
||||
|
||||
;; Interface, Import ns.foo
|
||||
(@interface func $ns_foo (import "ns" "foo")
|
||||
(result i32))
|
||||
|
||||
;; Interface, Import ns.bar
|
||||
(@interface func $ns_bar (import "ns" "bar"))
|
||||
|
||||
;; Interface, Adapter ns.foo
|
||||
(@interface adapt (import "ns" "foo")
|
||||
(param i32)
|
||||
arg.get 42)
|
||||
|
||||
;; Interface, Adapter bar
|
||||
(@interface adapt (export "bar")
|
||||
arg.get 42)
|
||||
|
||||
;; Interface, Forward main
|
||||
(@interface forward (export "main"))"#;
|
||||
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
}
|
77
lib/interface-types/src/interpreter/instruction.rs
Normal file
77
lib/interface-types/src/interpreter/instruction.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use crate::ast::InterfaceType;
|
||||
|
||||
/// Represents all the possible WIT instructions.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Instruction<'input> {
|
||||
/// The `arg.get` instruction.
|
||||
ArgumentGet {
|
||||
/// The argument index.
|
||||
index: u64,
|
||||
},
|
||||
|
||||
/// The `call` instruction.
|
||||
Call {
|
||||
/// The function index.
|
||||
function_index: usize,
|
||||
},
|
||||
|
||||
/// The `call-export` instruction.
|
||||
CallExport {
|
||||
/// The exported function name.
|
||||
export_name: &'input str,
|
||||
},
|
||||
|
||||
/// The `read-utf8` instruction.
|
||||
ReadUtf8,
|
||||
|
||||
/// The `write-utf8` instruction.
|
||||
WriteUtf8 {
|
||||
/// The allocator function name.
|
||||
allocator_name: &'input str,
|
||||
},
|
||||
|
||||
/// The `as-wasm` instruction.
|
||||
AsWasm(InterfaceType),
|
||||
|
||||
/// The `as-interface` instruction.
|
||||
AsInterface(InterfaceType),
|
||||
|
||||
/// The `table-ref-add` instruction.
|
||||
TableRefAdd,
|
||||
|
||||
/// The `table-ref-get` instruction.
|
||||
TableRefGet,
|
||||
|
||||
/// The `call-method` instruction.
|
||||
CallMethod(u64),
|
||||
|
||||
/// The `make-record` instruction.
|
||||
MakeRecord(InterfaceType),
|
||||
|
||||
/// The `get-field` instruction.
|
||||
GetField(InterfaceType, u64),
|
||||
|
||||
/// The `const` instruction.
|
||||
Const(InterfaceType, u64),
|
||||
|
||||
/// The `fold-seq` instruction.
|
||||
FoldSeq(u64),
|
||||
|
||||
/// The `add` instruction.
|
||||
Add(InterfaceType),
|
||||
|
||||
/// The `mem-to-seq` instruction.
|
||||
MemToSeq(InterfaceType, &'input str),
|
||||
|
||||
/// The `load` instruction.
|
||||
Load(InterfaceType, &'input str),
|
||||
|
||||
/// The `seq.new` instruction.
|
||||
SeqNew(InterfaceType),
|
||||
|
||||
/// The `list.push` instruction.
|
||||
ListPush,
|
||||
|
||||
/// The `repeat-until` instruction.
|
||||
RepeatUntil(u64, u64),
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
executable_instruction!(
|
||||
argument_get(index: u64, instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let invocation_inputs = runtime.invocation_inputs;
|
||||
|
||||
if index >= (invocation_inputs.len() as u64) {
|
||||
return Err(format!(
|
||||
"`{}` cannot access argument #{} because it doesn't exist.",
|
||||
instruction_name, index
|
||||
));
|
||||
}
|
||||
|
||||
runtime.stack.push(invocation_inputs[index as usize].clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_argument_get =
|
||||
instructions: [Instruction::ArgumentGet { index: 0 }],
|
||||
invocation_inputs: [InterfaceValue::I32(42)],
|
||||
instance: Instance::new(),
|
||||
stack: [InterfaceValue::I32(42)],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_argument_get__twice =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(7),
|
||||
InterfaceValue::I32(42),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [
|
||||
InterfaceValue::I32(7),
|
||||
InterfaceValue::I32(42),
|
||||
],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_argument_get__invalid_index =
|
||||
instructions: [Instruction::ArgumentGet { index: 1 }],
|
||||
invocation_inputs: [InterfaceValue::I32(42)],
|
||||
instance: Instance::new(),
|
||||
error: "`arg.get 1` cannot access argument #1 because it doesn't exist."
|
||||
);
|
||||
}
|
187
lib/interface-types/src/interpreter/instructions/call.rs
Normal file
187
lib/interface-types/src/interpreter/instructions/call.rs
Normal file
@ -0,0 +1,187 @@
|
||||
use crate::interpreter::wasm::{
|
||||
structures::{FunctionIndex, TypedIndex},
|
||||
values::InterfaceType,
|
||||
};
|
||||
|
||||
executable_instruction!(
|
||||
call(function_index: usize, instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
let index = FunctionIndex::new(function_index);
|
||||
|
||||
match instance.local_or_import(index) {
|
||||
Some(local_or_import) => {
|
||||
let inputs_cardinality = local_or_import.inputs_cardinality();
|
||||
|
||||
match runtime.stack.pop(inputs_cardinality) {
|
||||
Some(inputs) => {
|
||||
let input_types = inputs
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<InterfaceType>>();
|
||||
|
||||
if input_types != local_or_import.inputs() {
|
||||
return Err(format!(
|
||||
"`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).",
|
||||
instruction_name,
|
||||
function_index,
|
||||
local_or_import.inputs(),
|
||||
))
|
||||
}
|
||||
|
||||
match local_or_import.call(&inputs) {
|
||||
Ok(outputs) => {
|
||||
for output in outputs.iter() {
|
||||
runtime.stack.push(output.clone());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(format!(
|
||||
"`{}` failed when calling the local or imported function `{}`.",
|
||||
instruction_name,
|
||||
function_index
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
||||
instruction_name,
|
||||
function_index,
|
||||
inputs_cardinality,
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the local or imported function `{}` because it doesn't exist.",
|
||||
instruction_name,
|
||||
function_index,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_call =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [InterfaceValue::I32(12)],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__invalid_local_import_index =
|
||||
instructions: [
|
||||
Instruction::Call { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Default::default(),
|
||||
error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
// ^^ `42` expects 2 values on the stack, only one is present
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__invalid_types_in_the_stack =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I64(4),
|
||||
// ^^^ mismatch with `42` signature
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__failure_when_calling =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance {
|
||||
locals_or_imports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
42,
|
||||
LocalImport {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Err(()),
|
||||
// ^^^^^^^ function fails
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
error: r#"`call 42` failed when calling the local or imported function `42`."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__void =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance {
|
||||
locals_or_imports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
42,
|
||||
LocalImport {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Ok(vec![]),
|
||||
// ^^^^^^^^^^ void
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
stack: [],
|
||||
);
|
||||
}
|
177
lib/interface-types/src/interpreter/instructions/call_export.rs
Normal file
177
lib/interface-types/src/interpreter/instructions/call_export.rs
Normal file
@ -0,0 +1,177 @@
|
||||
use crate::interpreter::wasm::values::InterfaceType;
|
||||
|
||||
executable_instruction!(
|
||||
call_export(export_name: String, instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
|
||||
match instance.export(&export_name) {
|
||||
Some(export) => {
|
||||
let inputs_cardinality = export.inputs_cardinality();
|
||||
|
||||
match runtime.stack.pop(inputs_cardinality) {
|
||||
Some(inputs) => {
|
||||
let input_types = inputs
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<InterfaceType>>();
|
||||
|
||||
if input_types != export.inputs() {
|
||||
return Err(format!(
|
||||
"`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).",
|
||||
instruction_name,
|
||||
export_name,
|
||||
export.inputs(),
|
||||
))
|
||||
}
|
||||
|
||||
match export.call(&inputs) {
|
||||
Ok(outputs) => {
|
||||
for output in outputs.iter() {
|
||||
runtime.stack.push(output.clone());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(format!(
|
||||
"`{}` failed when calling the exported function `{}`.",
|
||||
instruction_name,
|
||||
export_name
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
||||
instruction_name,
|
||||
export_name,
|
||||
inputs_cardinality,
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the exported function `{}` because it doesn't exist.",
|
||||
instruction_name,
|
||||
export_name,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_call_export =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [InterfaceValue::I32(7)],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__invalid_export_name =
|
||||
instructions: [Instruction::CallExport { export_name: "bar" }],
|
||||
invocation_inputs: [],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__invalid_types_in_the_stack =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I64(4),
|
||||
// ^^^ mismatch with `sum` signature
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__failure_when_calling =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance {
|
||||
exports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
"sum".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Err(()),
|
||||
// ^^^^^^^ function fails
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
error: r#"`call-export "sum"` failed when calling the exported function `sum`."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__void =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance {
|
||||
exports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
"sum".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Ok(vec![]),
|
||||
// ^^^^^^^^^^ void function
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
stack: [],
|
||||
);
|
||||
}
|
188
lib/interface-types/src/interpreter/instructions/mod.rs
Normal file
188
lib/interface-types/src/interpreter/instructions/mod.rs
Normal file
@ -0,0 +1,188 @@
|
||||
mod argument_get;
|
||||
mod call;
|
||||
mod call_export;
|
||||
mod read_utf8;
|
||||
mod write_utf8;
|
||||
|
||||
pub(crate) use argument_get::argument_get;
|
||||
pub(crate) use call::call;
|
||||
pub(crate) use call_export::call_export;
|
||||
pub(crate) use read_utf8::read_utf8;
|
||||
pub(crate) use write_utf8::write_utf8;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use crate::interpreter::wasm::{
|
||||
self,
|
||||
values::{InterfaceType, InterfaceValue},
|
||||
};
|
||||
use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc};
|
||||
|
||||
pub(crate) struct Export {
|
||||
pub(crate) inputs: Vec<InterfaceType>,
|
||||
pub(crate) outputs: Vec<InterfaceType>,
|
||||
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
|
||||
}
|
||||
|
||||
impl wasm::structures::Export for Export {
|
||||
fn inputs_cardinality(&self) -> usize {
|
||||
self.inputs.len() as usize
|
||||
}
|
||||
|
||||
fn outputs_cardinality(&self) -> usize {
|
||||
self.outputs.len()
|
||||
}
|
||||
|
||||
fn inputs(&self) -> &[InterfaceType] {
|
||||
&self.inputs
|
||||
}
|
||||
|
||||
fn outputs(&self) -> &[InterfaceType] {
|
||||
&self.outputs
|
||||
}
|
||||
|
||||
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
|
||||
(self.function)(arguments)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LocalImport {
|
||||
pub(crate) inputs: Vec<InterfaceType>,
|
||||
pub(crate) outputs: Vec<InterfaceType>,
|
||||
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
|
||||
}
|
||||
|
||||
impl wasm::structures::LocalImport for LocalImport {
|
||||
fn inputs_cardinality(&self) -> usize {
|
||||
self.inputs.len()
|
||||
}
|
||||
|
||||
fn outputs_cardinality(&self) -> usize {
|
||||
self.outputs.len()
|
||||
}
|
||||
|
||||
fn inputs(&self) -> &[InterfaceType] {
|
||||
&self.inputs
|
||||
}
|
||||
|
||||
fn outputs(&self) -> &[InterfaceType] {
|
||||
&self.outputs
|
||||
}
|
||||
|
||||
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
|
||||
(self.function)(arguments)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct MemoryView(Rc<Vec<Cell<u8>>>);
|
||||
|
||||
impl wasm::structures::MemoryView for MemoryView {}
|
||||
|
||||
impl Deref for MemoryView {
|
||||
type Target = [Cell<u8>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Memory {
|
||||
pub(crate) view: MemoryView,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub(crate) fn new(data: Vec<Cell<u8>>) -> Self {
|
||||
Self {
|
||||
view: MemoryView(Rc::new(data)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl wasm::structures::Memory<MemoryView> for Memory {
|
||||
fn view(&self) -> MemoryView {
|
||||
self.view.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Instance {
|
||||
pub(crate) exports: HashMap<String, Export>,
|
||||
pub(crate) locals_or_imports: HashMap<usize, LocalImport>,
|
||||
pub(crate) memory: Memory,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
exports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
"sum".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |arguments: &[InterfaceValue]| {
|
||||
let a: i32 = (&arguments[0]).try_into().unwrap();
|
||||
let b: i32 = (&arguments[1]).try_into().unwrap();
|
||||
|
||||
Ok(vec![InterfaceValue::I32(a + b)])
|
||||
},
|
||||
},
|
||||
);
|
||||
hashmap.insert(
|
||||
"alloc".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |arguments: &[InterfaceValue]| {
|
||||
let _size: i32 = (&arguments[0]).try_into().unwrap();
|
||||
|
||||
Ok(vec![InterfaceValue::I32(0)])
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
locals_or_imports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
42,
|
||||
LocalImport {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |arguments: &[InterfaceValue]| {
|
||||
let a: i32 = (&arguments[0]).try_into().unwrap();
|
||||
let b: i32 = (&arguments[1]).try_into().unwrap();
|
||||
|
||||
Ok(vec![InterfaceValue::I32(a * b)])
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
memory: Memory::new(vec![Cell::new(0); 128]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl wasm::structures::Instance<Export, LocalImport, Memory, MemoryView> for Instance {
|
||||
fn export(&self, export_name: &str) -> Option<&Export> {
|
||||
self.exports.get(export_name)
|
||||
}
|
||||
|
||||
fn local_or_import<I: wasm::structures::TypedIndex + wasm::structures::LocalImportIndex>(
|
||||
&mut self,
|
||||
index: I,
|
||||
) -> Option<&LocalImport> {
|
||||
self.locals_or_imports.get(&index.index())
|
||||
}
|
||||
|
||||
fn memory(&self, _index: usize) -> Option<&Memory> {
|
||||
Some(&self.memory)
|
||||
}
|
||||
}
|
||||
}
|
131
lib/interface-types/src/interpreter/instructions/read_utf8.rs
Normal file
131
lib/interface-types/src/interpreter/instructions/read_utf8.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use crate::interpreter::wasm::values::InterfaceValue;
|
||||
use std::{cell::Cell, convert::TryFrom};
|
||||
|
||||
executable_instruction!(
|
||||
read_utf8(instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
match runtime.stack.pop(2) {
|
||||
Some(inputs) => match runtime.wasm_instance.memory(0) {
|
||||
Some(memory) => {
|
||||
let length = i32::try_from(&inputs[0])? as usize;
|
||||
let pointer = i32::try_from(&inputs[1])? as usize;
|
||||
let memory_view = memory.view();
|
||||
|
||||
if memory_view.len() < pointer + length {
|
||||
return Err(format!(
|
||||
"`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).",
|
||||
instruction_name,
|
||||
pointer + length,
|
||||
memory_view.len()
|
||||
));
|
||||
}
|
||||
|
||||
let data: Vec<u8> = (&memory_view[pointer..pointer + length])
|
||||
.iter()
|
||||
.map(Cell::get)
|
||||
.collect();
|
||||
|
||||
match String::from_utf8(data) {
|
||||
Ok(string) => {
|
||||
runtime.stack.push(InterfaceValue::String(string));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(utf8_error) => Err(format!(
|
||||
"`{}` failed because the read string isn't UTF-8 valid ({}).",
|
||||
instruction_name,
|
||||
utf8_error,
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` failed because there is no memory to read.",
|
||||
instruction_name
|
||||
))
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` failed because there is not enough data on the stack (needs 2).",
|
||||
instruction_name,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_read_utf8 =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ReadUtf8,
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(13),
|
||||
// ^^^^^^^ length
|
||||
InterfaceValue::I32(0),
|
||||
// ^^^^^^ pointer
|
||||
],
|
||||
instance: Instance {
|
||||
memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
|
||||
..Default::default()
|
||||
},
|
||||
stack: [InterfaceValue::String("Hello, World!".into())],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_read_utf8__read_out_of_memory =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ReadUtf8,
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(13),
|
||||
// ^^^^^^^ length is too long
|
||||
InterfaceValue::I32(0),
|
||||
// ^^^^^^ pointer
|
||||
],
|
||||
instance: Instance {
|
||||
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
|
||||
..Default::default()
|
||||
},
|
||||
error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_read_utf8__invalid_encoding =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ReadUtf8,
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(4),
|
||||
// ^^^^^^ length is too long
|
||||
InterfaceValue::I32(0),
|
||||
// ^^^^^^ pointer
|
||||
],
|
||||
instance: Instance {
|
||||
memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()),
|
||||
..Default::default()
|
||||
},
|
||||
error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_read_utf8__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ReadUtf8,
|
||||
// ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present.
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(13),
|
||||
InterfaceValue::I32(0),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#,
|
||||
);
|
||||
}
|
169
lib/interface-types/src/interpreter/instructions/write_utf8.rs
Normal file
169
lib/interface-types/src/interpreter/instructions/write_utf8.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use crate::interpreter::wasm::values::{InterfaceType, InterfaceValue};
|
||||
use std::convert::TryInto;
|
||||
|
||||
executable_instruction!(
|
||||
write_utf8(allocator_name: String, instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
|
||||
match instance.export(&allocator_name) {
|
||||
Some(allocator) => {
|
||||
if allocator.inputs() != [InterfaceType::I32] ||
|
||||
allocator.outputs() != [InterfaceType::I32] {
|
||||
return Err(format!(
|
||||
"`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).",
|
||||
instruction_name,
|
||||
allocator_name,
|
||||
))
|
||||
}
|
||||
|
||||
match instance.memory(0) {
|
||||
Some(memory) => match runtime.stack.pop1() {
|
||||
Some(string) => {
|
||||
let memory_view = memory.view();
|
||||
|
||||
let string: String = (&string).try_into()?;
|
||||
let string_bytes = string.as_bytes();
|
||||
let string_length = (string_bytes.len() as i32)
|
||||
.try_into()
|
||||
.map_err(|error| format!("{}", error))?;
|
||||
|
||||
match allocator.call(&[InterfaceValue::I32(string_length)]) {
|
||||
Ok(outputs) => {
|
||||
let string_pointer: i32 = (&outputs[0]).try_into()?;
|
||||
|
||||
for (nth, byte) in string_bytes.iter().enumerate() {
|
||||
memory_view[string_pointer as usize + nth].set(*byte);
|
||||
}
|
||||
|
||||
runtime.stack.push(InterfaceValue::I32(string_pointer));
|
||||
runtime.stack.push(InterfaceValue::I32(string_length));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(format!(
|
||||
"`{}` failed when calling the allocator `{}`.",
|
||||
instruction_name,
|
||||
allocator_name,
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
||||
instruction_name,
|
||||
allocator_name,
|
||||
1
|
||||
))
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` failed because there is no memory to write into.",
|
||||
instruction_name
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` failed because the exported function `{}` (the allocator) doesn't exist.",
|
||||
instruction_name,
|
||||
allocator_name
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_write_utf8 =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::WriteUtf8 { allocator_name: "alloc" },
|
||||
],
|
||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||
instance: Instance::new(),
|
||||
stack: [
|
||||
InterfaceValue::I32(0),
|
||||
// ^^^^^^ pointer
|
||||
InterfaceValue::I32(13),
|
||||
// ^^^^^^^ length
|
||||
]
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_write_utf8__roundtrip_with_read_utf8 =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::WriteUtf8 { allocator_name: "alloc" },
|
||||
Instruction::ReadUtf8,
|
||||
],
|
||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||
instance: Instance::new(),
|
||||
stack: [InterfaceValue::String("Hello, World!".into())],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_write_utf8__allocator_does_not_exist =
|
||||
instructions: [Instruction::WriteUtf8 { allocator_name: "alloc" }],
|
||||
invocation_inputs: [],
|
||||
instance: Instance { ..Default::default() },
|
||||
error: r#"`write-utf8 "alloc"` failed because the exported function `alloc` (the allocator) doesn't exist."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_write_utf8__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::WriteUtf8 { allocator_name: "alloc" }
|
||||
// ^^^^^ `alloc` expects 1 value on the stack, none is present
|
||||
],
|
||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||
instance: Instance::new(),
|
||||
error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is not enough data on the stack for the arguments (needs 1)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_write_utf8__failure_when_calling_the_allocator =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::WriteUtf8 { allocator_name: "alloc-fail" }
|
||||
],
|
||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||
instance: {
|
||||
let mut instance = Instance::new();
|
||||
instance.exports.insert(
|
||||
"alloc-fail".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Err(()),
|
||||
// ^^^^^^^ function fails
|
||||
},
|
||||
);
|
||||
|
||||
instance
|
||||
},
|
||||
error: r#"`write-utf8 "alloc-fail"` failed when calling the allocator `alloc-fail`."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_write_utf8__invalid_allocator_signature =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::WriteUtf8 { allocator_name: "alloc-fail" }
|
||||
],
|
||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||
instance: {
|
||||
let mut instance = Instance::new();
|
||||
instance.exports.insert(
|
||||
"alloc-fail".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![],
|
||||
function: |_| Err(()),
|
||||
},
|
||||
);
|
||||
|
||||
instance
|
||||
},
|
||||
error: r#"`write-utf8 "alloc-fail"` failed because the allocator `alloc-fail` has an invalid signature (expects [I32] -> [I32])."#,
|
||||
);
|
||||
}
|
241
lib/interface-types/src/interpreter/mod.rs
Normal file
241
lib/interface-types/src/interpreter/mod.rs
Normal file
@ -0,0 +1,241 @@
|
||||
//! A stack-based interpreter to execute instructions of WIT adapters.
|
||||
|
||||
mod instruction;
|
||||
mod instructions;
|
||||
pub mod stack;
|
||||
pub mod wasm;
|
||||
|
||||
pub use instruction::Instruction;
|
||||
use stack::Stack;
|
||||
use std::{convert::TryFrom, marker::PhantomData};
|
||||
use wasm::values::InterfaceValue;
|
||||
|
||||
/// Represents the `Runtime`, which is used by an adapter to execute
|
||||
/// its instructions.
|
||||
pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: wasm::structures::Export + 'instance,
|
||||
LocalImport: wasm::structures::LocalImport + 'instance,
|
||||
Memory: wasm::structures::Memory<MemoryView> + 'instance,
|
||||
MemoryView: wasm::structures::MemoryView,
|
||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView> + 'instance,
|
||||
{
|
||||
/// The invocation inputs are all the arguments received by an
|
||||
/// adapter.
|
||||
invocation_inputs: &'invocation [InterfaceValue],
|
||||
|
||||
/// Each runtime (so adapter) has its own stack instance.
|
||||
stack: Stack<InterfaceValue>,
|
||||
|
||||
/// The WebAssembly module instance. It is used by adapter's
|
||||
/// instructions.
|
||||
wasm_instance: &'instance mut Instance,
|
||||
|
||||
/// Phantom data.
|
||||
_phantom: PhantomData<(Export, LocalImport, Memory, MemoryView)>,
|
||||
}
|
||||
|
||||
/// Type alias for an executable instruction. It's an implementation
|
||||
/// details, but an instruction is a boxed closure instance.
|
||||
pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView> = Box<
|
||||
dyn Fn(&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView>) -> Result<(), String>,
|
||||
>;
|
||||
|
||||
/// An interpreter is the central piece of this crate. It is a set of
|
||||
/// executable instructions. Each instruction takes the runtime as
|
||||
/// argument. The runtime holds the invocation inputs, the stack, and
|
||||
/// the WebAssembly instance.
|
||||
///
|
||||
/// When the interpreter executes the instructions, each of them can
|
||||
/// query the WebAssembly instance, operates on the stack, or reads
|
||||
/// the invocation inputs. At the end of the execution, the stack
|
||||
/// supposedly contains a result. Since an interpreter is used by a
|
||||
/// WIT adapter to execute its instructions, the result on the stack
|
||||
/// is the result of the adapter.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use std::{cell::Cell, collections::HashMap, convert::TryInto};
|
||||
/// use wasmer_interface_types::interpreter::{
|
||||
/// instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
|
||||
/// // ^^^^^^^^^^^^ This is private and for testing purposes only.
|
||||
/// // It is basically a fake WebAssembly runtime.
|
||||
/// stack::Stackable,
|
||||
/// wasm::values::{InterfaceType, InterfaceValue},
|
||||
/// Instruction, Interpreter,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() {
|
||||
/// // 1. Creates an interpreter from a set of instructions. They will
|
||||
/// // be transformed into executable instructions.
|
||||
/// let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
|
||||
/// Instruction::ArgumentGet { index: 1 },
|
||||
/// Instruction::ArgumentGet { index: 0 },
|
||||
/// Instruction::CallExport { export_name: "sum" },
|
||||
/// ])
|
||||
/// .try_into()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // 2. Defines the arguments of the adapter.
|
||||
/// let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
|
||||
///
|
||||
/// // 3. Creates a WebAssembly instance.
|
||||
/// let mut instance = Instance {
|
||||
/// // 3.1. Defines one exported function: `fn sum(a: i32, b: i32) -> i32 { a + b }`.
|
||||
/// exports: {
|
||||
/// let mut hashmap = HashMap::new();
|
||||
/// hashmap.insert(
|
||||
/// "sum".into(),
|
||||
/// Export {
|
||||
/// // Defines the argument types of the function.
|
||||
/// inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
///
|
||||
/// // Defines the result types.
|
||||
/// outputs: vec![InterfaceType::I32],
|
||||
///
|
||||
/// // Defines the function implementation.
|
||||
/// function: |arguments: &[InterfaceValue]| {
|
||||
/// let a: i32 = (&arguments[0]).try_into().unwrap();
|
||||
/// let b: i32 = (&arguments[1]).try_into().unwrap();
|
||||
///
|
||||
/// Ok(vec![InterfaceValue::I32(a + b)])
|
||||
/// },
|
||||
/// },
|
||||
/// );
|
||||
/// },
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// // 4. Executes the instructions.
|
||||
/// let run = interpreter.run(&invocation_inputs, &mut instance);
|
||||
///
|
||||
/// assert!(run.is_ok());
|
||||
///
|
||||
/// let stack = run.unwrap();
|
||||
///
|
||||
/// // 5. Read the stack to get the result.
|
||||
/// assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]);
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: wasm::structures::Export,
|
||||
LocalImport: wasm::structures::LocalImport,
|
||||
Memory: wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: wasm::structures::MemoryView,
|
||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
executable_instructions:
|
||||
Vec<ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>>,
|
||||
}
|
||||
|
||||
impl<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: wasm::structures::Export,
|
||||
LocalImport: wasm::structures::LocalImport,
|
||||
Memory: wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: wasm::structures::MemoryView,
|
||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
fn iter(
|
||||
&self,
|
||||
) -> impl Iterator<
|
||||
Item = &ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>,
|
||||
> + '_ {
|
||||
self.executable_instructions.iter()
|
||||
}
|
||||
|
||||
/// Runs the interpreter, such as:
|
||||
/// 1. Create a fresh stack,
|
||||
/// 2. Create a fresh stack,
|
||||
/// 3. Execute the instructions one after the other, and
|
||||
/// returns the stack.
|
||||
pub fn run(
|
||||
&self,
|
||||
invocation_inputs: &[InterfaceValue],
|
||||
wasm_instance: &mut Instance,
|
||||
) -> Result<Stack<InterfaceValue>, String> {
|
||||
let mut runtime = Runtime {
|
||||
invocation_inputs,
|
||||
stack: Stack::new(),
|
||||
wasm_instance,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
|
||||
for executable_instruction in self.iter() {
|
||||
match executable_instruction(&mut runtime) {
|
||||
Ok(_) => continue,
|
||||
Err(message) => return Err(message),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(runtime.stack)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms a `Vec<Instruction>` into an `Interpreter`.
|
||||
impl<'binary_input, Instance, Export, LocalImport, Memory, MemoryView>
|
||||
TryFrom<&Vec<Instruction<'binary_input>>>
|
||||
for Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: wasm::structures::Export,
|
||||
LocalImport: wasm::structures::LocalImport,
|
||||
Memory: wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: wasm::structures::MemoryView,
|
||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
type Error = String;
|
||||
|
||||
fn try_from(instructions: &Vec<Instruction>) -> Result<Self, Self::Error> {
|
||||
let executable_instructions = instructions
|
||||
.iter()
|
||||
.map(|instruction| {
|
||||
let instruction_name = instruction.to_string();
|
||||
|
||||
match instruction {
|
||||
Instruction::ArgumentGet { index } => {
|
||||
instructions::argument_get(*index, instruction_name)
|
||||
}
|
||||
Instruction::Call { function_index } => {
|
||||
instructions::call(*function_index, instruction_name)
|
||||
}
|
||||
Instruction::CallExport { export_name } => {
|
||||
instructions::call_export((*export_name).to_owned(), instruction_name)
|
||||
}
|
||||
Instruction::ReadUtf8 => instructions::read_utf8(instruction_name),
|
||||
Instruction::WriteUtf8 { allocator_name } => {
|
||||
instructions::write_utf8((*allocator_name).to_owned(), instruction_name)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Interpreter {
|
||||
executable_instructions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{wasm::structures::EmptyMemoryView, Instruction, Interpreter};
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
fn test_interpreter_from_instructions() {
|
||||
let instructions = vec![
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "foo" },
|
||||
Instruction::ReadUtf8,
|
||||
Instruction::Call { function_index: 7 },
|
||||
];
|
||||
let interpreter: Interpreter<(), (), (), (), EmptyMemoryView> =
|
||||
(&instructions).try_into().unwrap();
|
||||
|
||||
assert_eq!(interpreter.executable_instructions.len(), 5);
|
||||
}
|
||||
}
|
130
lib/interface-types/src/interpreter/stack.rs
Normal file
130
lib/interface-types/src/interpreter/stack.rs
Normal file
@ -0,0 +1,130 @@
|
||||
//! A very light and generic stack implementation, exposing only the
|
||||
//! operations required by the interpreter.
|
||||
|
||||
/// The `Stackable` trait represents a small basic set of operations
|
||||
/// required by the interpreter.
|
||||
pub trait Stackable {
|
||||
/// The kind of item the stack holds.
|
||||
type Item;
|
||||
|
||||
/// Checks whether the stack is empty.
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
/// Extracts a slice containing the entire stack.
|
||||
fn as_slice(&self) -> &[Self::Item];
|
||||
|
||||
/// Appends one item to the end of the stack.
|
||||
fn push(&mut self, item: Self::Item);
|
||||
|
||||
/// Removes the last item of the stack and returns it, `None` if
|
||||
/// the stack is empty.
|
||||
fn pop1(&mut self) -> Option<Self::Item>;
|
||||
|
||||
/// Removes `n` elements from the end of the stack, `None` if the
|
||||
/// stack doesn't contain enough elements.
|
||||
/// Returned items are ordered by FIFO: the last element comes
|
||||
/// first in the list.
|
||||
fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>>;
|
||||
}
|
||||
|
||||
/// A stack implementation of the `Stackable` trait, based on a vector.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Stack<T>
|
||||
where
|
||||
T: Default + Clone,
|
||||
{
|
||||
/// Inner structure holding the items.
|
||||
inner: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Stack<T>
|
||||
where
|
||||
T: Default + Clone,
|
||||
{
|
||||
/// Creates a new empty stack.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stackable for Stack<T>
|
||||
where
|
||||
T: Default + Clone,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
self.inner.as_slice()
|
||||
}
|
||||
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.inner.push(item);
|
||||
}
|
||||
|
||||
fn pop1(&mut self) -> Option<Self::Item> {
|
||||
self.inner.pop()
|
||||
}
|
||||
|
||||
fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>> {
|
||||
if self.inner.len() < n {
|
||||
None
|
||||
} else {
|
||||
let items = self
|
||||
.inner
|
||||
.drain(self.inner.len() - n..)
|
||||
.rev()
|
||||
.collect::<Vec<Self::Item>>();
|
||||
|
||||
assert!(items.len() == n);
|
||||
|
||||
Some(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Stack, Stackable};
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
let mut stack = Stack::new();
|
||||
assert_eq!(stack.is_empty(), true);
|
||||
|
||||
stack.push(1);
|
||||
assert_eq!(stack.is_empty(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_pop1() {
|
||||
let mut stack = Stack::new();
|
||||
stack.push(1);
|
||||
|
||||
assert_eq!(stack.pop1(), Some(1));
|
||||
assert_eq!(stack.is_empty(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pop() {
|
||||
let mut stack = Stack::new();
|
||||
stack.push(1);
|
||||
stack.push(2);
|
||||
stack.push(3);
|
||||
stack.push(4);
|
||||
stack.push(5);
|
||||
stack.push(6);
|
||||
|
||||
assert_eq!(stack.pop(1), Some(vec![6]));
|
||||
assert_eq!(stack.pop(2), Some(vec![5, 4]));
|
||||
assert_eq!(stack.pop(4), None); // not enough items
|
||||
assert_eq!(stack.pop(3), Some(vec![3, 2, 1]));
|
||||
assert_eq!(stack.pop1(), None);
|
||||
assert_eq!(stack.is_empty(), true);
|
||||
}
|
||||
}
|
6
lib/interface-types/src/interpreter/wasm/mod.rs
Normal file
6
lib/interface-types/src/interpreter/wasm/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! An hypothetic WebAssembly runtime, represented as a set of enums,
|
||||
//! types, and traits —basically this is the part a runtime should
|
||||
//! take a look to use the `wasmer-interface-types` crate—.
|
||||
|
||||
pub mod structures;
|
||||
pub mod values;
|
159
lib/interface-types/src/interpreter/wasm/structures.rs
Normal file
159
lib/interface-types/src/interpreter/wasm/structures.rs
Normal file
@ -0,0 +1,159 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use super::values::{InterfaceType, InterfaceValue};
|
||||
use std::{cell::Cell, ops::Deref};
|
||||
|
||||
pub trait TypedIndex: Copy + Clone {
|
||||
fn new(index: usize) -> Self;
|
||||
fn index(&self) -> usize;
|
||||
}
|
||||
|
||||
macro_rules! typed_index {
|
||||
($type:ident) => {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct $type(usize);
|
||||
|
||||
impl TypedIndex for $type {
|
||||
fn new(index: usize) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
typed_index!(FunctionIndex);
|
||||
typed_index!(LocalFunctionIndex);
|
||||
typed_index!(ImportFunctionIndex);
|
||||
|
||||
pub trait LocalImportIndex {
|
||||
type Local: TypedIndex;
|
||||
type Import: TypedIndex;
|
||||
}
|
||||
|
||||
impl LocalImportIndex for FunctionIndex {
|
||||
type Local = LocalFunctionIndex;
|
||||
type Import = ImportFunctionIndex;
|
||||
}
|
||||
|
||||
pub trait Export {
|
||||
fn inputs_cardinality(&self) -> usize;
|
||||
fn outputs_cardinality(&self) -> usize;
|
||||
fn inputs(&self) -> &[InterfaceType];
|
||||
fn outputs(&self) -> &[InterfaceType];
|
||||
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>;
|
||||
}
|
||||
|
||||
pub trait LocalImport {
|
||||
fn inputs_cardinality(&self) -> usize;
|
||||
fn outputs_cardinality(&self) -> usize;
|
||||
fn inputs(&self) -> &[InterfaceType];
|
||||
fn outputs(&self) -> &[InterfaceType];
|
||||
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>;
|
||||
}
|
||||
|
||||
pub trait MemoryView: Deref<Target = [Cell<u8>]> {}
|
||||
|
||||
pub trait Memory<View>
|
||||
where
|
||||
View: MemoryView,
|
||||
{
|
||||
fn view(&self) -> View;
|
||||
}
|
||||
|
||||
pub trait Instance<E, LI, M, MV>
|
||||
where
|
||||
E: Export,
|
||||
LI: LocalImport,
|
||||
M: Memory<MV>,
|
||||
MV: MemoryView,
|
||||
{
|
||||
fn export(&self, export_name: &str) -> Option<&E>;
|
||||
fn local_or_import<I: TypedIndex + LocalImportIndex>(&mut self, index: I) -> Option<&LI>;
|
||||
fn memory(&self, index: usize) -> Option<&M>;
|
||||
}
|
||||
|
||||
impl Export for () {
|
||||
fn inputs_cardinality(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn outputs_cardinality(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn inputs(&self) -> &[InterfaceType] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn outputs(&self) -> &[InterfaceType] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn call(&self, _arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalImport for () {
|
||||
fn inputs_cardinality(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn outputs_cardinality(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn inputs(&self) -> &[InterfaceType] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn outputs(&self) -> &[InterfaceType] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn call(&self, _arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct EmptyMemoryView;
|
||||
|
||||
impl MemoryView for EmptyMemoryView {}
|
||||
|
||||
impl Deref for EmptyMemoryView {
|
||||
type Target = [Cell<u8>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory<EmptyMemoryView> for () {
|
||||
fn view(&self) -> EmptyMemoryView {
|
||||
EmptyMemoryView
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, LI, M, MV> Instance<E, LI, M, MV> for ()
|
||||
where
|
||||
E: Export,
|
||||
LI: LocalImport,
|
||||
M: Memory<MV>,
|
||||
MV: MemoryView,
|
||||
{
|
||||
fn export(&self, _export_name: &str) -> Option<&E> {
|
||||
None
|
||||
}
|
||||
|
||||
fn memory(&self, _: usize) -> Option<&M> {
|
||||
None
|
||||
}
|
||||
|
||||
fn local_or_import<I: TypedIndex + LocalImportIndex>(&mut self, _index: I) -> Option<&LI> {
|
||||
None
|
||||
}
|
||||
}
|
67
lib/interface-types/src/interpreter/wasm/values.rs
Normal file
67
lib/interface-types/src/interpreter/wasm/values.rs
Normal file
@ -0,0 +1,67 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub use crate::ast::InterfaceType;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InterfaceValue {
|
||||
Int(isize),
|
||||
Float(f64),
|
||||
Any(isize),
|
||||
String(String),
|
||||
// Seq(…),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
// AnyRef(…),
|
||||
}
|
||||
|
||||
impl From<&InterfaceValue> for InterfaceType {
|
||||
fn from(value: &InterfaceValue) -> Self {
|
||||
match value {
|
||||
InterfaceValue::Int(_) => Self::Int,
|
||||
InterfaceValue::Float(_) => Self::Float,
|
||||
InterfaceValue::Any(_) => Self::Any,
|
||||
InterfaceValue::String(_) => Self::String,
|
||||
InterfaceValue::I32(_) => Self::I32,
|
||||
InterfaceValue::I64(_) => Self::I64,
|
||||
InterfaceValue::F32(_) => Self::F32,
|
||||
InterfaceValue::F64(_) => Self::F64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InterfaceValue {
|
||||
fn default() -> Self {
|
||||
Self::I32(0)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_x_for_interface_value {
|
||||
($native_type:ty, $value_variant:ident) => {
|
||||
impl From<$native_type> for InterfaceValue {
|
||||
fn from(n: $native_type) -> Self {
|
||||
Self::$value_variant(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&InterfaceValue> for $native_type {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(w: &InterfaceValue) -> Result<Self, Self::Error> {
|
||||
match w {
|
||||
InterfaceValue::$value_variant(n) => Ok(n.clone()),
|
||||
_ => Err("Invalid cast."),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_x_for_interface_value!(String, String);
|
||||
from_x_for_interface_value!(i32, I32);
|
||||
from_x_for_interface_value!(i64, I64);
|
||||
from_x_for_interface_value!(f32, F32);
|
||||
from_x_for_interface_value!(f64, F64);
|
53
lib/interface-types/src/lib.rs
Normal file
53
lib/interface-types/src/lib.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//! This crate contains an implementation of [WebAssembly Interface
|
||||
//! Types][wit] (abbreviated WIT). It is composed of 4 parts:
|
||||
//!
|
||||
//! 1. AST: To represent the WIT language as a tree (which is not
|
||||
//! really abstract). This is the central representation of the
|
||||
//! language.
|
||||
//! 2. Decoders: To read the AST from a particular data representation;
|
||||
//! for instance, `decoders::binary` reads the AST from a binary.
|
||||
//! 3. Encoders: To write the AST into a particular format; for
|
||||
//! instance, `encoders::wat` writes the AST into a string
|
||||
//! representing WIT with its textual format.
|
||||
|
||||
//! 4. Interpreter: WIT defines a concept called Adapters. An adapter
|
||||
//! contains a set of instructions. So, in more details, this
|
||||
//! module contains:
|
||||
//! * A very light and generic stack implementation, exposing only
|
||||
//! the operations required by the interpreter,
|
||||
//! * A stack-based interpreter, defined by:
|
||||
//! * A compiler that transforms a set of instructions into a
|
||||
//! set of executable instructions,
|
||||
//! * A stack,
|
||||
//! * A runtime that holds the “invocation inputs” (arguments
|
||||
//! of the interpreter), the stack, and the WebAssembly
|
||||
//! instance (which holds the exports, the imports, the
|
||||
//! memories, the tables etc.),
|
||||
//! * An hypothetic WebAssembly runtime, represented as a set of
|
||||
//! enums, types, and traits —basically this is the part a
|
||||
//! runtime should take a look to use the
|
||||
//! `wasmer-interface-types` crate—.
|
||||
//!
|
||||
//!
|
||||
//! [wit]: https://github.com/WebAssembly/interface-types
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
missing_docs,
|
||||
nonstandard_style,
|
||||
unreachable_patterns,
|
||||
unused_imports,
|
||||
unused_mut,
|
||||
unused_unsafe,
|
||||
unused_variables
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png")]
|
||||
|
||||
pub mod ast;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod decoders;
|
||||
pub mod encoders;
|
||||
pub mod interpreter;
|
124
lib/interface-types/src/macros.rs
Normal file
124
lib/interface-types/src/macros.rs
Normal file
@ -0,0 +1,124 @@
|
||||
/// This macro runs a parser, extracts the next input and the parser
|
||||
/// output, and positions the next input on `$input`.
|
||||
macro_rules! consume {
|
||||
(($input:ident, $parser_output:ident) = $parser_expression:expr) => {
|
||||
let (next_input, $parser_output) = $parser_expression;
|
||||
$input = next_input;
|
||||
};
|
||||
}
|
||||
|
||||
/// This macro creates an executable instruction for the interpreter.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// The following example creates a `foo` executable instruction,
|
||||
/// which takes 2 arguments (`x` and `y`), and does something
|
||||
/// mysterious by using the `interpreter::Runtime` API.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// executable_instruction!(
|
||||
/// foo(x: u64, y: u64, instruction_name: String) -> _ {
|
||||
/// // ^ output type is purposely blank
|
||||
/// // ^^^^^^^^^^^^^^^^ the instruction name, for debugging purposes
|
||||
/// // ^ the `y` argument
|
||||
/// // ^ the `x` argument
|
||||
///
|
||||
/// // an executable instruction is a closure that takes a `Runtime` instance
|
||||
/// move |runtime| -> _ {
|
||||
/// // Do something.
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Check the existing executable instruction to get more examples.
|
||||
macro_rules! executable_instruction {
|
||||
($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => {
|
||||
use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable};
|
||||
|
||||
pub(crate) fn $name<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
$($argument_name: $argument_type),*
|
||||
) -> ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: wasm::structures::Export,
|
||||
LocalImport: wasm::structures::LocalImport,
|
||||
Memory: wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: wasm::structures::MemoryView,
|
||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
Box::new($implementation)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! test_executable_instruction {
|
||||
(
|
||||
$test_name:ident =
|
||||
instructions: [ $($instructions:expr),* $(,)* ],
|
||||
invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ],
|
||||
instance: $instance:expr,
|
||||
stack: [ $($stack:expr),* $(,)* ]
|
||||
$(,)*
|
||||
) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case, unused)]
|
||||
fn $test_name() {
|
||||
use crate::interpreter::{
|
||||
instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
|
||||
stack::Stackable,
|
||||
wasm::values::{InterfaceType, InterfaceValue},
|
||||
Instruction, Interpreter,
|
||||
};
|
||||
use std::{cell::Cell, collections::HashMap, convert::TryInto};
|
||||
|
||||
let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> =
|
||||
(&vec![$($instructions),*]).try_into().unwrap();
|
||||
|
||||
let invocation_inputs = vec![$($invocation_inputs),*];
|
||||
let mut instance = $instance;
|
||||
let run = interpreter.run(&invocation_inputs, &mut instance);
|
||||
|
||||
assert!(run.is_ok());
|
||||
|
||||
let stack = run.unwrap();
|
||||
|
||||
assert_eq!(stack.as_slice(), &[$($stack),*]);
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$test_name:ident =
|
||||
instructions: [ $($instructions:expr),* $(,)* ],
|
||||
invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ],
|
||||
instance: $instance:expr,
|
||||
error: $error:expr
|
||||
$(,)*
|
||||
) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case, unused)]
|
||||
fn $test_name() {
|
||||
use crate::interpreter::{
|
||||
instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
|
||||
stack::Stackable,
|
||||
wasm::values::{InterfaceType, InterfaceValue},
|
||||
Instruction, Interpreter,
|
||||
};
|
||||
use std::{cell::Cell, collections::HashMap, convert::TryInto};
|
||||
|
||||
let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> =
|
||||
(&vec![$($instructions),*]).try_into().unwrap();
|
||||
|
||||
let invocation_inputs = vec![$($invocation_inputs),*];
|
||||
let mut instance = $instance;
|
||||
let run = interpreter.run(&invocation_inputs, &mut instance);
|
||||
|
||||
assert!(run.is_err());
|
||||
|
||||
let error = run.unwrap_err();
|
||||
|
||||
assert_eq!(error, String::from($error));
|
||||
}
|
||||
};
|
||||
}
|
46
lib/interface-types/tests/binary.rs
Normal file
46
lib/interface-types/tests/binary.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use wasmer_interface_types::{
|
||||
ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction,
|
||||
};
|
||||
|
||||
/// Tests an AST to binary, then binary to AST roundtrip.
|
||||
#[test]
|
||||
fn test_binary_encoding_decoding_roundtrip() {
|
||||
let original_ast = Interfaces {
|
||||
exports: vec![Export {
|
||||
name: "ab",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
}],
|
||||
types: vec![Type::new(
|
||||
"ab",
|
||||
vec!["cd", "e"],
|
||||
vec![InterfaceType::I32, InterfaceType::I32],
|
||||
)],
|
||||
imports: vec![Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I64],
|
||||
}],
|
||||
adapters: vec![Adapter::Import {
|
||||
namespace: "a",
|
||||
name: "b",
|
||||
input_types: vec![InterfaceType::I32],
|
||||
output_types: vec![InterfaceType::I32],
|
||||
instructions: vec![Instruction::ArgumentGet { index: 1 }],
|
||||
}],
|
||||
forwards: vec![Forward { name: "a" }],
|
||||
};
|
||||
|
||||
let mut binary = vec![];
|
||||
|
||||
original_ast
|
||||
.to_bytes(&mut binary)
|
||||
.expect("Failed to encode the AST.");
|
||||
|
||||
let (remainder, ast) = parse::<()>(binary.as_slice()).expect("Failed to decode the AST.");
|
||||
|
||||
assert!(remainder.is_empty());
|
||||
|
||||
assert_eq!(original_ast, ast);
|
||||
}
|
@ -58,6 +58,7 @@ lazy_static! {
|
||||
"llvm-config".into(),
|
||||
format!("llvm-config-{}", CRATE_VERSION.major),
|
||||
format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
|
||||
format!("llvm-config{}{}", CRATE_VERSION.major, CRATE_VERSION.minor),
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -59,6 +59,7 @@ enum WasmTrapType {
|
||||
MemoryOutOfBounds = 2,
|
||||
CallIndirectOOB = 3,
|
||||
IllegalArithmetic = 4,
|
||||
MisalignedAtomicAccess = 5,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
@ -285,7 +286,7 @@ void module_delete(WasmModule *module) { delete module; }
|
||||
unsafe_unwind(new BreakpointException(callback));
|
||||
}
|
||||
|
||||
bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
bool cxx_invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
void *params, void *results, WasmTrapType *trap_out,
|
||||
box_any_t *user_error, void *invoke_env) noexcept {
|
||||
try {
|
||||
|
@ -24,13 +24,13 @@ use std::{
|
||||
use wasmer_runtime_core::{
|
||||
backend::{
|
||||
sys::{Memory, Protect},
|
||||
CacheGen, RunnableModule,
|
||||
CacheGen, ExceptionCode, RunnableModule,
|
||||
},
|
||||
cache::Error as CacheError,
|
||||
module::ModuleInfo,
|
||||
state::ModuleStateMap,
|
||||
structures::TypedIndex,
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm, vmcalls,
|
||||
};
|
||||
@ -59,18 +59,55 @@ extern "C" {
|
||||
fn throw_any(data: *mut dyn Any) -> !;
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
fn invoke_trampoline(
|
||||
fn cxx_invoke_trampoline(
|
||||
trampoline: Trampoline,
|
||||
vmctx_ptr: *mut vm::Ctx,
|
||||
func_ptr: NonNull<vm::Func>,
|
||||
params: *const u64,
|
||||
results: *mut u64,
|
||||
trap_out: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
trap_out: *mut i32,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
/// `invoke_trampoline` is a wrapper around `cxx_invoke_trampoline`, for fixing up the obsoleted
|
||||
/// `trap_out` in the C++ part.
|
||||
unsafe extern "C" fn invoke_trampoline(
|
||||
trampoline: Trampoline,
|
||||
vmctx_ptr: *mut vm::Ctx,
|
||||
func_ptr: NonNull<vm::Func>,
|
||||
params: *const u64,
|
||||
results: *mut u64,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let mut trap_out: i32 = -1;
|
||||
let ret = cxx_invoke_trampoline(
|
||||
trampoline,
|
||||
vmctx_ptr,
|
||||
func_ptr,
|
||||
params,
|
||||
results,
|
||||
&mut trap_out,
|
||||
error_out,
|
||||
invoke_env,
|
||||
);
|
||||
// Translate trap code if an error occurred.
|
||||
if !ret && (*error_out).is_none() && trap_out != -1 {
|
||||
*error_out = Some(Box::new(match trap_out {
|
||||
0 => ExceptionCode::Unreachable,
|
||||
1 => ExceptionCode::IncorrectCallIndirectSignature,
|
||||
2 => ExceptionCode::MemoryOutOfBounds,
|
||||
3 => ExceptionCode::CallIndirectOOB,
|
||||
4 => ExceptionCode::IllegalArithmetic,
|
||||
5 => ExceptionCode::MisalignedAtomicAccess,
|
||||
_ => return ret,
|
||||
}));
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
static SIGNAL_HANDLER_INSTALLED: Once = Once::new();
|
||||
|
||||
fn get_callbacks() -> Callbacks {
|
||||
@ -206,7 +243,10 @@ impl LLVMBackend {
|
||||
|
||||
let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer));
|
||||
|
||||
#[cfg(all(any(target_os = "linux", target_os = "macos"), target_arch = "x86_64"))]
|
||||
#[cfg(all(
|
||||
any(target_os = "freebsd", target_os = "linux", target_os = "macos"),
|
||||
target_arch = "x86_64"
|
||||
))]
|
||||
{
|
||||
use super::stackmap::{self, StkMapRecord, StkSizeRecord};
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -1,3 +1,9 @@
|
||||
//! Code for dealing with [LLVM][llvm-intrinsics] and VM intrinsics.
|
||||
//!
|
||||
//! VM intrinsics are used to interact with the host VM.
|
||||
//!
|
||||
//! [llvm-intrinsics]: https://llvm.org/docs/LangRef.html#intrinsic-functions
|
||||
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
@ -34,6 +40,7 @@ fn type_to_llvm_ptr<'ctx>(intrinsics: &Intrinsics<'ctx>, ty: Type) -> PointerTyp
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct containing LLVM and VM intrinsics.
|
||||
pub struct Intrinsics<'ctx> {
|
||||
pub ctlz_i32: FunctionValue<'ctx>,
|
||||
pub ctlz_i64: FunctionValue<'ctx>,
|
||||
@ -151,6 +158,7 @@ pub struct Intrinsics<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> Intrinsics<'ctx> {
|
||||
/// Create an [`Intrinsics`] for the given [`Context`].
|
||||
pub fn declare(module: &Module<'ctx>, context: &'ctx Context) -> Self {
|
||||
let void_ty = context.void_type();
|
||||
let i1_ty = context.bool_type();
|
||||
|
@ -53,7 +53,10 @@ pub enum StackmapEntryKind {
|
||||
}
|
||||
|
||||
impl StackmapEntry {
|
||||
#[cfg(all(any(target_os = "linux", target_os = "macos"), target_arch = "x86_64"))]
|
||||
#[cfg(all(
|
||||
any(target_os = "freebsd", target_os = "linux", target_os = "macos"),
|
||||
target_arch = "x86_64"
|
||||
))]
|
||||
pub fn populate_msm(
|
||||
&self,
|
||||
module_info: &ModuleInfo,
|
||||
|
@ -165,12 +165,10 @@ mod tests {
|
||||
}
|
||||
|
||||
let err = result.unwrap_err();
|
||||
match err {
|
||||
RuntimeError::Error { data } => {
|
||||
assert!(data.downcast_ref::<ExecutionLimitExceededError>().is_some());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
assert!(err
|
||||
.0
|
||||
.downcast_ref::<ExecutionLimitExceededError>()
|
||||
.is_some());
|
||||
|
||||
// verify it used the correct number of points
|
||||
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
|
||||
|
@ -15,7 +15,7 @@ auto-generated documentation.
|
||||
|
||||
Since the Wasmer runtime is written in Rust, the C and C++ API are
|
||||
designed to work hand-in-hand with its shared library. The C and C++
|
||||
header files, namely [`wasmer.h`] and [`wasmer.hh`] are documented
|
||||
header files, namely [`wasmer.h`] and `wasmer.hh` are documented
|
||||
here. Their source code can be found in the source tree of this
|
||||
crate. They are automatically generated, and always up-to-date in this
|
||||
repository. The C and C++ header files along with the runtime shared
|
||||
@ -23,7 +23,6 @@ libraries (`.so`, `.dylib`, `.dll`) can also be downloaded in the
|
||||
Wasmer [release page].
|
||||
|
||||
[`wasmer.h`]: ./wasmer_8h.html
|
||||
[`wasmer.hh`]: ./wasmer_8hh.html
|
||||
[release page]: https://github.com/wasmerio/wasmer/releases
|
||||
|
||||
Here is a simple example to use the C API:
|
||||
|
@ -118,7 +118,6 @@ WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = doc/index.md
|
||||
INPUT += wasmer.h
|
||||
INPUT += wasmer.hh
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hh
|
||||
|
@ -1,11 +1,12 @@
|
||||
//! Read runtime errors.
|
||||
|
||||
use libc::{c_char, c_int};
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
ptr, slice,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
static LAST_ERROR: RefCell<Option<Box<dyn Error>>> = RefCell::new(None);
|
||||
|
@ -107,7 +107,7 @@ pub mod table;
|
||||
pub mod trampoline;
|
||||
pub mod value;
|
||||
|
||||
/// The `wasmer_result_t` struct is a type that represents either a
|
||||
/// The `wasmer_result_t` enum is a type that represents either a
|
||||
/// success, or a failure.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
@ -119,15 +119,26 @@ pub enum wasmer_result_t {
|
||||
WASMER_ERROR = 2,
|
||||
}
|
||||
|
||||
/// The `wasmer_limits_t` struct is a type that describes a memory
|
||||
/// options. See the `wasmer_memory_t` struct or the
|
||||
/// `wasmer_memory_new()` function to get more information.
|
||||
#[repr(C)]
|
||||
pub struct wasmer_limits_t {
|
||||
/// The minimum number of allowed pages.
|
||||
pub min: u32,
|
||||
|
||||
/// The maximum number of allowed pages.
|
||||
pub max: wasmer_limit_option_t,
|
||||
}
|
||||
|
||||
/// The `wasmer_limit_option_t` struct represents an optional limit
|
||||
/// for `wasmer_limits_t`.
|
||||
#[repr(C)]
|
||||
pub struct wasmer_limit_option_t {
|
||||
/// Whether the limit is set.
|
||||
pub has_some: bool,
|
||||
|
||||
/// The limit value.
|
||||
pub some: u32,
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,66 @@
|
||||
//! Create, read, write, grow, destroy memory of an instance.
|
||||
|
||||
use crate::{error::update_last_error, error::CApiError, wasmer_limits_t, wasmer_result_t};
|
||||
use std::cell::Cell;
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
wasmer_limits_t, wasmer_result_t,
|
||||
};
|
||||
use std::{cell::Cell, ptr};
|
||||
use wasmer_runtime::Memory;
|
||||
use wasmer_runtime_core::{
|
||||
types::MemoryDescriptor,
|
||||
units::{Bytes, Pages},
|
||||
};
|
||||
|
||||
/// Opaque pointer to a `wasmer_runtime::Memory` value in Rust.
|
||||
///
|
||||
/// A `wasmer_runtime::Memory` represents a WebAssembly memory. It is
|
||||
/// possible to create one with `wasmer_memory_new()` and pass it as
|
||||
/// imports of an instance, or to read it from exports of an instance
|
||||
/// with `wasmer_export_to_memory()`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_memory_t;
|
||||
|
||||
/// Creates a new Memory for the given descriptor and initializes the given
|
||||
/// pointer to pointer to a pointer to the new memory.
|
||||
/// Creates a new empty WebAssembly memory for the given descriptor.
|
||||
///
|
||||
/// The caller owns the object and should call `wasmer_memory_destroy` to free it.
|
||||
/// The result is stored in the first argument `memory` if successful,
|
||||
/// i.e. when the function returns
|
||||
/// `wasmer_result_t::WASMER_OK`. Otherwise,
|
||||
/// `wasmer_result_t::WASMER_ERROR` is returned, and
|
||||
/// `wasmer_last_error_length()` with `wasmer_last_error_message()`
|
||||
/// must be used to read the error message.
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
/// The caller owns the memory and is responsible to free it with
|
||||
/// `wasmer_memory_destroy()`.
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
/// and `wasmer_last_error_message` to get an error message.
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// // 1. The memory object.
|
||||
/// wasmer_memory_t *memory = NULL;
|
||||
///
|
||||
/// // 2. The memory descriptor.
|
||||
/// wasmer_limits_t memory_descriptor = {
|
||||
/// .min = 10,
|
||||
/// .max = {
|
||||
/// .has_some = true,
|
||||
/// .some = 15,
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// // 3. Initialize the memory.
|
||||
/// wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
|
||||
///
|
||||
/// if (result != WASMER_OK) {
|
||||
/// int error_length = wasmer_last_error_length();
|
||||
/// char *error = malloc(error_length);
|
||||
/// wasmer_last_error_message(error, error_length);
|
||||
/// // Do something with `error`…
|
||||
/// }
|
||||
///
|
||||
/// // 4. Free the memory!
|
||||
/// wasmer_memory_destroy(memory);
|
||||
/// ```
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_memory_new(
|
||||
memory: *mut *mut wasmer_memory_t,
|
||||
@ -53,53 +93,139 @@ pub unsafe extern "C" fn wasmer_memory_new(
|
||||
wasmer_result_t::WASMER_OK
|
||||
}
|
||||
|
||||
/// Grows a Memory by the given number of pages.
|
||||
/// Grows a memory by the given number of pages (of 65Kb each).
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
/// The functions return `wasmer_result_t::WASMER_OK` upon success,
|
||||
/// `wasmer_result_t::WASMER_ERROR` otherwise. Use
|
||||
/// `wasmer_last_error_length()` with `wasmer_last_error_message()` to
|
||||
/// read the error message.
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
/// and `wasmer_last_error_message` to get an error message.
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// wasmer_result_t result = wasmer_memory_grow(memory, 10);
|
||||
///
|
||||
/// if (result != WASMER_OK) {
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_memory_grow(memory: *mut wasmer_memory_t, delta: u32) -> wasmer_result_t {
|
||||
if memory.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "`memory` is NULL.".to_string(),
|
||||
});
|
||||
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
|
||||
let memory = unsafe { &*(memory as *mut Memory) };
|
||||
let delta_result = memory.grow(Pages(delta));
|
||||
|
||||
match delta_result {
|
||||
Ok(_) => wasmer_result_t::WASMER_OK,
|
||||
Err(grow_error) => {
|
||||
update_last_error(grow_error);
|
||||
|
||||
wasmer_result_t::WASMER_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current length in pages of the given memory
|
||||
/// Reads the current length (in pages) of the given memory.
|
||||
///
|
||||
/// The function returns zero if `memory` is a null pointer.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// uint32_t memory_length = wasmer_memory_length(memory);
|
||||
///
|
||||
/// printf("Memory pages length: %d\n", memory_length);
|
||||
/// ```
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_memory_length(memory: *const wasmer_memory_t) -> u32 {
|
||||
if memory.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let memory = unsafe { &*(memory as *const Memory) };
|
||||
let Pages(len) = memory.size();
|
||||
len
|
||||
let Pages(length) = memory.size();
|
||||
|
||||
length
|
||||
}
|
||||
|
||||
/// Gets the start pointer to the bytes within a Memory
|
||||
/// Gets a pointer to the beginning of the contiguous memory data
|
||||
/// bytes.
|
||||
///
|
||||
/// The function returns `NULL` if `memory` is a null pointer.
|
||||
///
|
||||
/// Note that when the memory grows, it can be reallocated, and thus
|
||||
/// the returned pointer can be invalidated.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// uint8_t *memory_data = wasmer_memory_data(memory);
|
||||
/// char *str = (char*) malloc(sizeof(char) * 7);
|
||||
///
|
||||
/// for (uint32_t nth = 0; nth < 7; ++nth) {
|
||||
/// str[nth] = (char) memory_data[nth];
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_memory_data(mem: *const wasmer_memory_t) -> *mut u8 {
|
||||
let memory = unsafe { &*(mem as *const Memory) };
|
||||
pub extern "C" fn wasmer_memory_data(memory: *const wasmer_memory_t) -> *mut u8 {
|
||||
if memory.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let memory = unsafe { &*(memory as *const Memory) };
|
||||
|
||||
memory.view::<u8>()[..].as_ptr() as *mut Cell<u8> as *mut u8
|
||||
}
|
||||
|
||||
/// Gets the size in bytes of a Memory
|
||||
/// Gets the size in bytes of the memory data.
|
||||
///
|
||||
/// This function returns 0 if `memory` is a null pointer.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// uint32_t memory_data_length = wasmer_memory_data_length(memory);
|
||||
/// ```
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_memory_data_length(mem: *mut wasmer_memory_t) -> u32 {
|
||||
let memory = mem as *mut Memory;
|
||||
let Bytes(len) = unsafe { (*memory).size().bytes() };
|
||||
len as u32
|
||||
pub extern "C" fn wasmer_memory_data_length(memory: *mut wasmer_memory_t) -> u32 {
|
||||
if memory.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let memory = unsafe { &*(memory as *const Memory) };
|
||||
let Bytes(length) = memory.size().bytes();
|
||||
|
||||
length as u32
|
||||
}
|
||||
|
||||
/// Frees memory for the given Memory
|
||||
/// Frees memory for the given `wasmer_memory_t`.
|
||||
///
|
||||
/// Check the `wasmer_memory_new()` function to get a complete
|
||||
/// example.
|
||||
///
|
||||
/// If `memory` is a null pointer, this function does nothing.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// // Get a memory.
|
||||
/// wasmer_memory_t *memory = NULL;
|
||||
/// wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
|
||||
///
|
||||
/// // Destroy the memory.
|
||||
/// wasmer_memory_destroy(memory);
|
||||
/// ```
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) {
|
||||
|
@ -3,16 +3,32 @@
|
||||
use wasmer_runtime::Value;
|
||||
use wasmer_runtime_core::types::Type;
|
||||
|
||||
/// Represents all possibles WebAssembly value types.
|
||||
///
|
||||
/// See `wasmer_value_t` to get a complete example.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u32)]
|
||||
#[derive(Clone)]
|
||||
pub enum wasmer_value_tag {
|
||||
/// Represents the `i32` WebAssembly type.
|
||||
WASM_I32,
|
||||
|
||||
/// Represents the `i64` WebAssembly type.
|
||||
WASM_I64,
|
||||
|
||||
/// Represents the `f32` WebAssembly type.
|
||||
WASM_F32,
|
||||
|
||||
/// Represents the `f64` WebAssembly type.
|
||||
WASM_F64,
|
||||
}
|
||||
|
||||
/// Represents a WebAssembly value.
|
||||
///
|
||||
/// This is a [Rust union][rust-union], which is equivalent to the C
|
||||
/// union. See `wasmer_value_t` to get a complete example.
|
||||
///
|
||||
/// [rust-union]: https://doc.rust-lang.org/reference/items/unions.html
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(non_snake_case)]
|
||||
@ -23,10 +39,33 @@ pub union wasmer_value {
|
||||
pub F64: f64,
|
||||
}
|
||||
|
||||
/// Represents a WebAssembly type and value pair,
|
||||
/// i.e. `wasmer_value_tag` and `wasmer_value`. Since the latter is an
|
||||
/// union, it's the safe way to read or write a WebAssembly value in
|
||||
/// C.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// // Create a WebAssembly value.
|
||||
/// wasmer_value_t wasm_value = {
|
||||
/// .tag = WASM_I32,
|
||||
/// .value.I32 = 42,
|
||||
/// };
|
||||
///
|
||||
/// // Read a WebAssembly value.
|
||||
/// if (wasm_value.tag == WASM_I32) {
|
||||
/// int32_t x = wasm_value.value.I32;
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_value_t {
|
||||
/// The value type.
|
||||
pub tag: wasmer_value_tag,
|
||||
|
||||
/// The value.
|
||||
pub value: wasmer_value,
|
||||
}
|
||||
|
||||
|
Binary file not shown.
@ -7,12 +7,13 @@
|
||||
int main()
|
||||
{
|
||||
wasmer_memory_t *memory = NULL;
|
||||
wasmer_limits_t descriptor;
|
||||
descriptor.min = 10;
|
||||
wasmer_limit_option_t max;
|
||||
max.has_some = true;
|
||||
max.some = 15;
|
||||
descriptor.max = max;
|
||||
wasmer_limits_t descriptor = {
|
||||
.min = 10,
|
||||
.max = {
|
||||
.has_some = true,
|
||||
.some = 15,
|
||||
},
|
||||
};
|
||||
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
|
||||
printf("Memory result: %d\n", memory_result);
|
||||
assert(memory_result == WASMER_OK);
|
||||
|
@ -86,7 +86,7 @@ enum wasmer_import_export_kind {
|
||||
typedef uint32_t wasmer_import_export_kind;
|
||||
|
||||
/**
|
||||
* The `wasmer_result_t` struct is a type that represents either a
|
||||
* The `wasmer_result_t` enum is a type that represents either a
|
||||
* success, or a failure.
|
||||
*/
|
||||
typedef enum {
|
||||
@ -100,10 +100,27 @@ typedef enum {
|
||||
WASMER_ERROR = 2,
|
||||
} wasmer_result_t;
|
||||
|
||||
/**
|
||||
* Represents all possibles WebAssembly value types.
|
||||
*
|
||||
* See `wasmer_value_t` to get a complete example.
|
||||
*/
|
||||
enum wasmer_value_tag {
|
||||
/**
|
||||
* Represents the `i32` WebAssembly type.
|
||||
*/
|
||||
WASM_I32,
|
||||
/**
|
||||
* Represents the `i64` WebAssembly type.
|
||||
*/
|
||||
WASM_I64,
|
||||
/**
|
||||
* Represents the `f32` WebAssembly type.
|
||||
*/
|
||||
WASM_F32,
|
||||
/**
|
||||
* Represents the `f64` WebAssembly type.
|
||||
*/
|
||||
WASM_F64,
|
||||
};
|
||||
typedef uint32_t wasmer_value_tag;
|
||||
@ -162,6 +179,14 @@ typedef struct {
|
||||
|
||||
} wasmer_export_func_t;
|
||||
|
||||
/**
|
||||
* Represents a WebAssembly value.
|
||||
*
|
||||
* This is a [Rust union][rust-union], which is equivalent to the C
|
||||
* union. See `wasmer_value_t` to get a complete example.
|
||||
*
|
||||
* [rust-union]: https://doc.rust-lang.org/reference/items/unions.html
|
||||
*/
|
||||
typedef union {
|
||||
int32_t I32;
|
||||
int64_t I64;
|
||||
@ -169,8 +194,36 @@ typedef union {
|
||||
double F64;
|
||||
} wasmer_value;
|
||||
|
||||
/**
|
||||
* Represents a WebAssembly type and value pair,
|
||||
* i.e. `wasmer_value_tag` and `wasmer_value`. Since the latter is an
|
||||
* union, it's the safe way to read or write a WebAssembly value in
|
||||
* C.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* // Create a WebAssembly value.
|
||||
* wasmer_value_t wasm_value = {
|
||||
* .tag = WASM_I32,
|
||||
* .value.I32 = 42,
|
||||
* };
|
||||
*
|
||||
* // Read a WebAssembly value.
|
||||
* if (wasm_value.tag == WASM_I32) {
|
||||
* int32_t x = wasm_value.value.I32;
|
||||
* // …
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* The value type.
|
||||
*/
|
||||
wasmer_value_tag tag;
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
wasmer_value value;
|
||||
} wasmer_value_t;
|
||||
|
||||
@ -181,6 +234,14 @@ typedef struct {
|
||||
|
||||
} wasmer_export_t;
|
||||
|
||||
/**
|
||||
* Opaque pointer to a `wasmer_runtime::Memory` value in Rust.
|
||||
*
|
||||
* A `wasmer_runtime::Memory` represents a WebAssembly memory. It is
|
||||
* possible to create one with `wasmer_memory_new()` and pass it as
|
||||
* imports of an instance, or to read it from exports of an instance
|
||||
* with `wasmer_export_to_memory()`.
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
} wasmer_memory_t;
|
||||
@ -273,13 +334,34 @@ typedef struct {
|
||||
|
||||
} wasmer_instance_context_t;
|
||||
|
||||
/**
|
||||
* The `wasmer_limit_option_t` struct repreesents an optional limit
|
||||
* for `wasmer_limits_t`.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* Whether the limit is set.
|
||||
*/
|
||||
bool has_some;
|
||||
/**
|
||||
* The limit value.
|
||||
*/
|
||||
uint32_t some;
|
||||
} wasmer_limit_option_t;
|
||||
|
||||
/**
|
||||
* The `wasmer_limits_t` struct is a type that describes a memory
|
||||
* options. See the `wasmer_memory_t` struct or the
|
||||
* `wasmer_memory_new()` function to get more information.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* The minimum number of allowed pages.
|
||||
*/
|
||||
uint32_t min;
|
||||
/**
|
||||
* The maximum number of allowed pages.
|
||||
*/
|
||||
wasmer_limit_option_t max;
|
||||
} wasmer_limits_t;
|
||||
|
||||
@ -1057,45 +1139,137 @@ int wasmer_last_error_length(void);
|
||||
int wasmer_last_error_message(char *buffer, int length);
|
||||
|
||||
/**
|
||||
* Gets the start pointer to the bytes within a Memory
|
||||
* Gets a pointer to the beginning of the contiguous memory data
|
||||
* bytes.
|
||||
*
|
||||
* The function returns `NULL` if `memory` is a null pointer.
|
||||
*
|
||||
* Note that when the memory grows, it can be reallocated, and thus
|
||||
* the returned pointer can be invalidated.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* uint8_t *memory_data = wasmer_memory_data(memory);
|
||||
* char *str = (char*) malloc(sizeof(char) * 7);
|
||||
*
|
||||
* for (uint32_t nth = 0; nth < 7; ++nth) {
|
||||
* str[nth] = (char) memory_data[nth];
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
uint8_t *wasmer_memory_data(const wasmer_memory_t *mem);
|
||||
uint8_t *wasmer_memory_data(const wasmer_memory_t *memory);
|
||||
|
||||
/**
|
||||
* Gets the size in bytes of a Memory
|
||||
* Gets the size in bytes of the memory data.
|
||||
*
|
||||
* This function returns 0 if `memory` is a null pointer.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* uint32_t memory_data_length = wasmer_memory_data_length(memory);
|
||||
* ```
|
||||
*/
|
||||
uint32_t wasmer_memory_data_length(wasmer_memory_t *mem);
|
||||
uint32_t wasmer_memory_data_length(wasmer_memory_t *memory);
|
||||
|
||||
/**
|
||||
* Frees memory for the given Memory
|
||||
* Frees memory for the given `wasmer_memory_t`.
|
||||
*
|
||||
* Check the `wasmer_memory_new()` function to get a complete
|
||||
* example.
|
||||
*
|
||||
* If `memory` is a null pointer, this function does nothing.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* // Get a memory.
|
||||
* wasmer_memory_t *memory = NULL;
|
||||
* wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
|
||||
*
|
||||
* // Destroy the memory.
|
||||
* wasmer_memory_destroy(memory);
|
||||
* ```
|
||||
*/
|
||||
void wasmer_memory_destroy(wasmer_memory_t *memory);
|
||||
|
||||
/**
|
||||
* Grows a Memory by the given number of pages.
|
||||
* Grows a memory by the given number of pages (of 65Kb each).
|
||||
*
|
||||
* Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
* The functions return `wasmer_result_t::WASMER_OK` upon success,
|
||||
* `wasmer_result_t::WASMER_ERROR` otherwise. Use
|
||||
* `wasmer_last_error_length()` with `wasmer_last_error_message()` to
|
||||
* read the error message.
|
||||
*
|
||||
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
* and `wasmer_last_error_message` to get an error message.
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* wasmer_result_t result = wasmer_memory_grow(memory, 10);
|
||||
*
|
||||
* if (result != WASMER_OK) {
|
||||
* // …
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta);
|
||||
|
||||
/**
|
||||
* Returns the current length in pages of the given memory
|
||||
* Reads the current length (in pages) of the given memory.
|
||||
*
|
||||
* The function returns zero if `memory` is a null pointer.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* uint32_t memory_length = wasmer_memory_length(memory);
|
||||
*
|
||||
* printf("Memory pages length: %d\n", memory_length);
|
||||
* ```
|
||||
*/
|
||||
uint32_t wasmer_memory_length(const wasmer_memory_t *memory);
|
||||
|
||||
/**
|
||||
* Creates a new Memory for the given descriptor and initializes the given
|
||||
* pointer to pointer to a pointer to the new memory.
|
||||
* Creates a new empty WebAssembly memory for the given descriptor.
|
||||
*
|
||||
* The caller owns the object and should call `wasmer_memory_destroy` to free it.
|
||||
* The result is stored in the first argument `memory` if successful,
|
||||
* i.e. when the function returns
|
||||
* `wasmer_result_t::WASMER_OK`. Otherwise,
|
||||
* `wasmer_result_t::WASMER_ERROR` is returned, and
|
||||
* `wasmer_last_error_length()` with `wasmer_last_error_message()`
|
||||
* must be used to read the error message.
|
||||
*
|
||||
* Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
* The caller owns the memory and is responsible to free it with
|
||||
* `wasmer_memory_destroy()`.
|
||||
*
|
||||
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
* and `wasmer_last_error_message` to get an error message.
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* // 1. The memory object.
|
||||
* wasmer_memory_t *memory = NULL;
|
||||
*
|
||||
* // 2. The memory descriptor.
|
||||
* wasmer_limits_t memory_descriptor = {
|
||||
* .min = 10,
|
||||
* .max = {
|
||||
* .has_some = true,
|
||||
* .some = 15,
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* // 3. Initialize the memory.
|
||||
* wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
|
||||
*
|
||||
* if (result != WASMER_OK) {
|
||||
* int error_length = wasmer_last_error_length();
|
||||
* char *error = malloc(error_length);
|
||||
* wasmer_last_error_message(error, error_length);
|
||||
* // Do something with `error`…
|
||||
* }
|
||||
*
|
||||
* // 4. Free the memory!
|
||||
* wasmer_memory_destroy(memory);
|
||||
* ```
|
||||
*/
|
||||
wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits);
|
||||
|
||||
|
@ -65,7 +65,7 @@ enum class wasmer_import_export_kind : uint32_t {
|
||||
WASM_TABLE = 3,
|
||||
};
|
||||
|
||||
/// The `wasmer_result_t` struct is a type that represents either a
|
||||
/// The `wasmer_result_t` enum is a type that represents either a
|
||||
/// success, or a failure.
|
||||
enum class wasmer_result_t {
|
||||
/// Represents a success.
|
||||
@ -74,10 +74,17 @@ enum class wasmer_result_t {
|
||||
WASMER_ERROR = 2,
|
||||
};
|
||||
|
||||
/// Represents all possibles WebAssembly value types.
|
||||
///
|
||||
/// See `wasmer_value_t` to get a complete example.
|
||||
enum class wasmer_value_tag : uint32_t {
|
||||
/// Represents the `i32` WebAssembly type.
|
||||
WASM_I32,
|
||||
/// Represents the `i64` WebAssembly type.
|
||||
WASM_I64,
|
||||
/// Represents the `f32` WebAssembly type.
|
||||
WASM_F32,
|
||||
/// Represents the `f64` WebAssembly type.
|
||||
WASM_F64,
|
||||
};
|
||||
|
||||
@ -125,6 +132,12 @@ struct wasmer_export_func_t {
|
||||
|
||||
};
|
||||
|
||||
/// Represents a WebAssembly value.
|
||||
///
|
||||
/// This is a [Rust union][rust-union], which is equivalent to the C
|
||||
/// union. See `wasmer_value_t` to get a complete example.
|
||||
///
|
||||
/// [rust-union]: https://doc.rust-lang.org/reference/items/unions.html
|
||||
union wasmer_value {
|
||||
int32_t I32;
|
||||
int64_t I64;
|
||||
@ -132,8 +145,30 @@ union wasmer_value {
|
||||
double F64;
|
||||
};
|
||||
|
||||
/// Represents a WebAssembly type and value pair,
|
||||
/// i.e. `wasmer_value_tag` and `wasmer_value`. Since the latter is an
|
||||
/// union, it's the safe way to read or write a WebAssembly value in
|
||||
/// C.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// // Create a WebAssembly value.
|
||||
/// wasmer_value_t wasm_value = {
|
||||
/// .tag = WASM_I32,
|
||||
/// .value.I32 = 42,
|
||||
/// };
|
||||
///
|
||||
/// // Read a WebAssembly value.
|
||||
/// if (wasm_value.tag == WASM_I32) {
|
||||
/// int32_t x = wasm_value.value.I32;
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
struct wasmer_value_t {
|
||||
/// The value type.
|
||||
wasmer_value_tag tag;
|
||||
/// The value.
|
||||
wasmer_value value;
|
||||
};
|
||||
|
||||
@ -142,6 +177,12 @@ struct wasmer_export_t {
|
||||
|
||||
};
|
||||
|
||||
/// Opaque pointer to a `wasmer_runtime::Memory` value in Rust.
|
||||
///
|
||||
/// A `wasmer_runtime::Memory` represents a WebAssembly memory. It is
|
||||
/// possible to create one with `wasmer_memory_new()` and pass it as
|
||||
/// imports of an instance, or to read it from exports of an instance
|
||||
/// with `wasmer_export_to_memory()`.
|
||||
struct wasmer_memory_t {
|
||||
|
||||
};
|
||||
@ -228,13 +269,22 @@ struct wasmer_instance_context_t {
|
||||
|
||||
};
|
||||
|
||||
/// The `wasmer_limit_option_t` struct repreesents an optional limit
|
||||
/// for `wasmer_limits_t`.
|
||||
struct wasmer_limit_option_t {
|
||||
/// Whether the limit is set.
|
||||
bool has_some;
|
||||
/// The limit value.
|
||||
uint32_t some;
|
||||
};
|
||||
|
||||
/// The `wasmer_limits_t` struct is a type that describes a memory
|
||||
/// options. See the `wasmer_memory_t` struct or the
|
||||
/// `wasmer_memory_new()` function to get more information.
|
||||
struct wasmer_limits_t {
|
||||
/// The minimum number of allowed pages.
|
||||
uint32_t min;
|
||||
/// The maximum number of allowed pages.
|
||||
wasmer_limit_option_t max;
|
||||
};
|
||||
|
||||
@ -885,35 +935,127 @@ int wasmer_last_error_length();
|
||||
/// ```
|
||||
int wasmer_last_error_message(char *buffer, int length);
|
||||
|
||||
/// Gets the start pointer to the bytes within a Memory
|
||||
uint8_t *wasmer_memory_data(const wasmer_memory_t *mem);
|
||||
/// Gets a pointer to the beginning of the contiguous memory data
|
||||
/// bytes.
|
||||
///
|
||||
/// The function returns `NULL` if `memory` is a null pointer.
|
||||
///
|
||||
/// Note that when the memory grows, it can be reallocated, and thus
|
||||
/// the returned pointer can be invalidated.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// uint8_t *memory_data = wasmer_memory_data(memory);
|
||||
/// char *str = (char*) malloc(sizeof(char) * 7);
|
||||
///
|
||||
/// for (uint32_t nth = 0; nth < 7; ++nth) {
|
||||
/// str[nth] = (char) memory_data[nth];
|
||||
/// }
|
||||
/// ```
|
||||
uint8_t *wasmer_memory_data(const wasmer_memory_t *memory);
|
||||
|
||||
/// Gets the size in bytes of a Memory
|
||||
uint32_t wasmer_memory_data_length(wasmer_memory_t *mem);
|
||||
/// Gets the size in bytes of the memory data.
|
||||
///
|
||||
/// This function returns 0 if `memory` is a null pointer.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// uint32_t memory_data_length = wasmer_memory_data_length(memory);
|
||||
/// ```
|
||||
uint32_t wasmer_memory_data_length(wasmer_memory_t *memory);
|
||||
|
||||
/// Frees memory for the given Memory
|
||||
/// Frees memory for the given `wasmer_memory_t`.
|
||||
///
|
||||
/// Check the `wasmer_memory_new()` function to get a complete
|
||||
/// example.
|
||||
///
|
||||
/// If `memory` is a null pointer, this function does nothing.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// // Get a memory.
|
||||
/// wasmer_memory_t *memory = NULL;
|
||||
/// wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
|
||||
///
|
||||
/// // Destroy the memory.
|
||||
/// wasmer_memory_destroy(memory);
|
||||
/// ```
|
||||
void wasmer_memory_destroy(wasmer_memory_t *memory);
|
||||
|
||||
/// Grows a Memory by the given number of pages.
|
||||
/// Grows a memory by the given number of pages (of 65Kb each).
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
/// The functions return `wasmer_result_t::WASMER_OK` upon success,
|
||||
/// `wasmer_result_t::WASMER_ERROR` otherwise. Use
|
||||
/// `wasmer_last_error_length()` with `wasmer_last_error_message()` to
|
||||
/// read the error message.
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
/// and `wasmer_last_error_message` to get an error message.
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// wasmer_result_t result = wasmer_memory_grow(memory, 10);
|
||||
///
|
||||
/// if (result != WASMER_OK) {
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta);
|
||||
|
||||
/// Returns the current length in pages of the given memory
|
||||
/// Reads the current length (in pages) of the given memory.
|
||||
///
|
||||
/// The function returns zero if `memory` is a null pointer.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// uint32_t memory_length = wasmer_memory_length(memory);
|
||||
///
|
||||
/// printf("Memory pages length: %d\n", memory_length);
|
||||
/// ```
|
||||
uint32_t wasmer_memory_length(const wasmer_memory_t *memory);
|
||||
|
||||
/// Creates a new Memory for the given descriptor and initializes the given
|
||||
/// pointer to pointer to a pointer to the new memory.
|
||||
/// Creates a new empty WebAssembly memory for the given descriptor.
|
||||
///
|
||||
/// The caller owns the object and should call `wasmer_memory_destroy` to free it.
|
||||
/// The result is stored in the first argument `memory` if successful,
|
||||
/// i.e. when the function returns
|
||||
/// `wasmer_result_t::WASMER_OK`. Otherwise,
|
||||
/// `wasmer_result_t::WASMER_ERROR` is returned, and
|
||||
/// `wasmer_last_error_length()` with `wasmer_last_error_message()`
|
||||
/// must be used to read the error message.
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
/// The caller owns the memory and is responsible to free it with
|
||||
/// `wasmer_memory_destroy()`.
|
||||
///
|
||||
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
/// and `wasmer_last_error_message` to get an error message.
|
||||
/// Example:
|
||||
///
|
||||
/// ```c
|
||||
/// // 1. The memory object.
|
||||
/// wasmer_memory_t *memory = NULL;
|
||||
///
|
||||
/// // 2. The memory descriptor.
|
||||
/// wasmer_limits_t memory_descriptor = {
|
||||
/// .min = 10,
|
||||
/// .max = {
|
||||
/// .has_some = true,
|
||||
/// .some = 15,
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// // 3. Initialize the memory.
|
||||
/// wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
|
||||
///
|
||||
/// if (result != WASMER_OK) {
|
||||
/// int error_length = wasmer_last_error_length();
|
||||
/// char *error = malloc(error_length);
|
||||
/// wasmer_last_error_message(error, error_length);
|
||||
/// // Do something with `error`…
|
||||
/// }
|
||||
///
|
||||
/// // 4. Free the memory!
|
||||
/// wasmer_memory_destroy(memory);
|
||||
/// ```
|
||||
wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits);
|
||||
|
||||
/// Deserialize the given serialized module.
|
||||
|
21
lib/runtime-core-tests/tests/exception_handling.rs
Normal file
21
lib/runtime-core-tests/tests/exception_handling.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use wasmer_runtime_core::{compile_with, imports};
|
||||
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
||||
|
||||
#[test]
|
||||
fn exception_handling_works() {
|
||||
const MODULE: &str = r#"
|
||||
(module
|
||||
(func (export "throw_trap")
|
||||
unreachable
|
||||
))
|
||||
"#;
|
||||
|
||||
let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
|
||||
|
||||
let imports = imports! {};
|
||||
for _ in 0..2 {
|
||||
let instance = module.instantiate(&imports).unwrap();
|
||||
assert!(instance.call("throw_trap", &[]).is_err());
|
||||
}
|
||||
}
|
@ -16,12 +16,7 @@ macro_rules! call_and_assert {
|
||||
expected_value,
|
||||
concat!("Expected right when calling `", stringify!($function), "`.")
|
||||
),
|
||||
(
|
||||
Err(RuntimeError::Error { data }),
|
||||
Err(RuntimeError::Error {
|
||||
data: expected_data,
|
||||
}),
|
||||
) => {
|
||||
(Err(RuntimeError(data)), Err(RuntimeError(expected_data))) => {
|
||||
if let (Some(data), Some(expected_data)) = (
|
||||
data.downcast_ref::<&str>(),
|
||||
expected_data.downcast_ref::<&str>(),
|
||||
@ -260,35 +255,25 @@ test!(
|
||||
test!(
|
||||
test_fn_trap,
|
||||
function_fn_trap,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("foo {}", 2))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("foo {}", 2))))
|
||||
);
|
||||
test!(
|
||||
test_closure_trap,
|
||||
function_closure_trap,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("bar {}", 2))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("bar {}", 2))))
|
||||
);
|
||||
test!(
|
||||
test_fn_trap_with_vmctx,
|
||||
function_fn_trap_with_vmctx,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("baz {}", 2 + SHIFT))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
test_closure_trap_with_vmctx,
|
||||
function_closure_trap_with_vmctx,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("qux {}", 2 + SHIFT))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
test_closure_trap_with_vmctx_and_env,
|
||||
function_closure_trap_with_vmctx_and_env,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("! {}", 2 + shift + SHIFT))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
|
||||
);
|
||||
|
@ -32,6 +32,11 @@ fn main() {
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
|
||||
match (target_os.as_str(), target_arch.as_str()) {
|
||||
("freebsd", "x86_64") => {
|
||||
cc::Build::new()
|
||||
.file("image-loading-freebsd-x86-64.s")
|
||||
.compile("image-loading");
|
||||
}
|
||||
("linux", "x86_64") => {
|
||||
cc::Build::new()
|
||||
.file("image-loading-linux-x86-64.s")
|
||||
|
127
lib/runtime-core/image-loading-freebsd-x86-64.s
Normal file
127
lib/runtime-core/image-loading-freebsd-x86-64.s
Normal file
@ -0,0 +1,127 @@
|
||||
# NOTE: Keep this consistent with `fault.rs`.
|
||||
|
||||
.globl run_on_alternative_stack
|
||||
run_on_alternative_stack:
|
||||
# (stack_end, stack_begin)
|
||||
# We need to ensure 16-byte alignment here.
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %rbx
|
||||
pushq %rbp
|
||||
movq %rsp, -16(%rdi)
|
||||
|
||||
leaq run_on_alternative_stack.returning(%rip), %rax
|
||||
movq %rax, -24(%rdi)
|
||||
|
||||
movq %rsi, %rsp
|
||||
|
||||
movq (%rsp), %xmm0
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm1
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm2
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm3
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm4
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm5
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm6
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm7
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm8
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm9
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm10
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm11
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm12
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm13
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm14
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm15
|
||||
add $8, %rsp
|
||||
|
||||
popq %rbp
|
||||
popq %rax
|
||||
popq %rbx
|
||||
popq %rcx
|
||||
popq %rdx
|
||||
popq %rdi
|
||||
popq %rsi
|
||||
popq %r8
|
||||
popq %r9
|
||||
popq %r10
|
||||
popq %r11
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
retq
|
||||
|
||||
run_on_alternative_stack.returning:
|
||||
movq (%rsp), %rsp
|
||||
popq %rbp
|
||||
popq %rbx
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
retq
|
||||
|
||||
# For switching into a backend without information about where registers are preserved.
|
||||
.globl register_preservation_trampoline
|
||||
register_preservation_trampoline:
|
||||
subq $8, %rsp
|
||||
pushq %rax
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
pushq %rdi
|
||||
pushq %rsi
|
||||
pushq %r8
|
||||
pushq %r9
|
||||
pushq %r10
|
||||
|
||||
callq get_boundary_register_preservation@PLT
|
||||
|
||||
# Keep this consistent with BoundaryRegisterPreservation
|
||||
movq %r15, 0(%rax)
|
||||
movq %r14, 8(%rax)
|
||||
movq %r13, 16(%rax)
|
||||
movq %r12, 24(%rax)
|
||||
movq %rbx, 32(%rax)
|
||||
|
||||
popq %r10
|
||||
popq %r9
|
||||
popq %r8
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
popq %rax
|
||||
addq $8, %rsp
|
||||
|
||||
jmpq *%rax
|
@ -13,6 +13,7 @@ use crate::{
|
||||
module::ModuleInfo,
|
||||
sys::Memory,
|
||||
};
|
||||
use std::fmt;
|
||||
use std::{any::Any, ptr::NonNull};
|
||||
|
||||
use std::collections::HashMap;
|
||||
@ -109,9 +110,28 @@ impl BackendCompilerConfig {
|
||||
pub struct CompilerConfig {
|
||||
/// Symbol information generated from emscripten; used for more detailed debug messages
|
||||
pub symbol_map: Option<HashMap<u32, String>>,
|
||||
|
||||
/// How to make the decision whether to emit bounds checks for memory accesses.
|
||||
pub memory_bound_check_mode: MemoryBoundCheckMode,
|
||||
|
||||
/// Whether to generate explicit native stack checks against `stack_lower_bound` in `InternalCtx`.
|
||||
///
|
||||
/// Usually it's adequate to use hardware memory protection mechanisms such as `mprotect` on Unix to
|
||||
/// prevent stack overflow. But for low-level environments, e.g. the kernel, faults are generally
|
||||
/// not expected and relying on hardware memory protection would add too much complexity.
|
||||
pub enforce_stack_check: bool,
|
||||
|
||||
/// Whether to enable state tracking. Necessary for managed mode.
|
||||
pub track_state: bool,
|
||||
|
||||
/// Whether to enable full preemption checkpoint generation.
|
||||
///
|
||||
/// This inserts checkpoints at critical locations such as loop backedges and function calls,
|
||||
/// allowing preemptive unwinding/task switching.
|
||||
///
|
||||
/// When enabled there can be a small amount of runtime performance overhead.
|
||||
pub full_preemption: bool,
|
||||
|
||||
pub features: Features,
|
||||
|
||||
// Target info. Presently only supported by LLVM.
|
||||
@ -150,13 +170,36 @@ impl ExceptionTable {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum ExceptionCode {
|
||||
/// An `unreachable` opcode was executed.
|
||||
Unreachable,
|
||||
|
||||
Unreachable = 0,
|
||||
/// Call indirect incorrect signature trap.
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
/// Memory out of bounds trap.
|
||||
MemoryOutOfBounds = 2,
|
||||
/// Call indirect out of bounds trap.
|
||||
CallIndirectOOB = 3,
|
||||
/// An arithmetic exception, e.g. divided by zero.
|
||||
Arithmetic,
|
||||
IllegalArithmetic = 4,
|
||||
/// Misaligned atomic access trap.
|
||||
MisalignedAtomicAccess = 5,
|
||||
}
|
||||
|
||||
/// Memory access exception, e.g. misaligned/out-of-bound read/write.
|
||||
Memory,
|
||||
impl fmt::Display for ExceptionCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
ExceptionCode::Unreachable => "unreachable",
|
||||
ExceptionCode::IncorrectCallIndirectSignature => {
|
||||
"incorrect `call_indirect` signature"
|
||||
}
|
||||
ExceptionCode::MemoryOutOfBounds => "memory out-of-bounds access",
|
||||
ExceptionCode::CallIndirectOOB => "`call_indirect` out-of-bounds",
|
||||
ExceptionCode::IllegalArithmetic => "illegal arithmetic operation",
|
||||
ExceptionCode::MisalignedAtomicAccess => "misaligned atomic access",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Compiler {
|
||||
|
@ -179,18 +179,7 @@ impl std::error::Error for LinkError {}
|
||||
/// The main way to do this is `Instance.call`.
|
||||
///
|
||||
/// Comparing two `RuntimeError`s always evaluates to false.
|
||||
pub enum RuntimeError {
|
||||
/// Trap.
|
||||
Trap {
|
||||
/// Trap message.
|
||||
msg: Box<str>,
|
||||
},
|
||||
/// Error.
|
||||
Error {
|
||||
/// Error data.
|
||||
data: Box<dyn Any + Send>,
|
||||
},
|
||||
}
|
||||
pub struct RuntimeError(pub Box<dyn Any + Send>);
|
||||
|
||||
impl PartialEq for RuntimeError {
|
||||
fn eq(&self, _other: &RuntimeError) -> bool {
|
||||
@ -200,21 +189,15 @@ impl PartialEq for RuntimeError {
|
||||
|
||||
impl std::fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
RuntimeError::Trap { ref msg } => {
|
||||
write!(f, "WebAssembly trap occurred during runtime: {}", msg)
|
||||
}
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(s) = data.downcast_ref::<&str>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(exc_code) = data.downcast_ref::<ExceptionCode>() {
|
||||
write!(f, "Caught exception of type \"{:?}\".", exc_code)
|
||||
} else {
|
||||
write!(f, "unknown error")
|
||||
}
|
||||
}
|
||||
let data = &*self.0;
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(s) = data.downcast_ref::<&str>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(exc_code) = data.downcast_ref::<ExceptionCode>() {
|
||||
write!(f, "Caught exception of type \"{:?}\".", exc_code)
|
||||
} else {
|
||||
write!(f, "unknown error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -500,6 +500,249 @@ impl FaultInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "freebsd", target_arch = "aarch64"))]
|
||||
/// Get fault info from siginfo and ucontext.
|
||||
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
|
||||
#[repr(C)]
|
||||
pub struct ucontext_t {
|
||||
uc_sigmask: libc::sigset_t,
|
||||
uc_mcontext: mcontext_t,
|
||||
uc_link: *mut ucontext_t,
|
||||
uc_stack: libc::stack_t,
|
||||
uc_flags: i32,
|
||||
__spare__: [i32; 4],
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct gpregs {
|
||||
gp_x: [u64; 30],
|
||||
gp_lr: u64,
|
||||
gp_sp: u64,
|
||||
gp_elr: u64,
|
||||
gp_spsr: u64,
|
||||
gp_pad: i32,
|
||||
};
|
||||
#[repr(C)]
|
||||
pub struct fpregs {
|
||||
fp_q: [u128; 32],
|
||||
fp_sr: u32,
|
||||
fp_cr: u32,
|
||||
fp_flags: i32,
|
||||
fp_pad: i32,
|
||||
};
|
||||
#[repr(C)]
|
||||
pub struct mcontext_t {
|
||||
mc_gpregs: gpregs,
|
||||
mc_fpregs: fpregs,
|
||||
mc_flags: i32,
|
||||
mc_pad: i32,
|
||||
mc_spare: [u64; 8],
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *mut ucontext_t;
|
||||
let gregs = &(*ucontext).uc_mcontext.mc_gpregs;
|
||||
|
||||
let mut known_registers: [Option<u64>; 32] = [None; 32];
|
||||
|
||||
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs.gp_x[15] as _);
|
||||
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs.gp_x[14] as _);
|
||||
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs.gp_x[13] as _);
|
||||
known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs.gp_x[12] as _);
|
||||
known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs.gp_x[11] as _);
|
||||
known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs.gp_x[10] as _);
|
||||
known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs.gp_x[9] as _);
|
||||
known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs.gp_x[8] as _);
|
||||
known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs.gp_x[6] as _);
|
||||
known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs.gp_x[7] as _);
|
||||
known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs.gp_x[2] as _);
|
||||
known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs.gp_x[1] as _);
|
||||
known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs.gp_x[3] as _);
|
||||
known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs.gp_x[0] as _);
|
||||
|
||||
known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs.gp_x[5] as _);
|
||||
known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs.gp_x[28] as _);
|
||||
|
||||
FaultInfo {
|
||||
faulting_addr: si_addr as usize as _,
|
||||
ip: std::mem::transmute::<&mut u64, &'static Cell<usize>>(
|
||||
&mut (*ucontext).uc_mcontext.mc_gpregs.gp_elr,
|
||||
),
|
||||
known_registers,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
|
||||
/// Get fault info from siginfo and ucontext.
|
||||
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
|
||||
use crate::state::x64::XMM;
|
||||
#[repr(C)]
|
||||
pub struct ucontext_t {
|
||||
uc_sigmask: libc::sigset_t,
|
||||
uc_mcontext: mcontext_t,
|
||||
uc_link: *mut ucontext_t,
|
||||
uc_stack: libc::stack_t,
|
||||
uc_flags: i32,
|
||||
__spare__: [i32; 4],
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct mcontext_t {
|
||||
mc_onstack: u64,
|
||||
mc_rdi: u64,
|
||||
mc_rsi: u64,
|
||||
mc_rdx: u64,
|
||||
mc_rcx: u64,
|
||||
mc_r8: u64,
|
||||
mc_r9: u64,
|
||||
mc_rax: u64,
|
||||
mc_rbx: u64,
|
||||
mc_rbp: u64,
|
||||
mc_r10: u64,
|
||||
mc_r11: u64,
|
||||
mc_r12: u64,
|
||||
mc_r13: u64,
|
||||
mc_r14: u64,
|
||||
mc_r15: u64,
|
||||
mc_trapno: u32,
|
||||
mc_fs: u16,
|
||||
mc_gs: u16,
|
||||
mc_addr: u64,
|
||||
mc_flags: u32,
|
||||
mc_es: u16,
|
||||
mc_ds: u16,
|
||||
mc_err: u64,
|
||||
mc_rip: u64,
|
||||
mc_cs: u64,
|
||||
mc_rflags: u64,
|
||||
mc_rsp: u64,
|
||||
mc_ss: u64,
|
||||
mc_len: i64,
|
||||
|
||||
mc_fpformat: i64,
|
||||
mc_ownedfp: i64,
|
||||
mc_savefpu: *const savefpu,
|
||||
mc_fpstate: [i64; 63], // mc_fpstate[0] is a pointer to savefpu
|
||||
|
||||
mc_fsbase: u64,
|
||||
mc_gsbase: u64,
|
||||
|
||||
mc_xfpustate: u64,
|
||||
mc_xfpustate_len: u64,
|
||||
|
||||
mc_spare: [i64; 4],
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct xmmacc {
|
||||
element: [u32; 4],
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct __envxmm64 {
|
||||
en_cw: u16,
|
||||
en_sw: u16,
|
||||
en_tw: u8,
|
||||
en_zero: u8,
|
||||
en_opcode: u16,
|
||||
en_rip: u64,
|
||||
en_rdp: u64,
|
||||
en_mxcsr: u32,
|
||||
en_mxcsr_mask: u32,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct fpacc87 {
|
||||
fp_bytes: [u8; 10],
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct sv_fp {
|
||||
fp_acc: fpacc87,
|
||||
fp_pad: [u8; 6],
|
||||
}
|
||||
#[repr(C, align(16))]
|
||||
pub struct savefpu {
|
||||
sv_env: __envxmm64,
|
||||
sv_fp_t: [sv_fp; 8],
|
||||
sv_xmm: [xmmacc; 16],
|
||||
sv_pad: [u8; 96],
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *mut ucontext_t;
|
||||
let gregs = &mut (*ucontext).uc_mcontext;
|
||||
|
||||
fn read_xmm(reg: &xmmacc) -> u64 {
|
||||
(reg.element[0] as u64) | ((reg.element[1] as u64) << 32)
|
||||
}
|
||||
|
||||
let mut known_registers: [Option<u64>; 32] = [None; 32];
|
||||
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs.mc_r15);
|
||||
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs.mc_r14);
|
||||
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs.mc_r13);
|
||||
known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs.mc_r12);
|
||||
known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs.mc_r11);
|
||||
known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs.mc_r10);
|
||||
known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs.mc_r9);
|
||||
known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs.mc_r8);
|
||||
known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs.mc_rsi);
|
||||
known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs.mc_rdi);
|
||||
known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs.mc_rdx);
|
||||
known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs.mc_rcx);
|
||||
known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs.mc_rbx);
|
||||
known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs.mc_rax);
|
||||
|
||||
known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs.mc_rbp);
|
||||
known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs.mc_rsp);
|
||||
|
||||
// https://lists.freebsd.org/pipermail/freebsd-arch/2011-December/012077.html
|
||||
// https://people.freebsd.org/~kib/misc/defer_sig.c
|
||||
const _MC_HASFPXSTATE: u32 = 0x4;
|
||||
if (gregs.mc_flags & _MC_HASFPXSTATE) == 0 {
|
||||
// XXX mc_fpstate[0] is actually a pointer to a struct savefpu
|
||||
let fpregs = &*(*ucontext).uc_mcontext.mc_savefpu;
|
||||
known_registers[X64Register::XMM(XMM::XMM0).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[0]));
|
||||
known_registers[X64Register::XMM(XMM::XMM1).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[1]));
|
||||
known_registers[X64Register::XMM(XMM::XMM2).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[2]));
|
||||
known_registers[X64Register::XMM(XMM::XMM3).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[3]));
|
||||
known_registers[X64Register::XMM(XMM::XMM4).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[4]));
|
||||
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[5]));
|
||||
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[6]));
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[7]));
|
||||
known_registers[X64Register::XMM(XMM::XMM8).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[8]));
|
||||
known_registers[X64Register::XMM(XMM::XMM9).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[9]));
|
||||
known_registers[X64Register::XMM(XMM::XMM10).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[10]));
|
||||
known_registers[X64Register::XMM(XMM::XMM11).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[11]));
|
||||
known_registers[X64Register::XMM(XMM::XMM12).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[12]));
|
||||
known_registers[X64Register::XMM(XMM::XMM13).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[13]));
|
||||
known_registers[X64Register::XMM(XMM::XMM14).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[14]));
|
||||
known_registers[X64Register::XMM(XMM::XMM15).to_index().0] =
|
||||
Some(read_xmm(&fpregs.sv_xmm[15]));
|
||||
}
|
||||
|
||||
FaultInfo {
|
||||
faulting_addr: si_addr,
|
||||
ip: std::mem::transmute::<&mut u64, &'static Cell<usize>>(
|
||||
&mut (*ucontext).uc_mcontext.mc_rip,
|
||||
),
|
||||
known_registers,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
||||
/// Get fault info from siginfo and ucontext.
|
||||
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! The global module contains the implementation data structures and helper functions used to
|
||||
//! manipulate and access a wasm globals.
|
||||
//! The global module contains data structures and helper functions used to
|
||||
//! manipulate and access Wasm globals.
|
||||
use crate::{
|
||||
export::Export,
|
||||
import::IsExport,
|
||||
@ -11,7 +11,7 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
/// Container with a descriptor and a reference to a global value.
|
||||
/// A handle to a Wasm Global
|
||||
pub struct Global {
|
||||
desc: GlobalDescriptor,
|
||||
storage: Arc<Mutex<vm::LocalGlobal>>,
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
sig_registry::SigRegistry,
|
||||
structures::TypedIndex,
|
||||
table::Table,
|
||||
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
||||
typed_func::{Func, Wasm, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
||||
vm::{self, InternalField},
|
||||
};
|
||||
@ -674,8 +674,7 @@ pub(crate) fn call_func_with_index_inner(
|
||||
} = wasm;
|
||||
|
||||
let run_wasm = |result_space: *mut u64| unsafe {
|
||||
let mut trap_info = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
let mut error_out = None;
|
||||
|
||||
let success = invoke(
|
||||
trampoline,
|
||||
@ -683,21 +682,16 @@ pub(crate) fn call_func_with_index_inner(
|
||||
func_ptr,
|
||||
raw_args.as_ptr(),
|
||||
result_space,
|
||||
&mut trap_info,
|
||||
&mut user_error,
|
||||
&mut error_out,
|
||||
invoke_env,
|
||||
);
|
||||
|
||||
if success {
|
||||
Ok(())
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap {
|
||||
msg: trap_info.to_string().into(),
|
||||
})
|
||||
}
|
||||
Err(error_out
|
||||
.map(RuntimeError)
|
||||
.unwrap_or_else(|| RuntimeError(Box::new("invoke(): Unknown error".to_string()))))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
//! Wasmer Runtime Core Library
|
||||
//!
|
||||
//! The runtime core library provides common data structures which are shared by compiler backends
|
||||
//! to implement a Web Assembly runtime.
|
||||
//! This crate provides common data structures which are shared by compiler backends
|
||||
//! to implement a WebAssembly runtime.
|
||||
//!
|
||||
//! The runtime core also provides an API for users who use wasmer as an embedded wasm runtime which
|
||||
//! This crate also provides an API for users who use wasmer as an embedded wasm runtime which
|
||||
//! allows operations like compiling, instantiating, providing imports, access exports, memories,
|
||||
//! and tables for example.
|
||||
//!
|
||||
//! The runtime core library is recommended to be used by only power users who wish to customize the
|
||||
//! wasmer runtime. Most wasmer users should prefer the API which is re-exported by the wasmer
|
||||
//! runtime library which provides common defaults and a friendly API.
|
||||
//! Most wasmer users should prefer the API which is re-exported by the `wasmer-runtime`
|
||||
//! library by default. This crate provides additional APIs which may be useful to users
|
||||
//! that wish to customize the wasmer runtime.
|
||||
//!
|
||||
|
||||
#![deny(
|
||||
|
@ -88,7 +88,7 @@ impl Instance for LocalInstance {
|
||||
}
|
||||
}
|
||||
let offset = self.offsets[id];
|
||||
let addr: *const u8 = unsafe { self.code.as_ptr().offset(offset as isize) };
|
||||
let addr: *const u8 = unsafe { self.code.as_ptr().add(offset) };
|
||||
use std::mem::transmute;
|
||||
Ok(unsafe {
|
||||
match args_u64.len() {
|
||||
|
@ -109,7 +109,7 @@ impl Memory {
|
||||
}
|
||||
|
||||
/// Return a "view" of the currently accessible memory. By
|
||||
/// default, the view is unsyncronized, using regular memory
|
||||
/// default, the view is unsynchronized, using regular memory
|
||||
/// accesses. You can force a memory view to use atomic accesses
|
||||
/// by calling the [`atomically`] method.
|
||||
///
|
||||
|
@ -82,7 +82,9 @@ impl<'a, T: Atomic> MemoryView<'a, T> {
|
||||
impl<'a, T> Deref for MemoryView<'a, T, NonAtomically> {
|
||||
type Target = [Cell<T>];
|
||||
fn deref(&self) -> &[Cell<T>] {
|
||||
unsafe { slice::from_raw_parts(self.ptr as *const Cell<T>, self.length) }
|
||||
let mut_slice: &mut [T] = unsafe { slice::from_raw_parts_mut(self.ptr, self.length) };
|
||||
let cell_slice: &Cell<[T]> = Cell::from_mut(mut_slice);
|
||||
cell_slice.as_slice_of_cells()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,52 +11,12 @@ use std::{
|
||||
any::Any,
|
||||
convert::Infallible,
|
||||
ffi::c_void,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem, panic,
|
||||
ptr::{self, NonNull},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Wasm trap info.
|
||||
#[repr(C)]
|
||||
pub enum WasmTrapInfo {
|
||||
/// Unreachable trap.
|
||||
Unreachable = 0,
|
||||
/// Call indirect incorrect signature trap.
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
/// Memory out of bounds trap.
|
||||
MemoryOutOfBounds = 2,
|
||||
/// Call indirect out of bounds trap.
|
||||
CallIndirectOOB = 3,
|
||||
/// Illegal arithmetic trap.
|
||||
IllegalArithmetic = 4,
|
||||
/// Misaligned atomic access trap.
|
||||
MisalignedAtomicAccess = 5,
|
||||
/// Unknown trap.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl fmt::Display for WasmTrapInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
WasmTrapInfo::Unreachable => "unreachable",
|
||||
WasmTrapInfo::IncorrectCallIndirectSignature => {
|
||||
"incorrect `call_indirect` signature"
|
||||
}
|
||||
WasmTrapInfo::MemoryOutOfBounds => "memory out-of-bounds access",
|
||||
WasmTrapInfo::CallIndirectOOB => "`call_indirect` out-of-bounds",
|
||||
WasmTrapInfo::IllegalArithmetic => "illegal arithmetic operation",
|
||||
WasmTrapInfo::MisalignedAtomicAccess => "misaligned atomic access",
|
||||
WasmTrapInfo::Unknown => "unknown",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is just an empty trait to constrict that types that
|
||||
/// can be put into the third/fourth (depending if you include lifetimes)
|
||||
/// of the `Func` struct.
|
||||
@ -77,8 +37,7 @@ pub type Invoke = unsafe extern "C" fn(
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
extra: Option<NonNull<c_void>>,
|
||||
) -> bool;
|
||||
|
||||
@ -92,6 +51,8 @@ pub struct Wasm {
|
||||
pub(crate) invoke_env: Option<NonNull<c_void>>,
|
||||
}
|
||||
|
||||
impl Kind for Wasm {}
|
||||
|
||||
impl Wasm {
|
||||
/// Create new `Wasm` from given parts.
|
||||
pub unsafe fn from_raw_parts(
|
||||
@ -111,7 +72,6 @@ impl Wasm {
|
||||
/// by the host.
|
||||
pub struct Host(());
|
||||
|
||||
impl Kind for Wasm {}
|
||||
impl Kind for Host {}
|
||||
|
||||
/// Represents a list of WebAssembly values.
|
||||
@ -140,7 +100,7 @@ pub trait WasmTypeList {
|
||||
|
||||
/// This method is used to distribute the values onto a function,
|
||||
/// e.g. `(1, 2).call(func, …)`. This form is unlikely to be used
|
||||
/// directly in the code, see the `Func:call` implementation.
|
||||
/// directly in the code, see the `Func::call` implementation.
|
||||
unsafe fn call<Rets>(
|
||||
self,
|
||||
f: NonNull<vm::Func>,
|
||||
@ -151,14 +111,15 @@ pub trait WasmTypeList {
|
||||
Rets: WasmTypeList;
|
||||
}
|
||||
|
||||
/// Empty trait to specify the kind of `ExternalFunction`: With or
|
||||
/// Empty trait to specify the kind of `HostFunction`: With or
|
||||
/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the
|
||||
/// `ImplicitVmCtx` structures.
|
||||
///
|
||||
/// This type is never aimed to be used by a user. It is used by the
|
||||
/// This trait is never aimed to be used by a user. It is used by the
|
||||
/// trait system to automatically generate an appropriate `wrap`
|
||||
/// function.
|
||||
pub trait ExternalFunctionKind {}
|
||||
#[doc(hidden)]
|
||||
pub trait HostFunctionKind {}
|
||||
|
||||
/// This empty structure indicates that an external function must
|
||||
/// contain an explicit `vm::Ctx` argument (at first position).
|
||||
@ -168,8 +129,11 @@ pub trait ExternalFunctionKind {}
|
||||
/// x + 1
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
pub struct ExplicitVmCtx {}
|
||||
|
||||
impl HostFunctionKind for ExplicitVmCtx {}
|
||||
|
||||
/// This empty structure indicates that an external function has no
|
||||
/// `vm::Ctx` argument (at first position). Its signature is:
|
||||
///
|
||||
@ -180,18 +144,17 @@ pub struct ExplicitVmCtx {}
|
||||
/// ```
|
||||
pub struct ImplicitVmCtx {}
|
||||
|
||||
impl ExternalFunctionKind for ExplicitVmCtx {}
|
||||
impl ExternalFunctionKind for ImplicitVmCtx {}
|
||||
impl HostFunctionKind for ImplicitVmCtx {}
|
||||
|
||||
/// Represents a function that can be converted to a `vm::Func`
|
||||
/// (function pointer) that can be called within WebAssembly.
|
||||
pub trait ExternalFunction<Kind, Args, Rets>
|
||||
pub trait HostFunction<Kind, Args, Rets>
|
||||
where
|
||||
Kind: ExternalFunctionKind,
|
||||
Kind: HostFunctionKind,
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Conver to function pointer.
|
||||
/// Convert to function pointer.
|
||||
fn to_raw(self) -> (NonNull<vm::Func>, Option<NonNull<vm::FuncEnv>>);
|
||||
}
|
||||
|
||||
@ -268,8 +231,8 @@ where
|
||||
/// Creates a new `Func`.
|
||||
pub fn new<F, Kind>(func: F) -> Func<'a, Args, Rets, Host>
|
||||
where
|
||||
Kind: ExternalFunctionKind,
|
||||
F: ExternalFunction<Kind, Args, Rets>,
|
||||
Kind: HostFunctionKind,
|
||||
F: HostFunction<Kind, Args, Rets>,
|
||||
{
|
||||
let (func, func_env) = func.to_raw();
|
||||
|
||||
@ -351,6 +314,7 @@ macro_rules! impl_traits {
|
||||
where
|
||||
$( $x: WasmExternType ),*;
|
||||
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*
|
||||
@ -401,8 +365,7 @@ macro_rules! impl_traits {
|
||||
let ( $( $x ),* ) = self;
|
||||
let args = [ $( $x.to_native().to_binary()),* ];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
let mut error_out = None;
|
||||
|
||||
if (wasm.invoke)(
|
||||
wasm.trampoline,
|
||||
@ -410,22 +373,20 @@ macro_rules! impl_traits {
|
||||
f,
|
||||
args.as_ptr(),
|
||||
rets.as_mut().as_mut_ptr(),
|
||||
&mut trap,
|
||||
&mut user_error,
|
||||
&mut error_out,
|
||||
wasm.invoke_env
|
||||
) {
|
||||
Ok(Rets::from_ret_array(rets))
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap { msg: trap.to_string().into() })
|
||||
}
|
||||
Err(error_out.map(RuntimeError).unwrap_or_else(|| {
|
||||
RuntimeError(Box::new("invoke(): Unknown error".to_string()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x, )* Rets, Trap, FN > HostFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
@ -540,7 +501,8 @@ macro_rules! impl_traits {
|
||||
}
|
||||
}
|
||||
|
||||
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x, )* Rets, Trap, FN > HostFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
@ -652,13 +614,14 @@ macro_rules! impl_traits {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Call the typed func and return results.
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(non_snake_case, clippy::too_many_arguments)]
|
||||
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||
#[allow(unused_parens)]
|
||||
unsafe {
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! convert to other represenations.
|
||||
|
||||
use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages};
|
||||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, convert::TryFrom};
|
||||
|
||||
/// Represents a WebAssembly type.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -67,35 +67,32 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Value {
|
||||
fn from(i: i32) -> Self {
|
||||
Value::I32(i)
|
||||
}
|
||||
macro_rules! value_conversions {
|
||||
($native_type:ty, $value_variant:ident) => {
|
||||
impl From<$native_type> for Value {
|
||||
fn from(n: $native_type) -> Self {
|
||||
Self::$value_variant(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Value> for $native_type {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: &Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::$value_variant(value) => Ok(*value),
|
||||
_ => Err("Invalid cast."),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(i: i64) -> Self {
|
||||
Value::I64(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Value {
|
||||
fn from(f: f32) -> Self {
|
||||
Value::F32(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(f: f64) -> Self {
|
||||
Value::F64(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Value {
|
||||
fn from(v: u128) -> Self {
|
||||
Value::V128(v)
|
||||
}
|
||||
}
|
||||
value_conversions!(i32, I32);
|
||||
value_conversions!(i64, I64);
|
||||
value_conversions!(f32, F32);
|
||||
value_conversions!(f64, F64);
|
||||
value_conversions!(u128, V128);
|
||||
|
||||
/// Represents a native wasm type.
|
||||
pub unsafe trait NativeWasmType: Copy + Into<Value>
|
||||
@ -104,44 +101,57 @@ where
|
||||
{
|
||||
/// Type for this `NativeWasmType`.
|
||||
const TYPE: Type;
|
||||
|
||||
/// Convert from u64 bites to self.
|
||||
fn from_binary(bits: u64) -> Self;
|
||||
|
||||
/// Convert self to u64 binary representation.
|
||||
fn to_binary(self) -> u64;
|
||||
}
|
||||
|
||||
unsafe impl NativeWasmType for i32 {
|
||||
const TYPE: Type = Type::I32;
|
||||
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
bits as _
|
||||
}
|
||||
|
||||
fn to_binary(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NativeWasmType for i64 {
|
||||
const TYPE: Type = Type::I64;
|
||||
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
bits as _
|
||||
}
|
||||
|
||||
fn to_binary(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NativeWasmType for f32 {
|
||||
const TYPE: Type = Type::F32;
|
||||
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
f32::from_bits(bits as u32)
|
||||
}
|
||||
|
||||
fn to_binary(self) -> u64 {
|
||||
self.to_bits() as _
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NativeWasmType for f64 {
|
||||
const TYPE: Type = Type::F64;
|
||||
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
f64::from_bits(bits)
|
||||
}
|
||||
|
||||
fn to_binary(self) -> u64 {
|
||||
self.to_bits()
|
||||
}
|
||||
@ -154,103 +164,41 @@ where
|
||||
{
|
||||
/// Native wasm type for this `WasmExternType`.
|
||||
type Native: NativeWasmType;
|
||||
|
||||
/// Convert from given `Native` type to self.
|
||||
fn from_native(native: Self::Native) -> Self;
|
||||
|
||||
/// Convert self to `Native` type.
|
||||
fn to_native(self) -> Self::Native;
|
||||
}
|
||||
|
||||
unsafe impl WasmExternType for i8 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u8 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for i16 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u16 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for i32 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u32 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for i64 {
|
||||
type Native = i64;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u64 {
|
||||
type Native = i64;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for f32 {
|
||||
type Native = f32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for f64 {
|
||||
type Native = f64;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
macro_rules! wasm_extern_type {
|
||||
($type:ty => $native_type:ty) => {
|
||||
unsafe impl WasmExternType for $type {
|
||||
type Native = $native_type;
|
||||
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wasm_extern_type!(i8 => i32);
|
||||
wasm_extern_type!(u8 => i32);
|
||||
wasm_extern_type!(i16 => i32);
|
||||
wasm_extern_type!(u16 => i32);
|
||||
wasm_extern_type!(i32 => i32);
|
||||
wasm_extern_type!(u32 => i32);
|
||||
wasm_extern_type!(i64 => i64);
|
||||
wasm_extern_type!(u64 => i64);
|
||||
wasm_extern_type!(f32 => f32);
|
||||
wasm_extern_type!(f64 => f64);
|
||||
|
||||
// pub trait IntegerAtomic
|
||||
// where
|
||||
// Self: Sized
|
||||
|
@ -70,8 +70,8 @@ fn main() -> Result<(), error::Error> {
|
||||
|
||||
println!("result: {:?}", result);
|
||||
|
||||
if let Err(RuntimeError::Error { data }) = result {
|
||||
if let Ok(exit_code) = data.downcast::<ExitCode>() {
|
||||
if let Err(e) = result {
|
||||
if let Ok(exit_code) = e.0.downcast::<ExitCode>() {
|
||||
println!("exit code: {:?}", exit_code);
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@
|
||||
//!
|
||||
//! # Additional Notes:
|
||||
//!
|
||||
//! The `wasmer-runtime` is build to support compiler multiple backends.
|
||||
//! `wasmer-runtime` is built to support multiple compiler backends.
|
||||
//! Currently, we support the Singlepass, [Cranelift], and LLVM compilers
|
||||
//! with the [`wasmer-singlepass-backend`], [`wasmer-clif-backend`], and
|
||||
//! wasmer-llvm-backend crates, respectively.
|
||||
@ -145,7 +145,7 @@ pub mod units {
|
||||
}
|
||||
|
||||
pub mod types {
|
||||
//! Various types.
|
||||
//! Types used in the Wasm runtime and conversion functions.
|
||||
pub use wasmer_runtime_core::types::*;
|
||||
}
|
||||
|
||||
|
@ -40,10 +40,10 @@ fn error_propagation() {
|
||||
|
||||
let result = foo.call();
|
||||
|
||||
if let Err(RuntimeError::Error { data }) = result {
|
||||
let exit_code = data.downcast::<ExitCode>().unwrap();
|
||||
if let Err(RuntimeError(e)) = result {
|
||||
let exit_code = e.downcast::<ExitCode>().unwrap();
|
||||
assert_eq!(exit_code.code, 42);
|
||||
} else {
|
||||
panic!("didn't return RuntimeError::Error")
|
||||
panic!("didn't return RuntimeError")
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
use crate::emitter_x64::*;
|
||||
use crate::machine::*;
|
||||
use crate::protect_unix;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use dynasmrt::aarch64::Assembler;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@ -28,7 +27,7 @@ use wasmer_runtime_core::{
|
||||
},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
codegen::*,
|
||||
fault::raw::register_preservation_trampoline,
|
||||
fault::{self, raw::register_preservation_trampoline},
|
||||
loader::CodeMemory,
|
||||
memory::MemoryType,
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
@ -37,7 +36,7 @@ use wasmer_runtime_core::{
|
||||
ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm},
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
@ -369,14 +368,18 @@ impl RunnableModule for X64ExecutionContext {
|
||||
}
|
||||
|
||||
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
|
||||
// Correctly unwinding from `catch_unsafe_unwind` on hardware exceptions depends
|
||||
// on the signal handlers being installed. Here we call `ensure_sighandler` "statically"
|
||||
// outside `invoke()`.
|
||||
fault::ensure_sighandler();
|
||||
|
||||
unsafe extern "C" fn invoke(
|
||||
_trampoline: Trampoline,
|
||||
ctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
num_params_plus_one: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module;
|
||||
@ -384,8 +387,9 @@ impl RunnableModule for X64ExecutionContext {
|
||||
let args =
|
||||
slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1);
|
||||
|
||||
let ret = match protect_unix::call_protected(
|
||||
let ret = match fault::catch_unsafe_unwind(
|
||||
|| {
|
||||
// Puts the arguments onto the stack and calls Wasm entry.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect();
|
||||
@ -396,6 +400,9 @@ impl RunnableModule for X64ExecutionContext {
|
||||
func.as_ptr(),
|
||||
)
|
||||
}
|
||||
|
||||
// FIXME: Currently we are doing a hack here to convert between native aarch64 and
|
||||
// "emulated" x86 ABIs. Ideally, this should be done using handwritten assembly.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
struct CallCtx<'a> {
|
||||
@ -520,10 +527,7 @@ impl RunnableModule for X64ExecutionContext {
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
match err {
|
||||
protect_unix::CallProtError::Trap(info) => *trap_info = info,
|
||||
protect_unix::CallProtError::Error(data) => *user_error = Some(data),
|
||||
}
|
||||
*error_out = Some(err);
|
||||
false
|
||||
}
|
||||
};
|
||||
@ -549,8 +553,7 @@ impl RunnableModule for X64ExecutionContext {
|
||||
}
|
||||
|
||||
unsafe fn do_early_trap(&self, data: Box<dyn Any + Send>) -> ! {
|
||||
protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
|
||||
protect_unix::trigger_trap();
|
||||
fault::begin_unsafe_unwind(data);
|
||||
}
|
||||
|
||||
fn get_code(&self) -> Option<&[u8]> {
|
||||
@ -639,6 +642,7 @@ struct CodegenConfig {
|
||||
memory_bound_check_mode: MemoryBoundCheckMode,
|
||||
enforce_stack_check: bool,
|
||||
track_state: bool,
|
||||
full_preemption: bool,
|
||||
}
|
||||
|
||||
impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
||||
@ -921,6 +925,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
||||
memory_bound_check_mode: config.memory_bound_check_mode,
|
||||
enforce_stack_check: config.enforce_stack_check,
|
||||
track_state: config.track_state,
|
||||
full_preemption: config.full_preemption,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
@ -1021,14 +1026,14 @@ impl X64FunctionCode {
|
||||
Self::mark_trappable(a, m, fsm, control_stack);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
op(a, sz, Location::GPR(GPR::RCX));
|
||||
}
|
||||
_ => {
|
||||
Self::mark_trappable(a, m, fsm, control_stack);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
op(a, sz, loc);
|
||||
}
|
||||
}
|
||||
@ -1701,14 +1706,11 @@ impl X64FunctionCode {
|
||||
Location::GPR(GPR::RSP),
|
||||
);
|
||||
|
||||
// FIXME: Possible dynasm bug. This is a workaround.
|
||||
// Using RSP as the source/destination operand of a `mov` instruction produces invalid code.
|
||||
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX));
|
||||
for (i, r) in used_xmms.iter().enumerate() {
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::XMM(*r),
|
||||
Location::Memory(GPR::RCX, (i * 8) as i32),
|
||||
Location::Memory(GPR::RSP, (i * 8) as i32),
|
||||
);
|
||||
}
|
||||
for r in used_xmms.iter().rev() {
|
||||
@ -1786,37 +1788,26 @@ impl X64FunctionCode {
|
||||
}
|
||||
}
|
||||
match *param {
|
||||
// Dynasm bug: RSP in memory operand does not work
|
||||
Location::Imm64(_) | Location::XMM(_) => {
|
||||
Location::Imm64(_) => {
|
||||
// Dummy value slot to be filled with `mov`.
|
||||
a.emit_push(Size::S64, Location::GPR(GPR::RAX));
|
||||
|
||||
// Use R10 as the temporary register here, since it is callee-saved and not
|
||||
// used by the callback `cb`.
|
||||
a.emit_mov(Size::S64, *param, Location::GPR(GPR::R10));
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::GPR(GPR::RAX),
|
||||
Location::XMM(XMM::XMM0),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::GPR(GPR::RCX),
|
||||
Location::XMM(XMM::XMM1),
|
||||
);
|
||||
a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP));
|
||||
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX));
|
||||
a.emit_mov(Size::S64, *param, Location::GPR(GPR::RAX));
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::GPR(GPR::RAX),
|
||||
Location::Memory(GPR::RCX, 0),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::XMM(XMM::XMM0),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::XMM(XMM::XMM1),
|
||||
Location::GPR(GPR::RCX),
|
||||
Location::GPR(GPR::R10),
|
||||
Location::Memory(GPR::RSP, 0),
|
||||
);
|
||||
}
|
||||
Location::XMM(_) => {
|
||||
// Dummy value slot to be filled with `mov`.
|
||||
a.emit_push(Size::S64, Location::GPR(GPR::RAX));
|
||||
|
||||
// XMM registers can be directly stored to memory.
|
||||
a.emit_mov(Size::S64, *param, Location::Memory(GPR::RSP, 0));
|
||||
}
|
||||
_ => a.emit_push(Size::S64, *param),
|
||||
}
|
||||
}
|
||||
@ -1888,12 +1879,10 @@ impl X64FunctionCode {
|
||||
|
||||
// Restore XMMs.
|
||||
if used_xmms.len() > 0 {
|
||||
// FIXME: Possible dynasm bug. This is a workaround.
|
||||
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RDX));
|
||||
for (i, r) in used_xmms.iter().enumerate() {
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(GPR::RDX, (i * 8) as i32),
|
||||
Location::Memory(GPR::RSP, (i * 8) as i32),
|
||||
Location::XMM(*r),
|
||||
);
|
||||
}
|
||||
@ -2014,9 +2003,12 @@ impl X64FunctionCode {
|
||||
a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr));
|
||||
a.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr));
|
||||
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::Memory, |a| {
|
||||
a.emit_conditional_trap(Condition::Above)
|
||||
});
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
etable,
|
||||
ExceptionCode::MemoryOutOfBounds,
|
||||
|a| a.emit_conditional_trap(Condition::Above),
|
||||
);
|
||||
|
||||
m.release_temp_gpr(tmp_bound);
|
||||
}
|
||||
@ -2056,13 +2048,16 @@ impl X64FunctionCode {
|
||||
Location::Imm32(align - 1),
|
||||
Location::GPR(tmp_aligncheck),
|
||||
);
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::Memory, |a| {
|
||||
a.emit_conditional_trap(Condition::NotEqual)
|
||||
});
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
etable,
|
||||
ExceptionCode::MemoryOutOfBounds,
|
||||
|a| a.emit_conditional_trap(Condition::NotEqual),
|
||||
);
|
||||
m.release_temp_gpr(tmp_aligncheck);
|
||||
}
|
||||
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::Memory, |a| {
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::MemoryOutOfBounds, |a| {
|
||||
cb(a, m, tmp_addr)
|
||||
})?;
|
||||
|
||||
@ -2116,6 +2111,10 @@ impl X64FunctionCode {
|
||||
true,
|
||||
value_size,
|
||||
|a, m, addr| {
|
||||
// Memory moves with size < 32b do not zero upper bits.
|
||||
if memory_sz < Size::S32 {
|
||||
a.emit_xor(Size::S32, Location::GPR(compare), Location::GPR(compare));
|
||||
}
|
||||
a.emit_mov(memory_sz, Location::Memory(addr, 0), Location::GPR(compare));
|
||||
a.emit_mov(stack_sz, Location::GPR(compare), ret);
|
||||
cb(a, m, compare, value);
|
||||
@ -2193,7 +2192,7 @@ impl X64FunctionCode {
|
||||
a.emit_label(trap);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
a.emit_ud2();
|
||||
a.emit_label(end);
|
||||
}
|
||||
@ -2321,7 +2320,7 @@ impl X64FunctionCode {
|
||||
a.emit_label(trap);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
a.emit_ud2();
|
||||
a.emit_label(end);
|
||||
}
|
||||
@ -2449,7 +2448,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
self.exception_table.as_mut().unwrap(),
|
||||
ExceptionCode::Memory,
|
||||
ExceptionCode::MemoryOutOfBounds,
|
||||
|a| a.emit_conditional_trap(Condition::Below),
|
||||
);
|
||||
}
|
||||
@ -2491,28 +2490,31 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
// Check interrupt signal without branching
|
||||
let activate_offset = a.get_offset().0;
|
||||
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(
|
||||
Machine::get_vmctx_reg(),
|
||||
vm::Ctx::offset_interrupt_signal_mem() as i32,
|
||||
),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
self.fsm.loop_offsets.insert(
|
||||
a.get_offset().0,
|
||||
OffsetInfo {
|
||||
end_offset: a.get_offset().0 + 1,
|
||||
activate_offset,
|
||||
diff_id: state_diff_id,
|
||||
},
|
||||
);
|
||||
self.fsm.wasm_function_header_target_offset = Some(SuspendOffset::Loop(a.get_offset().0));
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(GPR::RAX, 0),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
if self.config.full_preemption {
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(
|
||||
Machine::get_vmctx_reg(),
|
||||
vm::Ctx::offset_interrupt_signal_mem() as i32,
|
||||
),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
self.fsm.loop_offsets.insert(
|
||||
a.get_offset().0,
|
||||
OffsetInfo {
|
||||
end_offset: a.get_offset().0 + 1,
|
||||
activate_offset,
|
||||
diff_id: state_diff_id,
|
||||
},
|
||||
);
|
||||
self.fsm.wasm_function_header_target_offset =
|
||||
Some(SuspendOffset::Loop(a.get_offset().0));
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(GPR::RAX, 0),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
}
|
||||
|
||||
if self.machine.state.wasm_inst_offset != usize::MAX {
|
||||
return Err(CodegenError {
|
||||
@ -6320,10 +6322,10 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
self.exception_table.as_mut().unwrap(),
|
||||
ExceptionCode::Memory,
|
||||
ExceptionCode::CallIndirectOOB,
|
||||
|a| a.emit_conditional_trap(Condition::BelowEqual),
|
||||
);
|
||||
a.emit_mov(Size::S64, func_index, Location::GPR(table_count));
|
||||
a.emit_mov(Size::S32, func_index, Location::GPR(table_count));
|
||||
a.emit_imul_imm32_gpr64(vm::Anyfunc::size() as u32, table_count);
|
||||
a.emit_add(
|
||||
Size::S64,
|
||||
@ -6351,7 +6353,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
self.exception_table.as_mut().unwrap(),
|
||||
ExceptionCode::Memory,
|
||||
ExceptionCode::IncorrectCallIndirectSignature,
|
||||
|a| a.emit_conditional_trap(Condition::NotEqual),
|
||||
);
|
||||
|
||||
@ -6575,31 +6577,33 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
a.emit_label(label);
|
||||
|
||||
// Check interrupt signal without branching
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(
|
||||
Machine::get_vmctx_reg(),
|
||||
vm::Ctx::offset_interrupt_signal_mem() as i32,
|
||||
),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
self.fsm.loop_offsets.insert(
|
||||
a.get_offset().0,
|
||||
OffsetInfo {
|
||||
end_offset: a.get_offset().0 + 1,
|
||||
activate_offset,
|
||||
diff_id: state_diff_id,
|
||||
},
|
||||
);
|
||||
self.fsm.wasm_offset_to_target_offset.insert(
|
||||
self.machine.state.wasm_inst_offset,
|
||||
SuspendOffset::Loop(a.get_offset().0),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(GPR::RAX, 0),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
if self.config.full_preemption {
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(
|
||||
Machine::get_vmctx_reg(),
|
||||
vm::Ctx::offset_interrupt_signal_mem() as i32,
|
||||
),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
self.fsm.loop_offsets.insert(
|
||||
a.get_offset().0,
|
||||
OffsetInfo {
|
||||
end_offset: a.get_offset().0 + 1,
|
||||
activate_offset,
|
||||
diff_id: state_diff_id,
|
||||
},
|
||||
);
|
||||
self.fsm.wasm_offset_to_target_offset.insert(
|
||||
self.machine.state.wasm_inst_offset,
|
||||
SuspendOffset::Loop(a.get_offset().0),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(GPR::RAX, 0),
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
}
|
||||
}
|
||||
Operator::Nop => {}
|
||||
Operator::MemorySize { reserved } => {
|
||||
|
@ -12,11 +12,13 @@
|
||||
#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
|
||||
|
||||
#[cfg(not(any(
|
||||
all(target_os = "freebsd", target_arch = "x86_64"),
|
||||
all(target_os = "freebsd", target_arch = "aarch64"),
|
||||
all(target_os = "macos", target_arch = "x86_64"),
|
||||
all(target_os = "linux", target_arch = "x86_64"),
|
||||
all(target_os = "linux", target_arch = "aarch64"),
|
||||
)))]
|
||||
compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64");
|
||||
compile_error!("This crate doesn't yet support compiling on operating systems other than FreeBSD, linux and macos and architectures other than x86_64");
|
||||
|
||||
extern crate dynasmrt;
|
||||
|
||||
@ -38,7 +40,6 @@ extern crate smallvec;
|
||||
mod codegen_x64;
|
||||
mod emitter_x64;
|
||||
mod machine;
|
||||
pub mod protect_unix;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod translator_aarch64;
|
||||
|
||||
|
@ -1,53 +0,0 @@
|
||||
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
|
||||
//! accesses that occur when runniing WebAssembly.
|
||||
//!
|
||||
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
|
||||
//!
|
||||
//! When a WebAssembly module triggers any traps, we perform recovery here.
|
||||
//!
|
||||
//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling
|
||||
//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here
|
||||
//! unless you have memory unsafety elsewhere in your code.
|
||||
//!
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use wasmer_runtime_core::codegen::BreakpointMap;
|
||||
use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensure_sighandler};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
|
||||
thread_local! {
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any + Send>>> = Cell::new(None);
|
||||
}
|
||||
|
||||
pub unsafe fn trigger_trap() -> ! {
|
||||
begin_unsafe_unwind(Box::new(()));
|
||||
}
|
||||
|
||||
pub enum CallProtError {
|
||||
Trap(WasmTrapInfo),
|
||||
Error(Box<dyn Any + Send>),
|
||||
}
|
||||
|
||||
pub fn call_protected<T>(
|
||||
f: impl FnOnce() -> T,
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
) -> Result<T, CallProtError> {
|
||||
ensure_sighandler();
|
||||
unsafe {
|
||||
let ret = catch_unsafe_unwind(|| f(), breakpoints);
|
||||
match ret {
|
||||
Ok(x) => Ok(x),
|
||||
Err(e) => {
|
||||
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(CallProtError::Error(data))
|
||||
} else {
|
||||
Err(CallProtError::Error(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn throw(payload: Box<dyn Any + Send>) -> ! {
|
||||
begin_unsafe_unwind(payload);
|
||||
}
|
33
lib/spectests/spectests/wasmer.wast
vendored
Normal file
33
lib/spectests/spectests/wasmer.wast
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
;; Wasmer-specific tests.
|
||||
|
||||
(module
|
||||
;; Auxiliary definitions
|
||||
(type $out-i32 (func (result i32)))
|
||||
|
||||
(func $const-i32 (type $out-i32) (i32.const 0x132))
|
||||
|
||||
(table funcref
|
||||
(elem
|
||||
$const-i32
|
||||
)
|
||||
)
|
||||
|
||||
;; https://github.com/wasmerio/wasmer/pull/1191
|
||||
(func (export "call-indirect-from-spilled-stack") (result i32)
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0) (i64.const 0))
|
||||
(i64.add (i64.const 0x100000000) (i64.const 0))
|
||||
(i32.wrap_i64)
|
||||
(call_indirect (type $out-i32))
|
||||
(return)
|
||||
)
|
||||
)
|
||||
|
||||
(assert_return (invoke "call-indirect-from-spilled-stack") (i32.const 0x132))
|
@ -3,7 +3,7 @@ mod tests {
|
||||
use wabt::wat2wasm;
|
||||
use wasmer_runtime::{
|
||||
error::{CallError, RuntimeError},
|
||||
ImportObject,
|
||||
ExceptionCode, ImportObject,
|
||||
};
|
||||
|
||||
// The semantics of stack overflow are documented at:
|
||||
@ -29,9 +29,9 @@ mod tests {
|
||||
|
||||
match result {
|
||||
Err(err) => match err {
|
||||
CallError::Runtime(RuntimeError::Trap { msg }) => {
|
||||
assert!(!msg.contains("segmentation violation"));
|
||||
assert!(!msg.contains("bus error"));
|
||||
CallError::Runtime(RuntimeError(e)) => {
|
||||
e.downcast::<ExceptionCode>()
|
||||
.expect("expecting exception code");
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
|
@ -674,54 +674,41 @@ mod tests {
|
||||
let call_result = maybe_call_result.unwrap();
|
||||
use wasmer_runtime::error::{CallError, RuntimeError};
|
||||
match call_result {
|
||||
Err(e) => {
|
||||
match e {
|
||||
CallError::Resolve(_) => {
|
||||
Err(e) => match e {
|
||||
CallError::Resolve(_) => {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line,
|
||||
kind: format!("{}", "AssertTrap"),
|
||||
message: format!("expected trap, got {:?}", e),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
line,
|
||||
);
|
||||
}
|
||||
CallError::Runtime(RuntimeError(e)) => {
|
||||
use wasmer_runtime::ExceptionCode;
|
||||
if let Some(_) = e.downcast_ref::<ExceptionCode>() {
|
||||
test_report.count_passed();
|
||||
} else {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line,
|
||||
kind: format!("{}", "AssertTrap"),
|
||||
message: format!("expected trap, got {:?}", e),
|
||||
message: format!(
|
||||
"expected trap, got RuntimeError"
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
line,
|
||||
);
|
||||
}
|
||||
CallError::Runtime(r) => {
|
||||
match r {
|
||||
RuntimeError::Trap { .. } => {
|
||||
// TODO assert message?
|
||||
test_report.count_passed()
|
||||
}
|
||||
RuntimeError::Error { ref data } => {
|
||||
use wasmer_runtime::ExceptionCode;
|
||||
if let Some(_) =
|
||||
data.downcast_ref::<ExceptionCode>()
|
||||
{
|
||||
test_report.count_passed();
|
||||
} else {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line,
|
||||
kind: format!("{}", "AssertTrap"),
|
||||
message: format!(
|
||||
"expected trap, got Runtime:Error {:?}",
|
||||
r
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
line,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(values) => {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
|
15
lib/wasi-tests/tests/wasitests/isatty.rs
Normal file
15
lib/wasi-tests/tests/wasitests/isatty.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// !!! THIS IS A GENERATED FILE !!!
|
||||
// ANY MANUAL EDITS MAY BE OVERWRITTEN AT ANY TIME
|
||||
// Files autogenerated with cargo build (build/wasitests.rs).
|
||||
|
||||
#[test]
|
||||
fn test_isatty() {
|
||||
assert_wasi_output!(
|
||||
"../../wasitests/isatty.wasm",
|
||||
"isatty",
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
"../../wasitests/isatty.out"
|
||||
);
|
||||
}
|
@ -18,6 +18,7 @@ mod file_metadata;
|
||||
mod fs_sandbox_test;
|
||||
mod fseek;
|
||||
mod hello;
|
||||
mod isatty;
|
||||
mod mapdir;
|
||||
mod path_link;
|
||||
mod path_rename;
|
||||
|
3
lib/wasi-tests/wasitests/isatty.out
Normal file
3
lib/wasi-tests/wasitests/isatty.out
Normal file
@ -0,0 +1,3 @@
|
||||
stdin: 1
|
||||
stdout: 1
|
||||
stderr: 1
|
20
lib/wasi-tests/wasitests/isatty.rs
Normal file
20
lib/wasi-tests/wasitests/isatty.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// We don't have access to libc, so we just use isatty
|
||||
// as an external function
|
||||
// use libc::isatty;
|
||||
|
||||
extern "C" {
|
||||
pub fn isatty(fd: i32) -> i32;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(target = "wasi")] {
|
||||
println!("stdin: {}", unsafe { isatty(0) });
|
||||
println!("stdout: {}", unsafe { isatty(1) });
|
||||
println!("stderr: {}", unsafe { isatty(2) });
|
||||
}
|
||||
#[cfg(not(target = "wasi"))] {
|
||||
println!("stdin: 1");
|
||||
println!("stdout: 1");
|
||||
println!("stderr: 1");
|
||||
}
|
||||
}
|
BIN
lib/wasi-tests/wasitests/isatty.wasm
Executable file
BIN
lib/wasi-tests/wasitests/isatty.wasm
Executable file
Binary file not shown.
@ -5,7 +5,8 @@
|
||||
unused_mut,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
unreachable_patterns,
|
||||
clippy::missing_safety_doc
|
||||
)]
|
||||
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
|
||||
#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
|
||||
@ -43,7 +44,7 @@ pub use self::utils::{get_wasi_version, is_wasi_module, WasiVersion};
|
||||
|
||||
use wasmer_runtime_core::{func, import::ImportObject, imports};
|
||||
|
||||
/// This is returned in the Box<dyn Any> RuntimeError::Error variant.
|
||||
/// This is returned in `RuntimeError`.
|
||||
/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`.
|
||||
pub struct ExitCode {
|
||||
pub code: syscalls::types::__wasi_exitcode_t,
|
||||
|
@ -1,3 +1,7 @@
|
||||
//! Macros to simplify some common WASI-specific tasks.
|
||||
|
||||
/// Like the `try!` macro or `?` syntax: returns the value if the computation
|
||||
/// succeeded or returns the error value.
|
||||
macro_rules! wasi_try {
|
||||
($expr:expr) => {{
|
||||
let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr;
|
||||
@ -18,6 +22,8 @@ macro_rules! wasi_try {
|
||||
}};
|
||||
}
|
||||
|
||||
/// Reads a string from Wasm memory and returns the invalid argument error
|
||||
/// code if it fails.
|
||||
macro_rules! get_input_str {
|
||||
($memory:expr, $data:expr, $len:expr) => {{
|
||||
wasi_try!($data.get_utf8_string($memory, $len), __WASI_EINVAL)
|
||||
|
@ -36,7 +36,7 @@ use wasmer_runtime_core::vm::Ctx;
|
||||
/// the fd value of the virtual root
|
||||
pub const VIRTUAL_ROOT_FD: __wasi_fd_t = 3;
|
||||
/// all the rights enabled
|
||||
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFFFFFF;
|
||||
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFF_FFFF;
|
||||
const STDIN_DEFAULT_RIGHTS: __wasi_rights_t = __WASI_RIGHT_FD_DATASYNC
|
||||
| __WASI_RIGHT_FD_READ
|
||||
| __WASI_RIGHT_FD_SYNC
|
||||
@ -52,7 +52,10 @@ const STDOUT_DEFAULT_RIGHTS: __wasi_rights_t = __WASI_RIGHT_FD_DATASYNC
|
||||
const STDERR_DEFAULT_RIGHTS: __wasi_rights_t = STDOUT_DEFAULT_RIGHTS;
|
||||
|
||||
/// Get WasiState from a Ctx
|
||||
/// This function is unsafe because it must be called on a WASI Ctx
|
||||
///
|
||||
/// # Safety
|
||||
/// - This function must be called on a `Ctx` that was created with `WasiState`
|
||||
/// in the data field
|
||||
pub unsafe fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
|
||||
&mut *(ctx.data as *mut WasiState)
|
||||
}
|
||||
@ -186,7 +189,7 @@ impl WasiFs {
|
||||
for dir in preopened_dirs {
|
||||
debug!("Attempting to preopen {}", &dir.to_string_lossy());
|
||||
// TODO: think about this
|
||||
let default_rights = 0x1FFFFFFF; // all rights
|
||||
let default_rights = ALL_RIGHTS;
|
||||
let cur_dir_metadata = dir.metadata().map_err(|e| {
|
||||
format!(
|
||||
"Could not get metadata for file {:?}: {}",
|
||||
@ -236,7 +239,7 @@ impl WasiFs {
|
||||
for (alias, real_dir) in mapped_dirs {
|
||||
debug!("Attempting to open {:?} at {}", real_dir, alias);
|
||||
// TODO: think about this
|
||||
let default_rights = 0x1FFFFFFF; // all rights
|
||||
let default_rights = ALL_RIGHTS;
|
||||
let cur_dir_metadata = real_dir.metadata().map_err(|e| {
|
||||
format!(
|
||||
"Could not get metadata for file {:?}: {}",
|
||||
@ -428,7 +431,7 @@ impl WasiFs {
|
||||
|
||||
// create virtual root
|
||||
let root_inode = {
|
||||
let all_rights = 0x1FFFFFFF;
|
||||
let all_rights = ALL_RIGHTS;
|
||||
// TODO: make this a list of positive rigths instead of negative ones
|
||||
// root gets all right for now
|
||||
let root_rights = all_rights
|
||||
@ -525,10 +528,15 @@ impl WasiFs {
|
||||
next
|
||||
}
|
||||
|
||||
/// like create dir all, but it also opens it
|
||||
/// This function is like create dir all, but it also opens it.
|
||||
/// Function is unsafe because it may break invariants and hasn't been tested.
|
||||
/// This is an experimental function and may be removed
|
||||
// dead code because this is an API for external use
|
||||
///
|
||||
/// # Safety
|
||||
/// - Virtual directories created with this function must not conflict with
|
||||
/// the standard operation of the WASI filesystem. This is vague and
|
||||
/// unlikely in pratice. Join the discussion at https://github.com/wasmerio/wasmer/issues/1219
|
||||
/// for what the newer, safer WASI FS APIs should look like.
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn open_dir_all(
|
||||
&mut self,
|
||||
@ -1045,12 +1053,28 @@ impl WasiFs {
|
||||
|
||||
pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> {
|
||||
match fd {
|
||||
__WASI_STDOUT_FILENO => {
|
||||
__WASI_STDIN_FILENO => {
|
||||
return Ok(__wasi_fdstat_t {
|
||||
fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
fs_flags: 0,
|
||||
fs_rights_base: ALL_RIGHTS,
|
||||
fs_rights_inheriting: ALL_RIGHTS,
|
||||
fs_rights_base: STDIN_DEFAULT_RIGHTS,
|
||||
fs_rights_inheriting: 0,
|
||||
})
|
||||
}
|
||||
__WASI_STDOUT_FILENO => {
|
||||
return Ok(__wasi_fdstat_t {
|
||||
fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
fs_flags: __WASI_FDFLAG_APPEND,
|
||||
fs_rights_base: STDOUT_DEFAULT_RIGHTS,
|
||||
fs_rights_inheriting: 0,
|
||||
})
|
||||
}
|
||||
__WASI_STDERR_FILENO => {
|
||||
return Ok(__wasi_fdstat_t {
|
||||
fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
fs_flags: __WASI_FDFLAG_APPEND,
|
||||
fs_rights_base: STDERR_DEFAULT_RIGHTS,
|
||||
fs_rights_inheriting: 0,
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
@ -1145,7 +1169,7 @@ impl WasiFs {
|
||||
stat.st_ino = self.get_next_inode_index();
|
||||
|
||||
Ok(self.inodes.insert(InodeVal {
|
||||
stat: stat,
|
||||
stat,
|
||||
is_preopened,
|
||||
name,
|
||||
kind,
|
||||
@ -1194,10 +1218,14 @@ impl WasiFs {
|
||||
Ok(idx)
|
||||
}
|
||||
|
||||
/// This function is unsafe because it's the caller's responsibility to ensure that
|
||||
/// all refences to the given inode have been removed from the filesystem
|
||||
/// Low level function to remove an inode, that is it deletes the WASI FS's
|
||||
/// knowledge of a file.
|
||||
///
|
||||
/// returns the inode if it existed and was removed
|
||||
/// This function returns the inode if it existed and was removed.
|
||||
///
|
||||
/// # Safety
|
||||
/// - The caller must ensure that all references to the specified inode have
|
||||
/// been removed from the filesystem.
|
||||
pub unsafe fn remove_inode(&mut self, inode: Inode) -> Option<InodeVal> {
|
||||
self.inodes.remove(inode)
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ impl<'de> Deserialize<'de> for HostFile {
|
||||
}
|
||||
}
|
||||
|
||||
const FIELDS: &'static [&'static str] = &["host_path", "flags"];
|
||||
const FIELDS: &[&str] = &["host_path", "flags"];
|
||||
deserializer.deserialize_struct("HostFile", FIELDS, HostFileVisitor)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![allow(unused, clippy::too_many_arguments)]
|
||||
pub mod types;
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "macos"))]
|
||||
pub mod unix;
|
||||
#[cfg(any(target_os = "windows"))]
|
||||
pub mod windows;
|
||||
@ -23,7 +23,7 @@ use std::convert::{Infallible, TryInto};
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
use wasmer_runtime_core::{memory::Memory, vm::Ctx};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "macos"))]
|
||||
pub use unix::*;
|
||||
|
||||
#[cfg(any(target_os = "windows"))]
|
||||
@ -1221,7 +1221,14 @@ pub fn fd_write(
|
||||
iovs_len: u32,
|
||||
nwritten: WasmPtr<u32>,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::fd_write: fd={}", fd);
|
||||
// If we are writing to stdout or stderr
|
||||
// we skip debug to not pollute the stdout/err
|
||||
// and do debugging happily after :)
|
||||
if fd != __WASI_STDOUT_FILENO && fd != __WASI_STDERR_FILENO {
|
||||
debug!("wasi::fd_write: fd={}", fd);
|
||||
} else {
|
||||
trace!("wasi::fd_write: fd={}", fd);
|
||||
}
|
||||
let (memory, state) = get_memory_and_wasi_state(ctx, 0);
|
||||
let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len));
|
||||
let nwritten_cell = wasi_try!(nwritten.deref(memory));
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_camel_case_types, clippy::identity_op)]
|
||||
|
||||
use crate::ptr::{Array, WasmPtr};
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
||||
|
@ -30,10 +30,10 @@ pub enum WasiVersion {
|
||||
}
|
||||
|
||||
/// Namespace for the `Snapshot0` version.
|
||||
const SNAPSHOT0_NAMESPACE: &'static str = "wasi_unstable";
|
||||
const SNAPSHOT0_NAMESPACE: &str = "wasi_unstable";
|
||||
|
||||
/// Namespace for the `Snapshot1` version.
|
||||
const SNAPSHOT1_NAMESPACE: &'static str = "wasi_snapshot_preview1";
|
||||
const SNAPSHOT1_NAMESPACE: &str = "wasi_snapshot_preview1";
|
||||
|
||||
/// Detect the version of WASI being used based on the import
|
||||
/// namespaces.
|
||||
|
@ -59,6 +59,7 @@ uint8_t callProtected(trampoline_t trampoline,
|
||||
// install exception handler
|
||||
if (exceptionHandlerInstalled == FALSE) {
|
||||
exceptionHandlerInstalled = TRUE;
|
||||
alreadyHandlingException = FALSE;
|
||||
handle = AddVectoredExceptionHandler(CALL_FIRST, exceptionHandler);
|
||||
}
|
||||
|
||||
@ -86,4 +87,4 @@ uint8_t callProtected(trampoline_t trampoline,
|
||||
|
||||
removeExceptionHandler();
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -486,7 +486,6 @@ fn execute_wasi(
|
||||
|
||||
#[cfg(not(feature = "managed"))]
|
||||
{
|
||||
use wasmer_runtime::error::RuntimeError;
|
||||
let result;
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -525,13 +524,8 @@ fn execute_wasi(
|
||||
}
|
||||
|
||||
if let Err(ref err) = result {
|
||||
match err {
|
||||
RuntimeError::Trap { msg } => return Err(format!("wasm trap occured: {}", msg)),
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(error_code) = data.downcast_ref::<wasmer_wasi::ExitCode>() {
|
||||
std::process::exit(error_code.code as i32)
|
||||
}
|
||||
}
|
||||
if let Some(error_code) = err.0.downcast_ref::<wasmer_wasi::ExitCode>() {
|
||||
std::process::exit(error_code.code as i32)
|
||||
}
|
||||
return Err(format!("error: {:?}", err));
|
||||
}
|
||||
@ -711,6 +705,10 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
symbol_map: em_symbol_map.clone(),
|
||||
memory_bound_check_mode: MemoryBoundCheckMode::Disable,
|
||||
enforce_stack_check: true,
|
||||
|
||||
// Kernel loader does not support explicit preemption checkpoints.
|
||||
full_preemption: false,
|
||||
|
||||
track_state,
|
||||
features: options.features.into_backend_features(),
|
||||
backend_specific_config,
|
||||
@ -725,6 +723,11 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
CompilerConfig {
|
||||
symbol_map: em_symbol_map.clone(),
|
||||
track_state,
|
||||
|
||||
// Enable full preemption if state tracking is enabled.
|
||||
// Preemption only makes sense with state information.
|
||||
full_preemption: track_state,
|
||||
|
||||
features: options.features.into_backend_features(),
|
||||
backend_specific_config,
|
||||
generate_debug_info: options.generate_debug_info,
|
||||
@ -822,7 +825,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
LoaderName::Kernel => Box::new(
|
||||
instance
|
||||
.load(::wasmer_kernel_loader::KernelLoader)
|
||||
.map_err(|e| format!("Can't use the local loader: {:?}", e))?,
|
||||
.map_err(|e| format!("Can't use the kernel loader: {:?}", e))?,
|
||||
),
|
||||
};
|
||||
println!("{:?}", ins.call(index, &args));
|
||||
|
Loading…
Reference in New Issue
Block a user