mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-16 16:20:49 +00:00
Merge #787
787: feat(interface-types) Implement Interface Types (WIT) r=Hywan a=Hywan So far, this PR implements a new crate named `wasmer-interface-types`. The goal is to implement the WebAssembly Interface Types (WIT) proposal, https://github.com/WebAssembly/interface-types. Keep in mind the proposal is highly unstable and experimental. The `wasmer-interface-types` is designed to be _runtime agnostic_, which means that there is no connection between this crate and `wasmer-runtime-core` or `wasmer-runtime` (or any backend crates). The hope is that other runtimes could use `wasmer-interface-types` according to their needs. The `wasmer-interface-types` is composed of 4 parts: * AST: [The WIT language, represented as an AST](https://github.com/Hywan/wasmer/blob/feat-interface-types/lib/interface-types/src/ast.rs) (it's not really abstract though), * Decoders: So far, it contains only [one parser that reads the WIT binary language](https://github.com/Hywan/wasmer/blob/feat-interface-types/lib/interface-types/src/decoders/binary.rs) and produces an AST, * Encoders: So far, it contains only [one compiler that transforms the AST into "WIT.wat”](https://github.com/Hywan/wasmer/blob/feat-interface-types/lib/interface-types/src/encoders/wat.rs), i.e. the textual representation of an Interface Types AST * Instructions: WIT defines a new concept called Adapters. An adapter contains a set of instructions. Those instructions are executed when data are bound. So, in more details, this module contains: * [`interpreter.rs`](https://github.com/Hywan/wasmer/blob/feat-interface-types/lib/interface-types/src/interpreter/mod.rs), 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 Wasm instance (which holds the exports, the imports, the memories, the tables etc.), * [`stack.rs`](https://github.com/Hywan/wasmer/blob/feat-interface-types/lib/interface-types/src/interpreter/stack.rs), a very light and generic stack implementation, exposing only the API required by the interpreter, * [`wasm/*.rs`](https://github.com/Hywan/wasmer/blob/feat-interface-types/lib/interface-types/src/interpreter/wasm/structures.rs), a set of enums, types, and traits to represent a Wasm runtime —basically this is the part a runtime should take a look to support the `wasmer-interface-types` crate—. Progression: * [x] WIT binary parser, * [x] WIT.wat compiler, * [ ] Interpreter: * [ ] Wasm runtime traits to represent: * [x] Instance, * [x] Export, * [x] Memory, * [x] Import, * [ ] Table, * [x] Generic stack implementation, * [ ] Implemented instructions: * [x] `arg.get`, * [x] `call`, * [x] `call-export`, * [x] `read-utf8`, * [x] `write-utf8`, * [ ] `as-wasm`, * [ ] `as-interface` * [ ] `table-ref-add` * [ ] `table-ref-get` * [ ] `call-method` * [ ] `make-record` * [ ] `get-field` * [ ] `const` * [ ] `fold-seq` * [ ] `add` * [ ] `mem-to-seq` * [ ] `load` * [ ] `seq.new` * [ ] `list.push` * [ ] `repeat-until` * [ ] `seq-to-mem` * [ ] `store` * [ ] Plug `wasmer-interface-types` into `wasmer-runtime-core` Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
This commit is contained in:
commit
16245c6bd2
297
Cargo.lock
generated
297
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",
|
||||
]
|
||||
@ -20,9 +20,18 @@ dependencies = [
|
||||
|
||||
[[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"
|
||||
@ -32,10 +41,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",
|
||||
]
|
||||
@ -70,12 +80,12 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "100179f909a27ed067ce4fa6db58f7fa0f67070d7a04d38f040886174b85ef6f"
|
||||
checksum = "46080006c1505f12f64dd2a09264b343381ed3190fa02c8005d5d662ac571c63"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"arrayvec 0.5.1",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"constant_time_eq",
|
||||
@ -85,9 +95,9 @@ dependencies = [
|
||||
|
||||
[[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",
|
||||
@ -127,11 +137,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",
|
||||
]
|
||||
@ -230,7 +240,7 @@ dependencies = [
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-entity",
|
||||
"log",
|
||||
"smallvec 1.1.0",
|
||||
"smallvec 1.2.0",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
]
|
||||
@ -332,10 +342,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",
|
||||
]
|
||||
|
||||
@ -362,9 +373,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
|
||||
checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
@ -375,9 +386,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",
|
||||
]
|
||||
@ -389,7 +400,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]]
|
||||
@ -411,9 +422,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]]
|
||||
@ -434,9 +445,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",
|
||||
]
|
||||
@ -521,9 +532,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]]
|
||||
@ -560,9 +571,9 @@ 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",
|
||||
]
|
||||
@ -609,9 +620,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",
|
||||
@ -620,13 +631,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]]
|
||||
@ -640,9 +651,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"
|
||||
@ -654,6 +665,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"
|
||||
@ -675,9 +699,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",
|
||||
]
|
||||
@ -705,12 +729,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"
|
||||
@ -758,6 +779,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"
|
||||
@ -771,39 +809,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",
|
||||
@ -811,9 +849,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"
|
||||
@ -882,7 +920,7 @@ dependencies = [
|
||||
"cloudabi",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.1.0",
|
||||
"smallvec 1.2.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -910,27 +948,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",
|
||||
]
|
||||
|
||||
@ -945,9 +983,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",
|
||||
]
|
||||
@ -967,7 +1005,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]]
|
||||
@ -991,9 +1029,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",
|
||||
@ -1185,9 +1223,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",
|
||||
@ -1206,9 +1244,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"
|
||||
@ -1230,13 +1268,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]]
|
||||
@ -1247,9 +1285,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",
|
||||
]
|
||||
@ -1354,16 +1392,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",
|
||||
@ -1381,9 +1419,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"
|
||||
@ -1391,6 +1429,12 @@ version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
@ -1399,9 +1443,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",
|
||||
@ -1410,15 +1454,15 @@ 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]]
|
||||
@ -1440,24 +1484,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]]
|
||||
@ -1474,7 +1518,7 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rand 0.7.2",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
@ -1491,22 +1535,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]]
|
||||
@ -1531,9 +1575,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",
|
||||
@ -1541,9 +1585,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",
|
||||
]
|
||||
@ -1573,9 +1617,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]]
|
||||
@ -1608,6 +1652,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"
|
||||
@ -1639,9 +1689,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",
|
||||
@ -1717,7 +1767,7 @@ checksum = "6d2e13201ef9ef527ad30a6bf1b08e3e024a40cf2731f393d80375dc88506207"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
"smallvec 1.1.0",
|
||||
"smallvec 1.2.0",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
@ -1769,6 +1819,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"
|
||||
@ -2000,9 +2057,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",
|
||||
]
|
||||
@ -2015,9 +2072,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",
|
||||
|
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"
|
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>>,
|
||||
}
|
983
lib/interface-types/src/decoders/binary.rs
Normal file
983
lib/interface-types/src/decoders/binary.rs
Normal file
@ -0,0 +1,983 @@
|
||||
//! 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 dbg!(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 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> {
|
||||
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,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[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!(parse::<()>(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;
|
5
lib/interface-types/src/encoders/mod.rs
Normal file
5
lib/interface-types/src/encoders/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
//! 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 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);
|
55
lib/interface-types/src/lib.rs
Normal file
55
lib/interface-types/src/lib.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! 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;
|
||||
|
||||
pub use decoders::binary::parse as parse_binary;
|
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));
|
||||
}
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user