├── tmp └── .keep ├── rust-toolchain ├── dist ├── add.wasm ├── cons8.wasm ├── sub.wasm ├── count.wasm ├── add_five.wasm ├── cons16.wasm ├── if_eq.wasm ├── signed.wasm ├── fib.wasm ├── if_gt.wasm ├── if_lt.wasm ├── cons8.wat ├── cons16.wat ├── signed.wat ├── sub.wat ├── add.wat ├── add_five.wat ├── if_eq.wat ├── fib.wat ├── if_gt.wat ├── if_lt.wat └── count.wat ├── fixtures ├── signed.c ├── sub.c ├── cons8.c ├── cons16.c ├── add.c ├── count.c ├── add_five.c ├── fib.c ├── if_eq.c ├── if_gt.c └── if_lt.c ├── discovery ├── src │ ├── add.wasm │ └── main.rs ├── openocd.cfg ├── build.rs ├── Cargo.toml ├── openocd.gdb ├── .cargo │ └── config ├── memory.x └── Cargo.lock ├── .gitignore ├── ci ├── submodule.sh ├── script.sh └── install.sh ├── .gitmodules ├── discovery-wasm ├── src │ └── lib.rs ├── Cargo.toml └── Cargo.lock ├── src ├── label.rs ├── decode │ ├── sec_start.rs │ ├── sec_function.rs │ ├── mod.rs │ ├── sec_memory.rs │ ├── code.rs │ ├── sec_custom.rs │ ├── sec_export.rs │ ├── sec_global.rs │ ├── sec_table.rs │ ├── sec_type.rs │ ├── sec_data.rs │ ├── sec_code.rs │ ├── sec_import.rs │ ├── sec_element.rs │ ├── decodable.rs │ ├── instruction.rs │ └── section.rs ├── indice.rs ├── value_type.rs ├── store.rs ├── embedder.rs ├── error.rs ├── spectest.rs ├── frame.rs ├── lib.rs ├── table.rs ├── global.rs ├── function.rs ├── stack.rs ├── module.rs ├── memory.rs └── isa.rs ├── README.md ├── Cargo.toml ├── report.txt ├── report.node.txt ├── run-wasm.js ├── bin └── main.rs ├── .travis.yml ├── benches └── run.rs ├── Makefile ├── Cargo.lock └── tests └── run.rs /tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2018-11-23 2 | -------------------------------------------------------------------------------- /dist/add.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 |   j -------------------------------------------------------------------------------- /dist/cons8.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 | A* -------------------------------------------------------------------------------- /fixtures/signed.c: -------------------------------------------------------------------------------- 1 | int subject() { 2 | return -129; 3 | } 4 | -------------------------------------------------------------------------------- /fixtures/sub.c: -------------------------------------------------------------------------------- 1 | int subject(int a) { 2 | return 100 - a; 3 | } 4 | -------------------------------------------------------------------------------- /discovery/src/add.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 |   j -------------------------------------------------------------------------------- /dist/sub.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogai/wasvm/HEAD/dist/sub.wasm -------------------------------------------------------------------------------- /dist/count.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogai/wasvm/HEAD/dist/count.wasm -------------------------------------------------------------------------------- /dist/add_five.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 |  3 | A 4 | j j -------------------------------------------------------------------------------- /dist/cons16.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogai/wasvm/HEAD/dist/cons16.wasm -------------------------------------------------------------------------------- /dist/if_eq.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 |  A 3 | FAA 4 | j -------------------------------------------------------------------------------- /dist/signed.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogai/wasvm/HEAD/dist/signed.wasm -------------------------------------------------------------------------------- /fixtures/cons8.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int subject() { 4 | return 42; 5 | } 6 | -------------------------------------------------------------------------------- /fixtures/cons16.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int subject() { 4 | return 255; 5 | } 6 | -------------------------------------------------------------------------------- /dist/fib.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 | +" AI  A~j! Aj j   -------------------------------------------------------------------------------- /fixtures/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int subject(int a, int b) { 4 | return a + b; 5 | } 6 | -------------------------------------------------------------------------------- /dist/if_gt.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 | (& A 3 | J A 4 | j Aj! A 5 | FA  -------------------------------------------------------------------------------- /dist/if_lt.wasm: -------------------------------------------------------------------------------- 1 | asm` _subject 2 | (& A 3 | H A 4 | j Aj! A 5 | FA  -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | perf.* 3 | out.perf 4 | out.perf_folded 5 | out.svg 6 | !tmp/.keep 7 | tmp/ 8 | /discovery/src/*.wasm 9 | -------------------------------------------------------------------------------- /dist/cons8.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (result i32))) 3 | (func (;0;) (type 0) (result i32) 4 | i32.const 42) 5 | (export "_subject" (func 0))) 6 | -------------------------------------------------------------------------------- /dist/cons16.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (result i32))) 3 | (func (;0;) (type 0) (result i32) 4 | i32.const 255) 5 | (export "_subject" (func 0))) 6 | -------------------------------------------------------------------------------- /dist/signed.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (result i32))) 3 | (func (;0;) (type 0) (result i32) 4 | i32.const -129) 5 | (export "_subject" (func 0))) 6 | -------------------------------------------------------------------------------- /fixtures/count.c: -------------------------------------------------------------------------------- 1 | int subject(int max) 2 | { 3 | int res = 0; 4 | for (int i = 0; i < max; i++) 5 | { 6 | res = res + max + i; 7 | }; 8 | return res; 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/add_five.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int add_five(int a) { 4 | return a + 5; 5 | } 6 | 7 | int subject(int a, int b) { 8 | return add_five(a) + add_five(b); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/fib.c: -------------------------------------------------------------------------------- 1 | int fib(int n) { 2 | if (n == 0 | n == 1) { 3 | return n; 4 | } 5 | return fib(n - 2) + fib(n - 1); 6 | } 7 | 8 | int subject(int a) { 9 | return fib(a); 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/if_eq.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int subject(int a) { 4 | if (a == 10) { 5 | return a + 5; 6 | } 7 | if (a != 10) { 8 | return a + 10; 9 | } 10 | return a + 15; 11 | } 12 | -------------------------------------------------------------------------------- /fixtures/if_gt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int subject(int a) { 4 | if (a > 10) { 5 | return a + 10; 6 | } 7 | if (a >= 10) { 8 | return a + 5; 9 | } 10 | return a + 15; 11 | } 12 | -------------------------------------------------------------------------------- /fixtures/if_lt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int subject(int a) { 4 | if (a < 10) { 5 | return a + 10; 6 | } 7 | if (a <= 10) { 8 | return a + 5; 9 | } 10 | return a + 15; 11 | } 12 | -------------------------------------------------------------------------------- /dist/sub.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (func (;0;) (type 0) (param i32) (result i32) 4 | i32.const 100 5 | get_local 0 6 | i32.sub) 7 | (export "_subject" (func 0))) 8 | -------------------------------------------------------------------------------- /dist/add.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32 i32) (result i32))) 3 | (func (;0;) (type 0) (param i32 i32) (result i32) 4 | get_local 1 5 | get_local 0 6 | i32.add) 7 | (export "_subject" (func 0))) 8 | -------------------------------------------------------------------------------- /ci/submodule.sh: -------------------------------------------------------------------------------- 1 | if [ "$TRAVIS_OS_NAME" == "linux" ]; then 2 | sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules 3 | else 4 | sed -i '' 's/git@github.com:/https:\/\/github.com\//' .gitmodules 5 | fi 6 | 7 | git submodule update --init --recursive 8 | -------------------------------------------------------------------------------- /dist/add_five.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32 i32) (result i32))) 3 | (func (;0;) (type 0) (param i32 i32) (result i32) 4 | get_local 0 5 | i32.const 10 6 | i32.add 7 | get_local 1 8 | i32.add) 9 | (export "_subject" (func 0))) 10 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | set -euxo pipefail 2 | 3 | main() { 4 | 5 | if [ $TARGET = x86_64-unknown-linux-gnu ]; then 6 | cargo clippy 7 | cargo test --target $TARGET 8 | else 9 | cd discovery 10 | cargo check --target $TARGET 11 | fi 12 | } 13 | 14 | main 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "testsuite"] 2 | path = testsuite 3 | url = git@github.com:WebAssembly/testsuite.git 4 | [submodule "FlameGraph"] 5 | path = FlameGraph 6 | url = git@github.com:brendangregg/FlameGraph.git 7 | [submodule "life"] 8 | path = life 9 | url = git@github.com:perlin-network/life.git 10 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | set -euxo pipefail 2 | 3 | main() { 4 | rustup component add clippy-preview llvm-tools-preview 5 | cargo install --force wasm-pack 6 | make discovery/src/discovery_wasm_bg.wasm 7 | case $TARGET in 8 | thumbv*-none-eabi*) 9 | rustup target add $TARGET 10 | ;; 11 | esac 12 | } 13 | 14 | main 15 | -------------------------------------------------------------------------------- /discovery-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate wasm_bindgen; 2 | 3 | use wasm_bindgen::prelude::*; 4 | 5 | #[wasm_bindgen] 6 | extern { 7 | #[no_mangle] 8 | pub fn my_hal_function(a: i32, b: i32) -> i32; 9 | } 10 | 11 | #[no_mangle] 12 | #[wasm_bindgen] 13 | pub fn use_hal_function(a: i32, b: i32) -> i32 { 14 | my_hal_function(a, b) + 10 15 | } 16 | 17 | -------------------------------------------------------------------------------- /dist/if_eq.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (func (;0;) (type 0) (param i32) (result i32) 4 | get_local 0 5 | i32.const 10 6 | i32.eq 7 | if (result i32) ;; label = @1 8 | i32.const 5 9 | else 10 | i32.const 10 11 | end 12 | get_local 0 13 | i32.add) 14 | (export "_subject" (func 0))) 15 | -------------------------------------------------------------------------------- /src/label.rs: -------------------------------------------------------------------------------- 1 | use value_type::ValueTypes; 2 | 3 | #[derive(PartialEq, Debug, Clone)] 4 | pub enum LabelKind { 5 | If, 6 | Else, 7 | Loop, 8 | Block, 9 | Frame, 10 | } 11 | 12 | #[derive(PartialEq, Debug, Clone)] 13 | pub struct Label { 14 | pub(crate) source_instruction: LabelKind, 15 | // FIXME: To Vec type 16 | pub(crate) return_type: ValueTypes, 17 | pub(crate) continuation: u32, 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/kogai/wasvm.svg?branch=master)](https://travis-ci.org/kogai/wasvm) 2 | 3 | :construction: WebAssembly Virtual Machine, which aim to fit embedded systems. 4 | 5 | ## Current status 6 | 7 | - [x] Almost testsuit has been [passed](https://travis-ci.org/kogai/wasvm) 8 | - [x] no_std 9 | - [x] Run on STM32F3DISCOVERY 10 | - [ ] Run wasm binary build with LLVM on STM32F3DISCOVERY 11 | -------------------------------------------------------------------------------- /src/decode/sec_start.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Decodable, Leb128Decodable, U32Decodable}; 2 | use alloc::vec::Vec; 3 | use error::Result; 4 | 5 | impl_decodable!(Section); 6 | impl Leb128Decodable for Section {} 7 | impl U32Decodable for Section {} 8 | 9 | impl Decodable for Section { 10 | type Item = u32; 11 | fn decode(&mut self) -> Result { 12 | let start_fn_idx = self.decode_leb128_u32()?; 13 | Ok(start_fn_idx) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /discovery/openocd.cfg: -------------------------------------------------------------------------------- 1 | # Sample OpenOCD configuration for the STM32F3DISCOVERY development board 2 | 3 | # Depending on the hardware revision you got you'll have to pick ONE of these 4 | # interfaces. At any time only one interface should be commented out. 5 | 6 | # Revision C (newer revision) 7 | source [find interface/stlink-v2-1.cfg] 8 | 9 | # Revision A and B (older revisions) 10 | # source [find interface/stlink-v2.cfg] 11 | 12 | source [find target/stm32f3x.cfg] 13 | -------------------------------------------------------------------------------- /discovery-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "discovery-wasm" 3 | version = "0.1.0" 4 | authors = ["Shinichi Kogai "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | wasm-bindgen = "0.2.33" 9 | 10 | [lib] 11 | crate-type = ["cdylib"] 12 | 13 | [profile.release] 14 | opt-level = 'z' 15 | codegen-units = 1 # better optimizations 16 | debug = true # symbols are nice and they don't increase the size on Flash 17 | lto = true # better optimizations 18 | panic = 'abort' 19 | -------------------------------------------------------------------------------- /src/indice.rs: -------------------------------------------------------------------------------- 1 | use core::convert::From; 2 | 3 | #[derive(Debug, Clone, PartialEq)] 4 | pub struct Indice(u32); 5 | 6 | impl From for Indice { 7 | fn from(n: u32) -> Self { 8 | Indice(n) 9 | } 10 | } 11 | 12 | impl From for Indice { 13 | fn from(n: usize) -> Self { 14 | Indice(n as u32) 15 | } 16 | } 17 | 18 | impl Indice { 19 | pub fn to_usize(&self) -> usize { 20 | self.0 as usize 21 | } 22 | 23 | pub fn to_u32(&self) -> u32 { 24 | self.0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasvm" 3 | version = "0.2.0" 4 | authors = ["Shinichi Kogai "] 5 | 6 | [dependencies] 7 | # FIXME: Use forked version which doing cargo-cult until an issue below solved. 8 | # https://github.com/rust-lang-nursery/libm/issues/4 9 | libm = { version = "0.1.2", git = "https://github.com/kogai/libm" } 10 | heapless = { version = "0.4.1", git = "https://github.com/japaric/heapless" } 11 | 12 | [dev-dependencies] 13 | wabt = "0.7.3" 14 | flame = "0.2.2" 15 | 16 | [[bin]] 17 | name = "main" 18 | path = "bin/main.rs" 19 | -------------------------------------------------------------------------------- /src/decode/sec_function.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Decodable, Leb128Decodable, U32Decodable}; 2 | use alloc::vec::Vec; 3 | use error::Result; 4 | 5 | impl_decodable!(Section); 6 | impl Leb128Decodable for Section {} 7 | impl U32Decodable for Section {} 8 | 9 | impl Decodable for Section { 10 | type Item = Vec; 11 | fn decode(&mut self) -> Result { 12 | let count_of_section = self.decode_leb128_u32()?; 13 | (0..count_of_section) 14 | .map(|_| Ok(self.decode_leb128_u32()? as u32)) 15 | .collect::>>() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/decode/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod decodable; 3 | mod byte; 4 | mod code; 5 | mod instruction; 6 | mod sec_code; 7 | mod sec_custom; 8 | mod sec_data; 9 | mod sec_element; 10 | mod sec_export; 11 | mod sec_function; 12 | mod sec_global; 13 | mod sec_import; 14 | mod sec_memory; 15 | mod sec_start; 16 | mod sec_table; 17 | mod sec_type; 18 | mod section; 19 | 20 | pub use self::byte::Byte; 21 | pub use self::decodable::{AbstractDecodable, U8Iterator}; 22 | pub use self::sec_data::Data; 23 | pub use self::sec_element::{Element, ElementType}; 24 | pub use self::sec_table::TableType; 25 | pub use self::section::Module; 26 | -------------------------------------------------------------------------------- /src/decode/sec_memory.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Decodable, Leb128Decodable, LimitDecodable, U32Decodable}; 2 | use alloc::vec::Vec; 3 | use error::Result; 4 | use memory::Limit; 5 | 6 | impl_decodable!(Section); 7 | impl Leb128Decodable for Section {} 8 | impl U32Decodable for Section {} 9 | impl LimitDecodable for Section {} 10 | 11 | impl Decodable for Section { 12 | type Item = Vec; 13 | fn decode(&mut self) -> Result { 14 | let count_of_section = self.decode_leb128_u32()?; 15 | (0..count_of_section) 16 | .map(|_| self.decode_limit()) 17 | .collect::>>() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dist/fib.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (func (;0;) (type 0) (param i32) (result i32) 4 | (local i32) 5 | get_local 0 6 | i32.const 2 7 | i32.lt_u 8 | if (result i32) ;; label = @1 9 | get_local 0 10 | else 11 | get_local 0 12 | i32.const -2 13 | i32.add 14 | call 0 15 | set_local 1 16 | get_local 0 17 | i32.const -1 18 | i32.add 19 | call 0 20 | get_local 1 21 | i32.add 22 | end) 23 | (func (;1;) (type 0) (param i32) (result i32) 24 | get_local 0 25 | call 0) 26 | (export "_subject" (func 1))) 27 | -------------------------------------------------------------------------------- /dist/if_gt.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (func (;0;) (type 0) (param i32) (result i32) 4 | (local i32) 5 | get_local 0 6 | i32.const 10 7 | i32.gt_s 8 | if (result i32) ;; label = @1 9 | get_local 0 10 | i32.const 10 11 | i32.add 12 | else 13 | get_local 0 14 | i32.const 15 15 | i32.add 16 | set_local 1 17 | get_local 0 18 | i32.const 10 19 | i32.eq 20 | if (result i32) ;; label = @2 21 | i32.const 15 22 | else 23 | get_local 1 24 | end 25 | end) 26 | (export "_subject" (func 0))) 27 | -------------------------------------------------------------------------------- /dist/if_lt.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (func (;0;) (type 0) (param i32) (result i32) 4 | (local i32) 5 | get_local 0 6 | i32.const 10 7 | i32.lt_s 8 | if (result i32) ;; label = @1 9 | get_local 0 10 | i32.const 10 11 | i32.add 12 | else 13 | get_local 0 14 | i32.const 15 15 | i32.add 16 | set_local 1 17 | get_local 0 18 | i32.const 10 19 | i32.eq 20 | if (result i32) ;; label = @2 21 | i32.const 15 22 | else 23 | get_local 1 24 | end 25 | end) 26 | (export "_subject" (func 0))) 27 | -------------------------------------------------------------------------------- /src/decode/code.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum ExportDescriptionCode { 3 | ExportDescFunctionIdx, 4 | ExportDescTableIdx, 5 | ExportDescMemIdx, 6 | ExportDescGlobalIdx, 7 | } 8 | 9 | impl From> for ExportDescriptionCode { 10 | fn from(code: Option) -> Self { 11 | use self::ExportDescriptionCode::*; 12 | match code { 13 | Some(0x00) => ExportDescFunctionIdx, 14 | Some(0x01) => ExportDescTableIdx, 15 | Some(0x02) => ExportDescMemIdx, 16 | Some(0x03) => ExportDescGlobalIdx, 17 | x => unreachable!("Export description code {:x?} does not supported yet.", x), 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /discovery/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 9 | File::create(out.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out.display()); 14 | 15 | // Only re-run the build script when memory.x is changed, 16 | // instead of when any part of the source code changes. 17 | println!("cargo:rerun-if-changed=memory.x"); 18 | } 19 | -------------------------------------------------------------------------------- /src/decode/sec_custom.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Leb128Decodable, NameDecodable, Decodable, U32Decodable, U8Iterator}; 2 | use alloc::string::String; 3 | use alloc::vec::Vec; 4 | use error::Result; 5 | 6 | impl_decodable!(Section); 7 | impl Leb128Decodable for Section {} 8 | impl U32Decodable for Section {} 9 | impl NameDecodable for Section {} 10 | 11 | impl Decodable for Section { 12 | type Item = Vec<(String, Vec)>; 13 | 14 | fn decode(&mut self) -> Result { 15 | let key = self.decode_name()?; 16 | let mut codes = vec![]; 17 | while let Some(code) = self.next() { 18 | codes.push(code); 19 | } 20 | Ok(vec![(key, codes)]) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dist/count.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (func (;0;) (type 0) (param i32) (result i32) 4 | (local i32) 5 | get_local 0 6 | i32.const 0 7 | i32.le_s 8 | if ;; label = @1 9 | i32.const 0 10 | return 11 | end 12 | get_local 0 13 | i32.const -1 14 | i32.add 15 | tee_local 1 16 | get_local 0 17 | i32.const 1 18 | i32.add 19 | i32.mul 20 | get_local 0 21 | i32.add 22 | get_local 1 23 | i64.extend_u/i32 24 | get_local 0 25 | i32.const -2 26 | i32.add 27 | i64.extend_u/i32 28 | i64.mul 29 | i64.const 8589934591 30 | i64.and 31 | i64.const 1 32 | i64.shr_u 33 | i32.wrap/i64 34 | i32.add) 35 | (export "_subject" (func 0))) 36 | -------------------------------------------------------------------------------- /discovery/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasvm-discovery" 3 | version = "0.1.0" 4 | authors = ["Shinichi Kogai "] 5 | 6 | [dependencies] 7 | cortex-m = "0.5.8" 8 | cortex-m-rt = "0.6.7" 9 | cortex-m-semihosting = "0.3.2" 10 | panic-halt = "0.2.0" 11 | alloc-cortex-m = "0.3.5" 12 | wasvm = { version = "0.2.0", path = "../" } 13 | 14 | [[bin]] 15 | name = "discovery" 16 | path = "src/main.rs" 17 | test = false 18 | bench = false 19 | 20 | [profile.dev] 21 | opt-level = 'z' 22 | codegen-units = 1 # better optimizations 23 | debug = true # symbols are nice and they don't increase the size on Flash 24 | panic = 'abort' 25 | 26 | [profile.release] 27 | opt-level = 'z' 28 | codegen-units = 1 # better optimizations 29 | debug = true # symbols are nice and they don't increase the size on Flash 30 | lto = true # better optimizations 31 | panic = 'abort' 32 | -------------------------------------------------------------------------------- /report.txt: -------------------------------------------------------------------------------- 1 | # started on Sun Jan 13 20:41:49 2019 2 | 3 | 4 | Performance counter stats for './target/release/main dist/fib 35': 5 | 6 | 12,230.73 msec task-clock:u # 1.000 CPUs utilized 7 | 0 context-switches:u # 0.000 K/sec 8 | 0 cpu-migrations:u # 0.000 K/sec 9 | 236 page-faults:u # 19.297 M/sec 10 | 44,002,475,714 cycles:u # 3597912.977 GHz 11 | 140,675,513,342 instructions:u # 3.20 insn per cycle 12 | 29,143,327,893 branches:u # 2382937685.446 M/sec 13 | 46,504,664 branch-misses:u # 0.16% of all branches 14 | 15 | 12.232197004 seconds time elapsed 16 | 17 | 12.221386000 seconds user 18 | 0.003329000 seconds sys 19 | 20 | 21 | -------------------------------------------------------------------------------- /report.node.txt: -------------------------------------------------------------------------------- 1 | # started on Sun Jan 13 10:39:17 2019 2 | 3 | 4 | Performance counter stats for 'node run-wasm dist/fib _subject 35': 5 | 6 | 134.30 msec task-clock:u # 1.000 CPUs utilized 7 | 0 context-switches:u # 0.000 K/sec 8 | 0 cpu-migrations:u # 0.000 K/sec 9 | 2,521 page-faults:u # 18813.433 M/sec 10 | 365,292,379 cycles:u # 2726062.530 GHz 11 | 756,086,210 instructions:u # 2.07 insn per cycle 12 | 169,837,757 branches:u # 1267445947.761 M/sec 13 | 1,189,292 branch-misses:u # 0.70% of all branches 14 | 15 | 0.134339217 seconds time elapsed 16 | 17 | 0.121834000 seconds user 18 | 0.013115000 seconds sys 19 | 20 | 21 | -------------------------------------------------------------------------------- /discovery/openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # detect unhandled exceptions, hard faults and panics 7 | break DefaultHandler 8 | break UserHardFault 9 | break rust_begin_unwind 10 | 11 | # *try* to stop at the user entry point (it might be gone due to inlining) 12 | break main 13 | 14 | monitor arm semihosting enable 15 | 16 | # # send captured ITM to the file itm.fifo 17 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 18 | # # 8000000 must match the core clock frequency 19 | # monitor tpiu config internal itm.txt uart off 8000000 20 | 21 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 22 | # # 8000000 must match the core clock frequency 23 | # # 2000000 is the frequency of the SWO pin 24 | # monitor tpiu config external uart off 8000000 2000000 25 | 26 | # # enable ITM port 0 27 | # monitor itm port 0 on 28 | 29 | load 30 | 31 | # start the process but immediately halt the processor 32 | stepi 33 | -------------------------------------------------------------------------------- /src/decode/sec_export.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Decodable, Leb128Decodable, NameDecodable, U32Decodable, U8Iterator}; 2 | use alloc::vec::Vec; 3 | use error::Result; 4 | use module::{ExportDescriptor, ExternalInterface, ExternalInterfaces, ModuleDescriptor}; 5 | 6 | impl_decodable!(Section); 7 | impl Leb128Decodable for Section {} 8 | impl U32Decodable for Section {} 9 | impl NameDecodable for Section {} 10 | 11 | impl Decodable for Section { 12 | type Item = ExternalInterfaces; 13 | 14 | fn decode(&mut self) -> Result { 15 | let count_of_section = self.decode_leb128_u32()?; 16 | let mut exports: ExternalInterfaces = ExternalInterfaces::default(); 17 | for _ in 0..count_of_section { 18 | let name = self.decode_name()?; 19 | let export_descriptor = ExportDescriptor::from((self.next(), self.decode_leb128_u32()?)); 20 | exports.push(ExternalInterface::new( 21 | None, 22 | name, 23 | ModuleDescriptor::ExportDescriptor(export_descriptor), 24 | )); 25 | } 26 | Ok(exports) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/decode/sec_global.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{ 2 | Decodable, Leb128Decodable, Peekable, SignedIntegerDecodable, U32Decodable, U8Iterator, 3 | }; 4 | use super::instruction::InstructionDecodable; 5 | use alloc::vec::Vec; 6 | use error::Result; 7 | use global::GlobalType; 8 | use value_type::ValueTypes; 9 | 10 | impl_decodable!(Section); 11 | impl Peekable for Section {} 12 | impl Leb128Decodable for Section {} 13 | impl U32Decodable for Section {} 14 | impl SignedIntegerDecodable for Section {} 15 | impl InstructionDecodable for Section {} 16 | 17 | impl Decodable for Section { 18 | type Item = Vec<(GlobalType, Vec)>; 19 | fn decode(&mut self) -> Result { 20 | let count_of_section = self.decode_leb128_u32()?; 21 | (0..count_of_section) 22 | .map(|_| { 23 | let value_type = ValueTypes::from(self.next()?); 24 | let global_type = GlobalType::new(self.next(), value_type)?; 25 | let init = self.decode_instructions()?; 26 | Ok((global_type, init)) 27 | }) 28 | .collect::>>() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /run-wasm.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 }); 4 | const importObj = { 5 | env: { 6 | abortStackOverflow: () => { 7 | throw new Error("overflow"); 8 | }, 9 | table: new WebAssembly.Table({ 10 | initial: 0, 11 | maximum: 0, 12 | element: "anyfunc" 13 | }), 14 | tableBase: 0, 15 | memory: memory, 16 | memoryBase: 1024, 17 | STACKTOP: 0, 18 | STACK_MAX: memory.buffer.byteLength 19 | } 20 | }; 21 | 22 | if (process.argv.length < 3) { 23 | console.error("Error! Use like 'node run-wasm.js [name-of-wasm-file] [name-of-invoke-function] [...arguments]'"); 24 | process.exit(1); 25 | } 26 | const [_, __, name, invoke, ...args] = process.argv; 27 | const wasm = `./${name}.wasm`; 28 | const invoke_fn = `${invoke}`; 29 | console.log(`Invoke "${invoke_fn}" of "${wasm}" with "${args}"`); 30 | const buffer = fs.readFileSync(wasm); 31 | WebAssembly.instantiate(buffer, importObj) 32 | .then(mod => { 33 | console.log(mod.instance.exports[invoke_fn](...args)); 34 | }) 35 | .catch(console.error); 36 | -------------------------------------------------------------------------------- /src/decode/sec_table.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Leb128Decodable, LimitDecodable, Decodable, U32Decodable, U8Iterator}; 2 | use super::sec_element::ElementType; 3 | use alloc::vec::Vec; 4 | use error::Result; 5 | use memory::Limit; 6 | 7 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 8 | pub struct TableType { 9 | element_type: ElementType, 10 | pub(crate) limit: Limit, 11 | } 12 | 13 | impl TableType { 14 | pub fn new(element_type: ElementType, limit: Limit) -> Self { 15 | TableType { 16 | element_type, 17 | limit, 18 | } 19 | } 20 | } 21 | 22 | impl_decodable!(Section); 23 | impl Leb128Decodable for Section {} 24 | impl U32Decodable for Section {} 25 | impl LimitDecodable for Section {} 26 | 27 | impl Decodable for Section { 28 | type Item = Vec; 29 | fn decode(&mut self) -> Result { 30 | let count_of_section = self.decode_leb128_u32()?; 31 | (0..count_of_section) 32 | .map(|_| { 33 | let element_type = ElementType::from(self.next()); 34 | let limit = self.decode_limit()?; 35 | Ok(TableType::new(element_type, limit)) 36 | }) 37 | .collect::>>() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /bin/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | extern crate wasvm; 4 | 5 | use std::env::args; 6 | use std::fs; 7 | use std::io; 8 | use std::io::Read; 9 | use wasvm::{decode_module, init_store, instantiate_module, Values}; 10 | 11 | fn main() -> io::Result<()> { 12 | let arguments = args().collect::>(); 13 | let (_, arguments) = arguments.split_at(1); 14 | match arguments.split_first() { 15 | Some((file_name, arguments)) => { 16 | let mut file = fs::File::open(format!("./{}.wasm", file_name))?; 17 | let mut bytes = vec![]; 18 | file.read_to_end(&mut bytes)?; 19 | 20 | let store = init_store(); 21 | let module = decode_module(&bytes); 22 | let mut vm = instantiate_module(store, module, Default::default(), 65536).unwrap(); 23 | let result = vm.run( 24 | "_subject", 25 | arguments 26 | .iter() 27 | .map(|v| i32::from_str_radix(v, 10).expect("Parameters must be i32")) 28 | .map(Values::I32) 29 | .collect::>(), 30 | ); 31 | println!("{:?}", result); 32 | } 33 | _ => unreachable!("Should specify file-name"), 34 | }; 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /src/decode/sec_type.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Decodable, Leb128Decodable, U32Decodable, U8Iterator}; 2 | use alloc::vec::Vec; 3 | use core::convert::From; 4 | use error::Result; 5 | use function::FunctionType; 6 | use isa::Isa; 7 | use value_type::ValueTypes; 8 | 9 | impl_decodable!(Section); 10 | impl Leb128Decodable for Section {} 11 | impl U32Decodable for Section {} 12 | 13 | impl Decodable for Section { 14 | type Item = Vec; 15 | fn decode(&mut self) -> Result { 16 | let count_of_type = self.decode_leb128_u32()?; 17 | (0..count_of_type) 18 | .map(|_| { 19 | let mut parameters = vec![]; 20 | let mut returns = vec![]; 21 | let _type_function = Isa::from(self.next()?); 22 | let size_of_arity = self.decode_leb128_u32()?; 23 | for _ in 0..size_of_arity { 24 | parameters.push(ValueTypes::from(self.next()?)); 25 | } 26 | let size_of_result = self.decode_leb128_u32()?; 27 | for _ in 0..size_of_result { 28 | returns.push(ValueTypes::from(self.next()?)); 29 | } 30 | Ok(FunctionType::new(parameters, returns)) 31 | }) 32 | .collect::>>() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/value_type.rs: -------------------------------------------------------------------------------- 1 | use core::convert::From; 2 | use core::fmt; 3 | 4 | #[derive(PartialEq, Clone)] 5 | pub enum ValueTypes { 6 | Unit, 7 | I32, 8 | I64, 9 | F32, 10 | F64, 11 | } 12 | 13 | pub const TYPE_UNIT: ValueTypes = ValueTypes::Unit; 14 | pub const TYPE_I32: ValueTypes = ValueTypes::I32; 15 | pub const TYPE_I64: ValueTypes = ValueTypes::I64; 16 | pub const TYPE_F32: ValueTypes = ValueTypes::F32; 17 | pub const TYPE_F64: ValueTypes = ValueTypes::F64; 18 | 19 | impl From for ValueTypes { 20 | fn from(code: u8) -> Self { 21 | match code { 22 | 0x40 => ValueTypes::Unit, 23 | 0x7f => ValueTypes::I32, 24 | 0x7e => ValueTypes::I64, 25 | 0x7d => ValueTypes::F32, 26 | 0x7c => ValueTypes::F64, 27 | x => unreachable!("Expected value type, got {:?}", x), 28 | } 29 | } 30 | } 31 | 32 | impl fmt::Debug for ValueTypes { 33 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 34 | use self::ValueTypes::*; 35 | write!( 36 | f, 37 | "{}", 38 | match self { 39 | Unit => "()", 40 | I32 => "i32", 41 | I64 => "i64", 42 | F32 => "f32", 43 | F64 => "f64", 44 | } 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: 4 | - nightly-2018-11-23 5 | 6 | matrix: 7 | include: 8 | - env: TARGET=thumbv6m-none-eabi 9 | if: (type = push AND branch = master) 10 | - env: TARGET=thumbv7m-none-eabi 11 | if: (type = push AND branch = master) 12 | - env: TARGET=thumbv7em-none-eabi 13 | if: (type = push AND branch = master) 14 | - env: TARGET=thumbv7em-none-eabihf 15 | if: (type = push AND branch = master) 16 | - env: TARGET=x86_64-unknown-linux-gnu 17 | 18 | notifications: 19 | email: 20 | on_success: change 21 | on_failure: change 22 | git: 23 | submodules: false 24 | before_install: 25 | - ci/submodule.sh 26 | install: 27 | - ci/install.sh 28 | - rustup component add clippy-preview llvm-tools-preview 29 | script: 30 | - ci/script.sh 31 | env: 32 | global: 33 | secure: >- 34 | gH2hEJF33p7sb3YpdcZ6x52ts92qHkuw95crOyJOUgoBR/j8sj7CtL7LQCYdVgQpUBE8k1VVp1idUmVJQzEg3POI4CXp2OSmb9n/giqJ/7qQCcS6OYTmbGkPHkghqJsn8Eoylwk0TKy3jt/Un/h0F4pkKynVsa4gvM/CasWJ7PiNpFnR759QhztkJsS1/HUAUu/6ulEgAgcicEVi/lujNg82Nji+F5s6xDKxRC+EHMRMP7pQsZEAMRXjqjpXumwAm0FZu3g0/H+OXxLKf8H6xuIwWIkm9GTz+0u+A0sZ8+oaLLPiTmmc6nfPSiuDjQ6PGH0h4W78ckqT5vPiyYcepDofM4jGsnML9w8yO84i/F+eLEhQHecbiirnrRIIZS3QNbUmbQYM/a8fUx01MRWPVcWL+onPTx7itlNXua5na6nJUtFY19JW70YLvd51nz+sgApyIFumNrwor4L2Np37KfipT5GUH/9LvYBRH4hctSPH+yZss46et3vteBieMXHMdaZFzZgVtAoH4s9bWOTaWii1ioyMhgJjy87yQMWQ5UVJxlU6f2TiYSU1Lr4oMa1RxXavNhpqcuwwDrbElEYh2tCqppNjXXyPrfG3T43UEyiF9jePnE+COPkyIyu2cRn8hxjLjNFr41GBFNUuk/NoVhccLRGGvpWrqMuHwOeWa1E= 35 | -------------------------------------------------------------------------------- /discovery/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7m-none-eabi] 2 | # uncomment this to make `cargo run` execute programs on QEMU 3 | # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" 4 | 5 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 6 | # uncomment ONE of these three option to make `cargo run` start a GDB session 7 | # which option to pick depends on your system 8 | # runner = "arm-none-eabi-gdb -q -x openocd.gdb" 9 | # runner = "gdb-multiarch -q -x openocd.gdb" 10 | # runner = "gdb -q -x openocd.gdb" 11 | 12 | rustflags = [ 13 | # LLD (shipped with the Rust toolchain) is used as the default linker 14 | 15 | "-C", "link-arg=-Tlink.x", 16 | "-C", "link-arg=-error-limit=0", 17 | 18 | # if you run into problems with LLD switch to the GNU linker by commenting out 19 | # this line 20 | # "-C", "linker=arm-none-eabi-ld", 21 | 22 | # if you need to link to pre-compiled C libraries provided by a C toolchain 23 | # use GCC as the linker by commenting out both lines above and then 24 | # uncommenting the three lines below 25 | # "-C", "linker=arm-none-eabi-gcc", 26 | # "-C", "link-arg=-Wl,-Tlink.x", 27 | # "-C", "link-arg=-nostartfiles", 28 | ] 29 | 30 | [build] 31 | # Pick ONE of these compilation targets 32 | # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 33 | # target = "thumbv7m-none-eabi" # Cortex-M3 34 | # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 35 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 36 | -------------------------------------------------------------------------------- /src/decode/sec_data.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{ 2 | Leb128Decodable, Decodable, Peekable, SignedIntegerDecodable, U32Decodable, U8Iterator, 3 | }; 4 | use super::instruction::InstructionDecodable; 5 | use alloc::vec::Vec; 6 | use error::Result; 7 | 8 | #[derive(Debug)] 9 | pub struct Data { 10 | pub memidx: u32, 11 | pub offset: Vec, 12 | pub init: Vec, 13 | } 14 | 15 | impl Data { 16 | pub fn new(memidx: u32, offset: Vec, init: Vec) -> Self { 17 | Data { 18 | memidx, 19 | offset, 20 | init, 21 | } 22 | } 23 | pub fn get_data_idx(&self) -> u32 { 24 | self.memidx 25 | } 26 | pub fn get_init(self) -> Vec { 27 | self.init 28 | } 29 | } 30 | 31 | impl_decodable!(Section); 32 | impl Peekable for Section {} 33 | impl Leb128Decodable for Section {} 34 | impl U32Decodable for Section {} 35 | impl SignedIntegerDecodable for Section {} 36 | impl InstructionDecodable for Section {} 37 | 38 | impl Decodable for Section { 39 | type Item = Vec; 40 | fn decode(&mut self) -> Result { 41 | let count_of_section = self.decode_leb128_u32()?; 42 | (0..count_of_section) 43 | .map(|_| { 44 | let memidx = self.decode_leb128_u32()?; 45 | let offset = self.decode_instructions()?; 46 | let size_of_data = self.decode_leb128_u32()?; 47 | let mut init = vec![]; 48 | for _ in 0..size_of_data { 49 | init.push(self.next()?); 50 | } 51 | Ok(Data::new(memidx, offset, init)) 52 | }) 53 | .collect::>>() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /discovery/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ 4 | /* TODO Adjust these memory regions to match your device memory layout */ 5 | /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ 6 | /* FLASH : ORIGIN = 0x00000000, LENGTH = 256K */ 7 | /* RAM : ORIGIN = 0x20000000, LENGTH = 64K */ 8 | 9 | /* Linker script for the STM32F303VCT6 */ 10 | FLASH : ORIGIN = 0x08000000, LENGTH = 256K 11 | RAM : ORIGIN = 0x20000000, LENGTH = 40K 12 | } 13 | 14 | /* This is where the call stack will be allocated. */ 15 | /* The stack is of the full descending type. */ 16 | /* You may want to use this variable to locate the call stack and static 17 | variables in different memory regions. Below is shown the default value */ 18 | /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ 19 | 20 | /* You can use this symbol to customize the location of the .text section */ 21 | /* If omitted the .text section will be placed right after the .vector_table 22 | section */ 23 | /* This is required only on microcontrollers that store some configuration right 24 | after the vector table */ 25 | /* _stext = ORIGIN(FLASH) + 0x400; */ 26 | 27 | /* Example of putting non-initialized variables into custom RAM locations. */ 28 | /* This assumes you have defined a region RAM2 above, and in the Rust 29 | sources added the attribute `#[link_section = ".ram2bss"]` to the data 30 | you want to place there. */ 31 | /* Note that the section will not be zero-initialized by the runtime! */ 32 | /* SECTIONS { 33 | .ram2bss (NOLOAD) : ALIGN(4) { 34 | *(.ram2bss); 35 | . = ALIGN(4); 36 | } > RAM2 37 | } INSERT AFTER .bss; 38 | */ 39 | -------------------------------------------------------------------------------- /src/decode/sec_code.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{ 2 | Decodable, Leb128Decodable, Peekable, SignedIntegerDecodable, U32Decodable, U8Iterator, 3 | }; 4 | use super::instruction::InstructionDecodable; 5 | use alloc::vec::Vec; 6 | use core::convert::From; 7 | use error::Result; 8 | use value_type::ValueTypes; 9 | 10 | impl_decodable!(Section); 11 | 12 | impl Peekable for Section {} 13 | impl Leb128Decodable for Section {} 14 | impl U32Decodable for Section {} 15 | impl SignedIntegerDecodable for Section {} 16 | impl InstructionDecodable for Section {} 17 | 18 | impl Decodable for Section { 19 | // FIXME: 20 | type Item = Vec, Vec)>>; 21 | fn decode(&mut self) -> Result { 22 | let count_of_section = self.decode_leb128_u32()?; 23 | (0..count_of_section) 24 | .map(|_| { 25 | let size_of_function = self.decode_leb128_u32()?; 26 | let end_of_function = self.byte_ptr + (size_of_function as usize); 27 | let count_of_locals = self.decode_leb128_u32()? as usize; 28 | let mut locals: Vec = Vec::with_capacity(count_of_locals); 29 | for _ in 0..count_of_locals { 30 | let count_of_type = self.decode_leb128_u32()?; 31 | let value_type = ValueTypes::from(self.next()?); 32 | for _ in 0..count_of_type { 33 | locals.push(value_type.clone()); 34 | } 35 | } 36 | Ok(match self.decode_instructions() { 37 | Ok(expressions) => Ok((expressions, locals)), 38 | Err(err) => { 39 | self.byte_ptr = end_of_function; 40 | Err(err) 41 | } 42 | }) 43 | }) 44 | .collect::>>() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/decode/sec_import.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{ 2 | Leb128Decodable, LimitDecodable, NameDecodable, Decodable, U32Decodable, U8Iterator, 3 | }; 4 | use super::sec_element::ElementType; 5 | use super::sec_table::TableType; 6 | use alloc::vec::Vec; 7 | use error::Result; 8 | use global::GlobalType; 9 | use module::{ 10 | ExternalInterface, ExternalInterfaces, ImportDescriptor, ModuleDescriptor, FUNCTION_DESCRIPTOR, 11 | GLOBAL_DESCRIPTOR, MEMORY_DESCRIPTOR, TABLE_DESCRIPTOR, 12 | }; 13 | use value_type::ValueTypes; 14 | 15 | impl_decodable!(Section); 16 | impl Leb128Decodable for Section {} 17 | impl U32Decodable for Section {} 18 | impl LimitDecodable for Section {} 19 | impl NameDecodable for Section {} 20 | 21 | impl Decodable for Section { 22 | type Item = ExternalInterfaces; 23 | fn decode(&mut self) -> Result { 24 | let count_of_section = self.decode_leb128_u32()?; 25 | let mut imports: ExternalInterfaces = ExternalInterfaces::default(); 26 | for _ in 0..count_of_section { 27 | let module_name = self.decode_name()?; 28 | let name = self.decode_name()?; 29 | let import_descriptor = match From::from(self.next()) { 30 | FUNCTION_DESCRIPTOR => ImportDescriptor::Function(From::from(self.decode_leb128_u32()?)), 31 | TABLE_DESCRIPTOR => ImportDescriptor::Table(TableType::new( 32 | ElementType::from(self.next()), 33 | self.decode_limit()?, 34 | )), 35 | MEMORY_DESCRIPTOR => ImportDescriptor::Memory(self.decode_limit()?), 36 | GLOBAL_DESCRIPTOR => { 37 | let value_type = ValueTypes::from(self.next()?); 38 | let global_type = GlobalType::new(self.next(), value_type)?; 39 | ImportDescriptor::Global(global_type) 40 | } 41 | }; 42 | imports.push(ExternalInterface::new( 43 | Some(module_name), 44 | name, 45 | ModuleDescriptor::ImportDescriptor(import_descriptor), 46 | )); 47 | } 48 | Ok(imports) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /benches/run.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_range_loop)] 2 | #![feature(test)] 3 | extern crate flame; 4 | extern crate test; 5 | extern crate wasvm; 6 | 7 | use std::fs; 8 | use std::io::Read; 9 | use wasvm::{decode_module, init_store, instantiate_module, Values}; 10 | 11 | #[derive(Clone)] 12 | struct Sample(usize, usize); 13 | 14 | // NOTE: Compare which one is fast vec.push(el) or vec[idx] = el; 15 | // And below is the result. 16 | // test bench_assign ... bench: 59 ns/iter (+/- 1) 17 | // test bench_push ... bench: 624 ns/iter (+/- 5) 18 | 19 | #[bench] 20 | fn bench_assign(b: &mut test::Bencher) { 21 | let mut buf: Vec = vec![Sample(0, 0); 100]; 22 | b.iter(|| { 23 | for i in 0..100 { 24 | buf[i] = Sample(i, i); 25 | } 26 | }); 27 | } 28 | 29 | #[bench] 30 | fn bench_push(b: &mut test::Bencher) { 31 | let mut buf = Vec::with_capacity(100); 32 | b.iter(|| { 33 | for i in 0..100 { 34 | buf.push(Sample(i, i)); 35 | } 36 | }); 37 | } 38 | 39 | macro_rules! impl_benches { 40 | ($test_name: ident, $bench_name: expr, $expect: expr) => { 41 | #[bench] 42 | fn $test_name(_b: &mut test::Bencher) { 43 | let mut file = fs::File::open(format!("./tmp/{}.wasm", $bench_name)).unwrap(); 44 | let mut bytes = vec![]; 45 | file.read_to_end(&mut bytes).unwrap(); 46 | flame::start($bench_name); 47 | // b.iter(|| { 48 | let store = init_store(); 49 | let module = decode_module(&bytes); 50 | let mut vm = instantiate_module(store, module, Default::default(), 65536).unwrap(); 51 | assert_eq!(vm.run("app_main", vec![]).unwrap(), $expect); 52 | // }); 53 | flame::end($bench_name); 54 | flame::dump_stdout(); 55 | } 56 | }; 57 | } 58 | 59 | impl_benches!(bench_fib_recursive, "fib_recursive", Values::I32(922_7465)); 60 | impl_benches!( 61 | bench_pollard_rho_128, 62 | "pollard_rho_128", 63 | Values::I64(263_5722_1265_1198_9555) 64 | ); 65 | impl_benches!( 66 | bench_snappy_compress, 67 | "snappy_compress", 68 | Values::I32(39_3476) 69 | ); 70 | -------------------------------------------------------------------------------- /src/store.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::default::Default; 3 | use error::Result; 4 | use function::{FunctionInstance, FunctionType}; 5 | use global::GlobalInstances; 6 | use indice::Indice; 7 | use memory::MemoryInstances; 8 | use table::{TableInstance, TableInstances}; 9 | use value::Values; 10 | 11 | #[derive(Debug)] 12 | pub struct Store { 13 | pub function_instances: Vec, 14 | pub function_types: Vec, 15 | pub memory_instances: MemoryInstances, 16 | pub table_instances: TableInstances, 17 | pub global_instances: GlobalInstances, 18 | } 19 | 20 | impl Store { 21 | pub fn new( 22 | function_instances: Vec, 23 | function_types: Vec, 24 | memory_instances: MemoryInstances, 25 | table_instances: TableInstances, 26 | global_instances: GlobalInstances, 27 | ) -> Self { 28 | Store { 29 | function_instances, 30 | function_types, 31 | memory_instances, 32 | table_instances, 33 | global_instances, 34 | } 35 | } 36 | 37 | pub fn get_function_instance(&self, fn_idx: &Indice) -> Option { 38 | self.function_instances.get(fn_idx.to_usize()).cloned() 39 | } 40 | 41 | pub fn get_function_type(&self, idx: &Indice) -> Option<&FunctionType> { 42 | self.function_types.get(idx.to_usize()) 43 | } 44 | 45 | pub fn get_function_type_by_instance(&self, idx: &Indice) -> Option { 46 | self 47 | .get_function_instance(idx) 48 | .map(|x| x.get_function_type()) 49 | } 50 | 51 | pub fn get_global(&self, idx: &Indice) -> Result { 52 | self.global_instances.get_global(idx) 53 | } 54 | 55 | pub fn set_global(&mut self, idx: &Indice, value: Values) { 56 | self.global_instances.set_global(idx, value) 57 | } 58 | 59 | pub fn get_table_at(&self, idx: &Indice) -> Option { 60 | self.table_instances.get_table_at(idx) 61 | } 62 | } 63 | 64 | impl Default for Store { 65 | fn default() -> Self { 66 | Store { 67 | function_instances: Vec::new(), 68 | function_types: Vec::new(), 69 | memory_instances: MemoryInstances::empty(), 70 | table_instances: TableInstances::empty(), 71 | global_instances: GlobalInstances::empty(), 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /discovery/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc)] 4 | #![feature(alloc_error_handler)] 5 | #![feature(custom_attribute)] 6 | #![feature(core_intrinsics)] 7 | 8 | extern crate alloc; 9 | extern crate alloc_cortex_m; 10 | extern crate cortex_m; 11 | extern crate cortex_m_rt; 12 | extern crate cortex_m_semihosting; 13 | extern crate panic_halt; 14 | extern crate wasvm; 15 | 16 | use alloc::alloc::Layout; 17 | use alloc::prelude::*; 18 | use alloc_cortex_m::CortexMHeap; 19 | use cortex_m::asm; 20 | use cortex_m_rt::{entry, heap_start}; 21 | use cortex_m_semihosting::hprintln; 22 | use wasvm::{ 23 | decode_module, init_store, instantiate_module, ExternalModule, ExternalModules, 24 | FunctionInstance, FunctionType, ValueTypes, Values, 25 | }; 26 | 27 | #[global_allocator] 28 | static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); 29 | 30 | fn my_hal_function(_arguments: &[Values]) -> alloc::vec::Vec { 31 | [Values::I32(3 * 5)].to_vec() 32 | } 33 | 34 | #[entry] 35 | fn main() -> ! { 36 | let start = heap_start() as usize; 37 | let size = 65536; 38 | unsafe { ALLOCATOR.init(start, size) } 39 | 40 | hprintln!("Start...").unwrap(); 41 | 42 | let bytes = include_bytes!("discovery_wasm_bg.wasm"); 43 | let store = init_store(); 44 | let section = decode_module(bytes); 45 | let mut external_modules = ExternalModules::default(); 46 | let external_module = ExternalModule::new( 47 | [FunctionInstance::new_host_fn( 48 | Some("__wbg_myhalfunction_59a89d8df8955cf7".to_owned()), 49 | FunctionType::new( 50 | [ValueTypes::I32, ValueTypes::I32].to_vec(), 51 | [ValueTypes::I32].to_vec(), 52 | ), 53 | &my_hal_function, 54 | )] 55 | .to_vec(), 56 | [].to_vec(), 57 | [].to_vec(), 58 | [].to_vec(), 59 | [].to_vec(), 60 | ); 61 | external_modules.register_module(Some("./discovery_wasm".to_owned()), external_module).unwrap(); 62 | // FIXME: Causes OOM. 63 | let instance = instantiate_module(store, section, external_modules, 128); 64 | let mut vm = instance.unwrap(); 65 | let result = vm.run( 66 | "use_hal_function", 67 | [Values::I32(3), Values::I32(5)].to_vec(), 68 | ); 69 | 70 | // Need to enable semihosting of gdb 71 | hprintln!("Result={:?}", result).unwrap(); 72 | 73 | loop {} 74 | } 75 | 76 | #[alloc_error_handler] 77 | fn on_oom(_layout: Layout) -> ! { 78 | asm::bkpt(); 79 | 80 | loop {} 81 | } 82 | -------------------------------------------------------------------------------- /src/decode/sec_element.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{ 2 | Decodable, Leb128Decodable, Peekable, SignedIntegerDecodable, U32Decodable, 3 | }; 4 | use super::instruction::InstructionDecodable; 5 | use alloc::vec::Vec; 6 | use error::Result; 7 | use function::FunctionInstance; 8 | use indice::Indice; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct Element { 12 | pub(crate) table_idx: Indice, 13 | pub(crate) offset: Vec, 14 | pub(crate) init: Vec, 15 | } 16 | 17 | impl Element { 18 | pub fn new(table_idx: Indice, offset: Vec, init: Vec) -> Self { 19 | Element { 20 | table_idx, 21 | offset, 22 | init, 23 | } 24 | } 25 | 26 | pub fn get_table_idx(&self) -> &Indice { 27 | &self.table_idx 28 | } 29 | 30 | pub fn move_init_to(&self) -> Vec { 31 | self.init.clone() 32 | } 33 | 34 | pub(crate) fn wrap_by_option( 35 | &self, 36 | function_instances: &[FunctionInstance], 37 | ) -> Vec> { 38 | self 39 | .init 40 | .iter() 41 | .map(|fn_idx| function_instances.get(fn_idx.to_usize()).cloned()) 42 | .collect() 43 | } 44 | } 45 | 46 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 47 | pub enum ElementType { 48 | AnyFunc, 49 | } 50 | 51 | impl From> for ElementType { 52 | fn from(code: Option) -> Self { 53 | match code { 54 | Some(0x70) => ElementType::AnyFunc, 55 | x => unreachable!("Expected element-type code, got {:?}", x), 56 | } 57 | } 58 | } 59 | 60 | impl_decodable!(Section); 61 | impl Peekable for Section {} 62 | impl Leb128Decodable for Section {} 63 | impl U32Decodable for Section {} 64 | impl SignedIntegerDecodable for Section {} 65 | impl InstructionDecodable for Section {} 66 | 67 | impl Section { 68 | fn decode_function_idx(&mut self) -> Result> { 69 | let count = self.decode_leb128_u32()?; 70 | let mut buf = vec![]; 71 | for _ in 0..count { 72 | buf.push(Indice::from(self.decode_leb128_u32()?)); 73 | } 74 | Ok(buf) 75 | } 76 | } 77 | 78 | impl Decodable for Section { 79 | type Item = Vec; 80 | fn decode(&mut self) -> Result { 81 | let count_of_section = self.decode_leb128_u32()?; 82 | (0..count_of_section) 83 | .map(|_| { 84 | let table_idx = self.decode_leb128_u32()?; 85 | let offset = self.decode_instructions()?; 86 | let init = self.decode_function_idx()?; 87 | Ok(Element::new(From::from(table_idx), offset, init)) 88 | }) 89 | .collect::>>() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/embedder.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use decode::{Byte, Module}; 4 | use error::Result; 5 | use frame::Frame; 6 | use module::ExternalModules; 7 | use stack::Stack; 8 | use store::Store; 9 | use validate::Context; 10 | use vm::ModuleInstance; 11 | 12 | pub fn init_store() -> Store { 13 | Default::default() 14 | } 15 | 16 | pub fn decode_module(bytes: &[u8]) -> Result { 17 | Byte::new_with_drop(&bytes)?.decode() 18 | } 19 | 20 | pub fn validate_module(module: &Result) -> Result<()> { 21 | match module { 22 | Ok(module) => Context::new(module)?.validate(), 23 | Err(err) => Err(err.to_owned()), 24 | } 25 | } 26 | 27 | pub fn instantiate_module( 28 | mut store: Store, 29 | section: Result, // module: Module(PreVm) 30 | external_modules: ExternalModules, 31 | max_stack_height: usize, 32 | ) -> Result { 33 | // TODO: Return pair of (Store, Vm) by using Rc type. 34 | let internal_module = section?.complete(&external_modules, &mut store)?; 35 | let mut vm = 36 | ModuleInstance::new_from(store, internal_module, external_modules, max_stack_height)?; 37 | if let Some(idx) = vm.start_index().clone() { 38 | let function_instance = vm.get_function_instance(&idx)?; 39 | let frame = Frame::new( 40 | vm.stack.stack_ptr(), 41 | vm.stack.frame_ptr(), 42 | function_instance, 43 | &mut vec![], 44 | ); 45 | vm.stack.push_frame(frame)?; 46 | vm.evaluate()?; 47 | vm.stack = Stack::new(max_stack_height); 48 | }; 49 | Ok(vm) 50 | } 51 | 52 | // module_imports(module):(name,name,externtype)∗¶ 53 | // module_exports(module):(name,externtype)∗¶ 54 | // get_export(moduleinst,name):externval | error¶ 55 | 56 | // Function 57 | // alloc_func(store,functype,hostfunc):(store,funcaddr)¶ 58 | // type_func(store,funcaddr):functype¶ 59 | // invoke_func(store,funcaddr,val∗):(store,val∗ | error)¶ 60 | 61 | // Table 62 | // alloc_table(store,tabletype):(store,tableaddr)¶ 63 | // type_table(store,tableaddr):tabletype¶ 64 | // read_table(store,tableaddr,i):funcaddr? | error¶ 65 | // write_table(store,tableaddr,i,funcaddr?):store | error¶ 66 | // size_table(store,tableaddr):i32¶ 67 | // grow_table(store,tableaddr,n):store | error¶ 68 | 69 | // Memory 70 | // alloc_mem(store,memtype):(store,memaddr) 71 | // type_mem(store,memaddr):memtype¶ 72 | // read_mem(store,memaddr,i):byte | error 73 | // write_mem(store,memaddr,i,byte):store | error 74 | // size_mem(store,memaddr):i32 75 | // grow_mem(store,memaddr,n):store | error 76 | 77 | // Global 78 | // alloc_global(store,globaltype,val):(store,globaladdr) 79 | // type_global(store,globaladdr):globaltype 80 | // read_global(store,globaladdr):val 81 | // write_global(store,globaladdr,val):store | error 82 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use core::convert::From; 2 | use core::option::NoneError; 3 | 4 | #[derive(Debug, Clone, PartialEq)] 5 | pub enum Trap { 6 | DivisionOverflow, 7 | DivisionByZero, 8 | DataSegmentDoesNotFit, 9 | ElementSegmentDoesNotFit, 10 | MemoryAccessOutOfBounds, 11 | BitshiftOverflow, 12 | IntegerOverflow, 13 | InvalidConversionToInt, 14 | InvalidMutability, 15 | Unknown, 16 | StackOverflow, 17 | StackUnderflow, 18 | Notfound, 19 | Undefined, 20 | UndefinedElement, 21 | TypeMismatch, 22 | IndirectCallTypeMismatch, 23 | FailToGrow, 24 | UnexpectedEnd, 25 | InvalidSectionId, 26 | LengthOutofBounds, 27 | Unreachable, 28 | UnknownImport, 29 | UninitializedElement, 30 | IncompatibleImportType, 31 | MagicHeaderNotDetected, 32 | UnsupportedTextform, 33 | IntegerRepresentationTooLong, 34 | FunctionAndCodeInconsitent, 35 | InvalidUTF8Encoding, 36 | LinearMapOverflowed, 37 | } 38 | 39 | #[derive(Debug, Clone, PartialEq)] 40 | pub enum TypeError { 41 | NotFound, 42 | MultipleTables, 43 | MultipleMemories, 44 | TypeMismatch, 45 | IndirectCallTypeMismatch, 46 | IncompatibleImportType, 47 | InvalidResultArity, 48 | InvalidAlignment, 49 | InvalidMemorySize, 50 | InvalidStartFunction, 51 | UnknownLabel, 52 | UnknownLocal, 53 | UnknownMemory, 54 | UnknownFunctionType(u32), 55 | UnknownFunction(u32), 56 | UnknownTable(u32), 57 | UnknownGlobal(u32), 58 | ConstantExpressionRequired, 59 | DuplicateExportName, 60 | GlobalIsImmutable, 61 | } 62 | 63 | #[derive(Debug, Clone, PartialEq)] 64 | pub enum WasmError { 65 | Trap(Trap), 66 | TypeError(TypeError), 67 | } 68 | 69 | impl From for NoneError { 70 | fn from(_: WasmError) -> Self { 71 | NoneError 72 | } 73 | } 74 | 75 | impl From for WasmError { 76 | fn from(_: NoneError) -> Self { 77 | WasmError::Trap(Trap::Notfound) 78 | } 79 | } 80 | 81 | impl From for self::Trap { 82 | fn from(wasm_error: WasmError) -> Self { 83 | match wasm_error { 84 | WasmError::Trap(e) => e, 85 | _ => unreachable!(), 86 | } 87 | } 88 | } 89 | 90 | impl From for WasmError { 91 | fn from(trap: Trap) -> Self { 92 | WasmError::Trap(trap) 93 | } 94 | } 95 | 96 | impl From for WasmError { 97 | fn from(type_error: TypeError) -> Self { 98 | WasmError::TypeError(type_error) 99 | } 100 | } 101 | 102 | impl From for TypeError { 103 | fn from(wasm_error: WasmError) -> Self { 104 | match wasm_error { 105 | WasmError::TypeError(e) => e, 106 | _ => unreachable!(), 107 | } 108 | } 109 | } 110 | 111 | pub type Result = core::result::Result; 112 | -------------------------------------------------------------------------------- /src/spectest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use decode::{ElementType, TableType}; 4 | use function::{FunctionInstance, FunctionType}; 5 | use global::{GlobalInstance, GlobalInstances, GlobalType}; 6 | use memory::{Limit, MemoryInstance}; 7 | use module::ExternalModule; 8 | use table::TableInstance; 9 | use value::Values; 10 | use value_type::{TYPE_F32, TYPE_F64, TYPE_I32}; 11 | 12 | fn host_function(_values: &[Values]) -> Vec { 13 | vec![] 14 | } 15 | 16 | pub fn create_spectest() -> ExternalModule { 17 | ExternalModule::new( 18 | vec![ 19 | FunctionInstance::new_host_fn( 20 | Some("print".to_owned()), 21 | FunctionType::new(vec![], vec![]), 22 | &host_function, 23 | ), 24 | // 4 25 | FunctionInstance::new_host_fn( 26 | Some("print_i32".to_owned()), 27 | FunctionType::new(vec![TYPE_I32], vec![]), 28 | &host_function, 29 | ), 30 | // 5 31 | FunctionInstance::new_host_fn( 32 | Some("print_i32_f32".to_owned()), 33 | FunctionType::new(vec![TYPE_I32, TYPE_F32], vec![]), 34 | &host_function, 35 | ), 36 | // 6 37 | FunctionInstance::new_host_fn( 38 | Some("print_f64_f64".to_owned()), 39 | FunctionType::new(vec![TYPE_F64, TYPE_F64], vec![]), 40 | &host_function, 41 | ), 42 | // 2 43 | FunctionInstance::new_host_fn( 44 | Some("print_f32".to_owned()), 45 | FunctionType::new(vec![TYPE_F32], vec![]), 46 | &host_function, 47 | ), 48 | // 3 49 | FunctionInstance::new_host_fn( 50 | Some("print_f64".to_owned()), 51 | FunctionType::new(vec![TYPE_F64], vec![]), 52 | &host_function, 53 | ), 54 | ], 55 | vec![], 56 | // MemoryInstances 57 | vec![MemoryInstance::new( 58 | vec![], 59 | Limit::HasUpperLimit(1, 2), 60 | Some("memory".to_owned()), 61 | &GlobalInstances::empty(), 62 | ) 63 | .unwrap()], 64 | // TableInstances 65 | vec![TableInstance::new( 66 | vec![], 67 | TableType::new(ElementType::AnyFunc, Limit::HasUpperLimit(10, 20)), 68 | Some("table".to_owned()), 69 | &GlobalInstances::empty(), 70 | &[], 71 | ) 72 | .unwrap()], 73 | // GlobalInstances 74 | vec![ 75 | GlobalInstance::new( 76 | GlobalType::Const(TYPE_I32), 77 | Values::I32(666), 78 | Some("global_i32".to_owned()), 79 | ), 80 | GlobalInstance::new( 81 | GlobalType::Const(TYPE_F32), 82 | Values::F32(666.6), 83 | Some("global_f32".to_owned()), 84 | ), 85 | GlobalInstance::new( 86 | GlobalType::Const(TYPE_F64), 87 | Values::F64(666.6), 88 | Some("global_f64".to_owned()), 89 | ), 90 | ], 91 | ) 92 | } 93 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC := $(wildcard **/*.rs) 2 | TRIPLE := wasm32-unknown-unknown 3 | CSRCS=$(wildcard ./fixtures/*.c) 4 | WASTS=$(filter-out "./testsuite/binary.json", $(wildcard ./testsuite/*.wast)) 5 | BENCH_DIR := life/bench/cases 6 | C_WASMS=$(CSRCS:.c=.wasm) 7 | WASMS=$(WASTS:.wast=.wasm) 8 | TARGET := thumbv7em-none-eabihf 9 | ARM_GDB := arm-none-eabi-gdb 10 | DISCOVERY := discovery 11 | 12 | all: $(C_WASMS) 13 | dist: $(C_WASMS) 14 | 15 | discovery/debug: discovery/target/$(TARGET)/debug/$(DISCOVERY) 16 | discovery/release: discovery/target/$(TARGET)/release/$(DISCOVERY) 17 | discovery: discovery/debug discovery/release 18 | 19 | $(C_WASMS): $(CSRCS) 20 | emcc -O3 -g0 fixtures/$(shell basename $@ .wasm).c -s "EXPORTED_FUNCTIONS=['_subject', '_f', '_g']" -s WASM=1 -o $(shell basename $@ .wasm).js 21 | wasm-gc $(shell basename $@) -o dist/$(shell basename $@) 22 | wasm2wat dist/$(shell basename $@) -o dist/$(shell basename $@ .wasm).wat 23 | rm ./$(shell basename $@ .wasm).* 24 | 25 | discovery/target/$(TARGET)/debug/$(DISCOVERY): $(SRC) 26 | cd discovery && \ 27 | cargo build 28 | 29 | discovery/target/$(TARGET)/release/$(DISCOVERY): $(SRC) 30 | cd discovery && \ 31 | cargo build --release 32 | 33 | .PHONY: openocd gdb/debug gdb/release 34 | openocd: 35 | openocd -f discovery/openocd.cfg 36 | 37 | gdb/debug: discovery/target/$(TARGET)/debug/$(DISCOVERY) 38 | $(ARM_GDB) -x discovery/openocd.gdb discovery/target/$(TARGET)/debug/$(DISCOVERY) 39 | 40 | gdb/release: discovery/target/$(TARGET)/release/$(DISCOVERY) 41 | $(ARM_GDB) -x discovery/openocd.gdb discovery/target/$(TARGET)/release/$(DISCOVERY) 42 | 43 | discovery/src/discovery_wasm_bg.wasm: $(SRC) 44 | cd discovery-wasm && \ 45 | wasm-pack build 46 | cp discovery-wasm/pkg/discovery_wasm_bg.wasm discovery/src/discovery_wasm_bg.wasm 47 | 48 | target/release/main: $(SRC) Makefile 49 | RUSTFLAGS='-g' cargo build --release 50 | 51 | .PHONY: report.txt 52 | report.txt: target/release/main Makefile 53 | perf stat -o report.txt ./target/release/main dist/fib 35 54 | 55 | .PHONY: out.perf 56 | out.perf: target/release/main Makefile 57 | perf record --call-graph=lbr ./target/release/main dist/fib 35 58 | # perf record -g -- node run-wasm.js dist/fib subject 35 59 | perf script > out.perf 60 | # perf report -g fractal --sort dso,comm 61 | # perf report -n --stdio 62 | 63 | out.svg: out.perf 64 | ./FlameGraph/stackcollapse-perf.pl out.perf > out.perf_folded 65 | ./FlameGraph/flamegraph.pl out.perf_folded > out.svg 66 | 67 | report.node.txt: Makefile 68 | perf stat -o report.node.txt node run-wasm dist/fib _subject 35 69 | 70 | .PHONY: run 71 | run: 72 | cargo run --bin main 73 | 74 | .PHONY: benches 75 | benches: tmp/fib_recursive.wasm tmp/pollard_rho_128.wasm tmp/snappy_compress.wasm 76 | 77 | tmp/fib_recursive.wasm: 78 | cargo build --release --target=$(TRIPLE) --manifest-path=$(BENCH_DIR)/fib_recursive/Cargo.toml 79 | mv $(BENCH_DIR)/fib_recursive/target/$(TRIPLE)/release/fib_recursive.wasm tmp/ 80 | 81 | tmp/pollard_rho_128.wasm: 82 | cargo build --release --target=$(TRIPLE) --manifest-path=$(BENCH_DIR)/pollard_rho_128/Cargo.toml 83 | mv $(BENCH_DIR)/pollard_rho_128/target/$(TRIPLE)/release/pollard_rho_128.wasm tmp/ 84 | 85 | tmp/snappy_compress.wasm: 86 | cargo build --release --target=$(TRIPLE) --manifest-path=$(BENCH_DIR)/snappy_compress/Cargo.toml 87 | mv $(BENCH_DIR)/snappy_compress/target/$(TRIPLE)/release/snappy_compress.wasm tmp/ 88 | 89 | # Prefer to replace Docker container 90 | install: 91 | packer -S wabt --noconfirm 92 | cargo install wasm-gc cargo-binutils cargo-bloat 93 | rustup target add \ 94 | thumbv6m-none-eabi\ 95 | thumbv7m-none-eabi\ 96 | thumbv7em-none-eabi\ 97 | thumbv7em-none-eabihf\ 98 | wasm32-unknown-emscripten 99 | stup component add llvm-tools-preview 100 | sudo pacman -S \ 101 | arm-none-eabi-gdb \ 102 | qemu-arch-extra 103 | -------------------------------------------------------------------------------- /src/frame.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::cell::{Cell, RefCell, RefMut}; 3 | use core::fmt; 4 | use core::ops::Sub; 5 | use error::Result; 6 | use function::FunctionInstance; 7 | use indice::Indice; 8 | use stack::StackEntry; 9 | use value_type::ValueTypes; 10 | 11 | macro_rules! impl_pop_bytes { 12 | ($name: ident, $ty: ty, $width: expr) => { 13 | pub(crate) fn $name(&self) -> Result<$ty> { 14 | let mut buf = [0; $width]; 15 | let body = match self.function_instance { 16 | FunctionInstance::LocalFn(ref f) => f.body(), 17 | _ => unreachable!() 18 | }; 19 | let start = self.ptr.get() as usize; 20 | let end = start + $width; 21 | buf.clone_from_slice(&body[start..end]); 22 | self.ptr.set((start + $width) as u32); 23 | Ok(unsafe { core::mem::transmute::<_, $ty>(buf) }) 24 | } 25 | }; 26 | } 27 | 28 | #[derive(PartialEq)] 29 | pub struct Frame { 30 | // FIXME: No need to hold local_variables in frame. 31 | local_variables: RefCell>, 32 | pub(crate) function_instance: FunctionInstance, 33 | ptr: Cell, 34 | pub last_ptr: u32, 35 | pub return_ptr: usize, 36 | pub prev_return_ptr: usize, 37 | } 38 | 39 | impl Frame { 40 | impl_pop_bytes!(pop_raw_u32, u32, 4); 41 | impl_pop_bytes!(pop_raw_u64, u64, 8); 42 | 43 | pub fn new( 44 | return_ptr: usize, 45 | prev_return_ptr: usize, 46 | function_instance: FunctionInstance, 47 | arguments: &mut Vec, 48 | ) -> Self { 49 | match function_instance { 50 | FunctionInstance::LocalFn(ref f) => { 51 | let last_ptr = f.get_expressions_count() as u32; 52 | Frame { 53 | local_variables: Frame::derive_local_variables(arguments, f.local_variables()), 54 | function_instance: function_instance.clone(), 55 | last_ptr, 56 | return_ptr, 57 | prev_return_ptr, 58 | ptr: Cell::new(0), 59 | } 60 | } 61 | FunctionInstance::HostFn(_) => Frame { 62 | local_variables: Frame::derive_local_variables(arguments, vec![]), 63 | function_instance, 64 | last_ptr: 0, 65 | return_ptr, 66 | prev_return_ptr, 67 | ptr: Cell::new(0), 68 | }, 69 | } 70 | } 71 | 72 | pub fn is_completed(&self) -> bool { 73 | self.ptr.get().ge(&self.last_ptr) 74 | } 75 | 76 | pub fn is_fresh(&self) -> bool { 77 | self.ptr.get().eq(&0) 78 | } 79 | 80 | pub fn get_local_variables(&self) -> RefMut> { 81 | self.local_variables.borrow_mut() 82 | } 83 | 84 | // From: args[2,1]; locals[4,3] 85 | // To [4,3,2,1] 86 | fn derive_local_variables( 87 | arguments: &mut Vec, 88 | mut local_variables: Vec, 89 | ) -> RefCell> { 90 | local_variables.append(arguments); 91 | RefCell::new(local_variables) 92 | } 93 | 94 | pub fn get_return_type(&self) -> &Vec { 95 | &self.function_instance.get_return_type() 96 | } 97 | 98 | pub fn get_return_count(&self) -> u32 { 99 | self.function_instance.get_return_count() 100 | } 101 | 102 | pub fn get_start_of_label(&self) -> u32 { 103 | self.ptr.get().sub(1) 104 | } 105 | 106 | fn peek(&self) -> Option<&u8> { 107 | let ptr = self.ptr.get(); 108 | match self.function_instance { 109 | FunctionInstance::LocalFn(ref f) => f.get(ptr as usize), 110 | _ => unreachable!(), 111 | } 112 | } 113 | 114 | pub fn pop_ref(&self) -> Option<&u8> { 115 | let head = self.peek(); 116 | let ptr = self.ptr.get(); 117 | self.ptr.set(ptr + 1); 118 | head 119 | } 120 | 121 | pub fn pop_runtime_type(&self) -> Option { 122 | match self.pop_ref() { 123 | Some(byte) => Some(ValueTypes::from(*byte)), 124 | None => None, 125 | } 126 | } 127 | 128 | pub fn is_next_empty(&self) -> bool { 129 | match self.peek() { 130 | None => true, 131 | _ => false, 132 | } 133 | } 134 | 135 | pub fn jump_to(&self, ptr_of_label: u32) { 136 | self.ptr.replace(ptr_of_label); 137 | } 138 | 139 | pub fn jump_to_last(&self) { 140 | let last_ptr = self.last_ptr; 141 | self.jump_to(last_ptr); 142 | } 143 | 144 | pub fn get_table_address(&self) -> Indice { 145 | From::from(0u32) 146 | } 147 | } 148 | 149 | impl fmt::Debug for Frame { 150 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 151 | f.debug_struct("Frame") 152 | .field( 153 | "type", 154 | &format!("{:?}", self.function_instance.get_function_type()), 155 | ) 156 | .field("ptr", &self.ptr) 157 | .field("return_ptr", &self.return_ptr) 158 | .finish() 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(try_trait)] 2 | #![feature(try_from)] 3 | #![feature(int_to_from_bytes)] 4 | #![feature(alloc)] 5 | #![feature(core_intrinsics)] 6 | #![cfg_attr(not(test), no_std)] 7 | #![allow(clippy::needless_range_loop)] 8 | 9 | #[cfg(test)] 10 | #[macro_use] 11 | extern crate std as alloc; 12 | 13 | // FIXME: Use 'heapless' crate. 14 | #[cfg(not(test))] 15 | #[macro_use] 16 | extern crate alloc; 17 | 18 | #[cfg(test)] 19 | #[macro_use] 20 | extern crate core; 21 | 22 | extern crate heapless; 23 | extern crate libm; 24 | 25 | #[macro_use] 26 | mod decode; 27 | mod embedder; 28 | mod error; 29 | mod frame; 30 | mod function; 31 | mod global; 32 | mod indice; 33 | mod isa; 34 | mod label; 35 | mod memory; 36 | mod module; 37 | mod spectest; 38 | mod stack; 39 | mod store; 40 | mod table; 41 | mod validate; 42 | mod value; 43 | mod value_type; 44 | mod vm; 45 | 46 | pub use self::embedder::{decode_module, init_store, instantiate_module, validate_module}; 47 | pub use self::error::{Trap, WasmError}; 48 | pub use self::function::{FunctionInstance, FunctionType}; 49 | pub use self::module::{ExternalModule, ExternalModules}; 50 | pub use self::spectest::create_spectest; 51 | pub use self::value::Values; 52 | pub use self::value_type::ValueTypes; 53 | pub use self::vm::ModuleInstance; 54 | 55 | #[cfg(test)] 56 | mod tests { 57 | use super::*; 58 | use std::fs::File; 59 | use std::io::Read; 60 | 61 | #[test] 62 | fn test_repl() { 63 | let mut buf: Vec = Vec::with_capacity(10); 64 | let source = vec![1, 2, 3]; 65 | buf.resize(6, 0); 66 | buf[3..6].copy_from_slice(&source); 67 | assert_eq!(buf, vec![0, 0, 0, 1, 2, 3]); 68 | } 69 | 70 | macro_rules! test_eval { 71 | ($fn_name:ident, $file_name:expr, $call_arguments: expr, $expect_value: expr) => { 72 | #[test] 73 | fn $fn_name() { 74 | let mut file = File::open(format!("./dist/{}.wasm", $file_name)).unwrap(); 75 | let mut bytes = vec![]; 76 | file.read_to_end(&mut bytes).unwrap(); 77 | 78 | let store = init_store(); 79 | let section = decode_module(&bytes); 80 | let mut vm = instantiate_module(store, section, Default::default(), 65536).unwrap(); 81 | let actual = vm.run("_subject", $call_arguments).unwrap(); 82 | assert_eq!(actual, Values::I32($expect_value)); 83 | } 84 | }; 85 | } 86 | 87 | fn my_hal_function(_arguments: &[Values]) -> alloc::vec::Vec { 88 | [Values::I32(3 * 5)].to_vec() 89 | } 90 | 91 | #[test] 92 | fn eval_discovery() { 93 | let mut file = File::open("./discovery-wasm/pkg/discovery_wasm_bg.wasm").unwrap(); 94 | let mut bytes = vec![]; 95 | file.read_to_end(&mut bytes).unwrap(); 96 | 97 | let store = init_store(); 98 | let section = decode_module(&bytes); 99 | let mut external_modules = ExternalModules::default(); 100 | let external_module = ExternalModule::new( 101 | [FunctionInstance::new_host_fn( 102 | // FIXME: no_mangle 103 | Some("__wbg_myhalfunction_59a89d8df8955cf7".to_owned()), 104 | FunctionType::new( 105 | [ValueTypes::I32, ValueTypes::I32].to_vec(), 106 | [ValueTypes::I32].to_vec(), 107 | ), 108 | &my_hal_function, 109 | )] 110 | .to_vec(), 111 | [].to_vec(), 112 | [].to_vec(), 113 | [].to_vec(), 114 | [].to_vec(), 115 | ); 116 | external_modules 117 | .register_module(Some("./discovery_wasm".to_owned()), external_module) 118 | .unwrap(); 119 | let mut vm = instantiate_module(store, section, external_modules, 65536).unwrap(); 120 | 121 | let actual = vm 122 | .run( 123 | "use_hal_function", 124 | [Values::I32(3), Values::I32(5)].to_vec(), 125 | ) 126 | .unwrap(); 127 | assert_eq!(actual, Values::I32(25)); 128 | } 129 | 130 | test_eval!(evaluate_cons8, "cons8", vec![], 42); 131 | test_eval!( 132 | evaluate_add_simple, 133 | "add", 134 | vec![Values::I32(3), Values::I32(4)], 135 | 7 136 | ); 137 | test_eval!(evaluate_sub, "sub", vec![Values::I32(10)], 90); 138 | test_eval!( 139 | evaluate_add_five, 140 | "add_five", 141 | vec![Values::I32(3), Values::I32(4)], 142 | 17 143 | ); 144 | test_eval!(evaluate_if_lt_1, "if_lt", vec![Values::I32(10)], 15); 145 | test_eval!(evaluate_if_lt_2, "if_lt", vec![Values::I32(9)], 19); 146 | test_eval!(evaluate_if_lt_3, "if_lt", vec![Values::I32(11)], 26); 147 | 148 | test_eval!(evaluate_if_gt_1, "if_gt", vec![Values::I32(10)], 15); 149 | test_eval!(evaluate_if_gt_2, "if_gt", vec![Values::I32(15)], 25); 150 | test_eval!(evaluate_if_gt_3, "if_gt", vec![Values::I32(5)], 20); 151 | 152 | test_eval!(evaluate_if_eq_1, "if_eq", vec![Values::I32(10)], 15); 153 | test_eval!(evaluate_if_eq_2, "if_eq", vec![Values::I32(11)], 21); 154 | test_eval!(evaluate_fib, "fib", vec![Values::I32(15)], 610); 155 | test_eval!(evaluate_5_count, "count", vec![Values::I32(5)], 35); 156 | test_eval!(evaluate_10_count, "count", vec![Values::I32(10)], 145); 157 | test_eval!(evaluate_100_count, "count", vec![Values::I32(100)], 14950); 158 | } 159 | -------------------------------------------------------------------------------- /src/table.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use alloc::rc::Rc; 4 | use alloc::string::String; 5 | use alloc::vec::Vec; 6 | use core::cell::RefCell; 7 | use core::clone::Clone; 8 | use decode::{Element, TableType}; 9 | use error::{Result, Trap, WasmError}; 10 | use function::FunctionInstance; 11 | use global::GlobalInstances; 12 | use indice::Indice; 13 | use isa::Isa; 14 | use memory::Limit; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct TableInstance { 18 | pub(crate) function_elements: Vec>, 19 | pub(crate) export_name: Option, 20 | table_type: TableType, 21 | } 22 | 23 | impl TableInstance { 24 | pub fn new( 25 | elements: Vec, 26 | table_type: TableType, 27 | export_name: Option, 28 | global_instances: &GlobalInstances, 29 | function_instances: &[FunctionInstance], 30 | ) -> Result { 31 | let table_size = match table_type.limit { 32 | Limit::NoUpperLimit(min) | Limit::HasUpperLimit(min, _) => min, 33 | } as usize; 34 | let mut function_elements = vec![None; table_size]; 35 | for el in elements.into_iter() { 36 | let offset = Isa::constant_expression(&el.offset, global_instances)?; 37 | let mut function_addresses = el.wrap_by_option(function_instances); 38 | let end = offset + function_addresses.len(); 39 | if end > function_elements.len() { 40 | return Err(WasmError::Trap(Trap::ElementSegmentDoesNotFit)); 41 | } 42 | function_addresses.swap_with_slice(&mut function_elements[offset..end]); 43 | } 44 | Ok(TableInstance { 45 | function_elements, 46 | export_name, 47 | table_type, 48 | }) 49 | } 50 | 51 | pub fn validate( 52 | elements: &[Element], 53 | table_type: &TableType, 54 | global_instances: &GlobalInstances, 55 | function_instances: &[FunctionInstance], 56 | ) -> Result<()> { 57 | let table_size = match table_type.limit { 58 | Limit::NoUpperLimit(min) | Limit::HasUpperLimit(min, _) => min, 59 | } as usize; 60 | for el in elements.iter() { 61 | let offset = Isa::constant_expression(&el.offset, global_instances)?; 62 | let mut function_addresses = el.wrap_by_option(function_instances); 63 | let end = offset + function_addresses.len(); 64 | if end > table_size { 65 | return Err(WasmError::Trap(Trap::ElementSegmentDoesNotFit)); 66 | } 67 | } 68 | Ok(()) 69 | } 70 | 71 | pub fn len(&self) -> usize { 72 | self.function_elements.len() 73 | } 74 | 75 | pub fn get_function_instance(&self, idx: u32) -> Result { 76 | match self.function_elements.get(idx as usize) { 77 | Some(Some(x)) => Ok(x.clone()), 78 | Some(None) => Err(WasmError::Trap(Trap::UninitializedElement)), 79 | None => Err(WasmError::Trap(Trap::UndefinedElement)), 80 | } 81 | } 82 | } 83 | 84 | #[derive(Debug)] 85 | pub struct TableInstances(Rc>>); 86 | 87 | impl TableInstances { 88 | pub fn new(table_instances: Vec) -> Self { 89 | TableInstances(Rc::new(RefCell::new(table_instances))) 90 | } 91 | 92 | pub fn empty() -> Self { 93 | TableInstances::new(vec![]) 94 | } 95 | 96 | pub fn find_by_name(&self, name: &str) -> bool { 97 | match self.0.borrow().first() { 98 | Some(table_instance) => table_instance.export_name == Some(name.to_owned()), 99 | None => false, 100 | } 101 | } 102 | 103 | // NOTE: It represents `self.table_type > other_table_type` 104 | pub fn gt_table_type(&self, other: &TableType) -> bool { 105 | match self.0.borrow().first() { 106 | Some(table_instance) => &table_instance.table_type > other, 107 | None => false, 108 | } 109 | } 110 | 111 | pub fn get_table_at(&self, idx: &Indice) -> Option { 112 | let table_instances = self.0.borrow(); 113 | table_instances.get(idx.to_usize()).cloned() 114 | } 115 | 116 | pub fn link( 117 | &self, 118 | elements: &[Element], 119 | global_instances: &GlobalInstances, 120 | function_instances: &[FunctionInstance], 121 | ) -> Result<()> { 122 | let mut table_instances = self.0.borrow_mut(); 123 | let table_instance = table_instances.first_mut()?; 124 | let function_elements = &mut table_instance.function_elements; 125 | 126 | for el in elements.iter() { 127 | let offset = Isa::constant_expression(&el.offset, global_instances)?; 128 | let mut function_addresses = el.wrap_by_option(function_instances); 129 | let end = offset + function_addresses.len(); 130 | function_addresses.swap_with_slice(&mut function_elements[offset..end]); 131 | } 132 | Ok(()) 133 | } 134 | 135 | pub fn validate( 136 | &self, 137 | elements: &[Element], 138 | global_instances: &GlobalInstances, 139 | function_instances: &[FunctionInstance], 140 | ) -> Result<()> { 141 | let mut table_instances = self.0.borrow_mut(); 142 | let table_instance = table_instances.first_mut()?; 143 | let function_elements = &mut table_instance.function_elements; 144 | 145 | for el in elements.iter() { 146 | let offset = Isa::constant_expression(&el.offset, global_instances)?; 147 | let mut function_addresses = el.wrap_by_option(function_instances); 148 | let end = offset + function_addresses.len(); 149 | if end > function_elements.len() { 150 | return Err(WasmError::Trap(Trap::ElementSegmentDoesNotFit)); 151 | } 152 | } 153 | Ok(()) 154 | } 155 | } 156 | 157 | impl Clone for TableInstances { 158 | fn clone(&self) -> Self { 159 | TableInstances(self.0.clone()) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /discovery-wasm/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "cfg-if" 3 | version = "0.1.6" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "discovery-wasm" 8 | version = "0.1.0" 9 | dependencies = [ 10 | "wasm-bindgen 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 11 | ] 12 | 13 | [[package]] 14 | name = "lazy_static" 15 | version = "1.2.0" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | 18 | [[package]] 19 | name = "log" 20 | version = "0.4.6" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | dependencies = [ 23 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "proc-macro2" 28 | version = "0.4.27" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 32 | ] 33 | 34 | [[package]] 35 | name = "quote" 36 | version = "0.6.11" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | dependencies = [ 39 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 40 | ] 41 | 42 | [[package]] 43 | name = "syn" 44 | version = "0.15.26" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | dependencies = [ 47 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 48 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 50 | ] 51 | 52 | [[package]] 53 | name = "unicode-xid" 54 | version = "0.1.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [[package]] 58 | name = "wasm-bindgen" 59 | version = "0.2.33" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | dependencies = [ 62 | "wasm-bindgen-macro 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 63 | ] 64 | 65 | [[package]] 66 | name = "wasm-bindgen-backend" 67 | version = "0.2.33" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | dependencies = [ 70 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "wasm-bindgen-shared 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 76 | ] 77 | 78 | [[package]] 79 | name = "wasm-bindgen-macro" 80 | version = "0.2.33" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | dependencies = [ 83 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "wasm-bindgen-macro-support 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "wasm-bindgen-macro-support" 89 | version = "0.2.33" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "wasm-bindgen-backend 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "wasm-bindgen-shared 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 97 | ] 98 | 99 | [[package]] 100 | name = "wasm-bindgen-shared" 101 | version = "0.2.33" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | 104 | [metadata] 105 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" 106 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" 107 | "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" 108 | "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" 109 | "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" 110 | "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" 111 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 112 | "checksum wasm-bindgen 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "2d8c1eb210a0e91e24feb8ccd6f7484a5d442bfdf2ff179204b3a1d16e1029cc" 113 | "checksum wasm-bindgen-backend 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab9a5e88bf5dea5be82bb5f8b68c0f8e550675796ac88570ea4c4e89923413" 114 | "checksum wasm-bindgen-macro 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "52909345426e198a0d34f63526f9f4d86ca50c24b4a22a019d0bc86570ffe1e3" 115 | "checksum wasm-bindgen-macro-support 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "2a130a5906bd540390cda0a28e7f8a2d450222461beb92a5eb5c6a33b8b8bc2a" 116 | "checksum wasm-bindgen-shared 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "9c406cd5b42c36db6f76f17d28bcd26187fd90cda686231f3d04de07a716fc3a" 117 | -------------------------------------------------------------------------------- /src/global.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use alloc::rc::Rc; 4 | use alloc::string::String; 5 | use alloc::vec::Vec; 6 | use core::cell::RefCell; 7 | use error::{Result, Trap, WasmError}; 8 | use indice::Indice; 9 | use isa::Isa; 10 | use module::{ 11 | ExternalInterface, ExternalInterfaces, ExternalModules, ImportDescriptor, ModuleDescriptor, 12 | GLOBAL_DESCRIPTOR, 13 | }; 14 | use value::Values; 15 | use value_type::ValueTypes; 16 | 17 | #[derive(Debug, Clone, PartialEq)] 18 | pub enum GlobalType { 19 | Const(ValueTypes), 20 | Var(ValueTypes), 21 | } 22 | 23 | impl GlobalType { 24 | pub fn new(code: Option, v: ValueTypes) -> Result { 25 | match code { 26 | Some(0x00) => Ok(GlobalType::Const(v)), 27 | Some(0x01) => Ok(GlobalType::Var(v)), 28 | _ => Err(WasmError::Trap(Trap::InvalidMutability)), 29 | } 30 | } 31 | } 32 | 33 | #[derive(Debug)] 34 | struct GlobalInstanceImpl { 35 | global_type: GlobalType, 36 | value: Values, 37 | export_name: Option, 38 | } 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct GlobalInstance(Rc>); 42 | 43 | impl GlobalInstance { 44 | pub fn new(global_type: GlobalType, value: Values, export_name: Option) -> Self { 45 | GlobalInstance(Rc::new(RefCell::new(GlobalInstanceImpl { 46 | global_type, 47 | value, 48 | export_name, 49 | }))) 50 | } 51 | 52 | pub fn get_value(&self) -> Values { 53 | self.0.borrow().value.clone() 54 | } 55 | 56 | pub fn set_value(&self, value: Values) { 57 | self.0.borrow_mut().value = value; 58 | } 59 | 60 | fn is_same_name(&self, name: &str) -> bool { 61 | self.0.borrow().export_name == Some(name.to_string()) 62 | } 63 | 64 | fn is_same_type(&self, ty: &GlobalType) -> bool { 65 | &self.0.borrow().global_type == ty 66 | } 67 | } 68 | 69 | #[derive(Debug, Clone)] 70 | pub struct GlobalInstances(Rc>>); 71 | 72 | impl GlobalInstances { 73 | pub fn new(global_instances: Vec) -> Self { 74 | GlobalInstances(Rc::new(RefCell::new(global_instances))) 75 | } 76 | 77 | pub fn empty() -> Self { 78 | GlobalInstances::new(vec![]) 79 | } 80 | 81 | pub fn new_with_external( 82 | globals: Vec<(GlobalType, Vec)>, 83 | exports: &ExternalInterfaces, 84 | imports: &[ExternalInterface], 85 | external_modules: &ExternalModules, 86 | ) -> Result { 87 | let mut global_instances: Vec = vec![]; 88 | for import in imports.iter() { 89 | match import { 90 | ExternalInterface { 91 | descriptor: ModuleDescriptor::ImportDescriptor(ImportDescriptor::Global(ty)), 92 | name, 93 | module_name, 94 | } => { 95 | let global_instance = external_modules 96 | .find_global_instances(module_name)? 97 | .find(name) 98 | .ok_or(Trap::UnknownImport)?; 99 | if !global_instance.is_same_type(ty) { 100 | return Err(WasmError::Trap(Trap::IncompatibleImportType)); 101 | } 102 | global_instances.push(global_instance); 103 | } 104 | x => unreachable!("Expected global descriptor, got {:?}", x), 105 | }; 106 | } 107 | for (idx, (global_type, init)) in globals.into_iter().enumerate() { 108 | let export_name = exports 109 | .find_kind_by_idx(idx as u32, &GLOBAL_DESCRIPTOR) 110 | .map(|x| x.name.to_owned()); 111 | let init_first = init.first()?; 112 | let value = match Isa::from(*init_first) { 113 | Isa::I32Const => { 114 | let mut buf = [0; 4]; 115 | buf.clone_from_slice(&init[1..5]); 116 | Values::I32(unsafe { core::mem::transmute::<_, u32>(buf) } as i32) 117 | } 118 | Isa::I64Const => { 119 | let mut buf = [0; 8]; 120 | buf.clone_from_slice(&init[1..9]); 121 | Values::I64(unsafe { core::mem::transmute::<_, u64>(buf) } as i64) 122 | } 123 | Isa::F32Const => { 124 | let mut buf = [0; 4]; 125 | buf.clone_from_slice(&init[1..5]); 126 | Values::F32(f32::from_bits(unsafe { 127 | core::mem::transmute::<_, u32>(buf) 128 | })) 129 | } 130 | Isa::F64Const => { 131 | let mut buf = [0; 8]; 132 | buf.clone_from_slice(&init[1..9]); 133 | Values::F64(f64::from_bits(unsafe { 134 | core::mem::transmute::<_, u64>(buf) 135 | })) 136 | } 137 | Isa::GetGlobal => { 138 | let mut buf = [0; 4]; 139 | buf.clone_from_slice(&init[1..5]); 140 | let idx = Indice::from(unsafe { core::mem::transmute::<_, u32>(buf) }); 141 | global_instances.get(idx.to_usize())?.get_value() 142 | } 143 | x => unreachable!("Expected initial value of global, got {:?}", x), 144 | }; 145 | global_instances.push(GlobalInstance::new(global_type, value, export_name)); 146 | } 147 | Ok(GlobalInstances::new(global_instances)) 148 | } 149 | 150 | pub fn find(&self, name: &str) -> Option { 151 | self 152 | .0 153 | .borrow() 154 | .iter() 155 | .find(|instance| instance.is_same_name(name)) 156 | .cloned() 157 | } 158 | 159 | pub fn get_global(&self, idx: &Indice) -> Result { 160 | self 161 | .0 162 | .borrow() 163 | .get(idx.to_usize()) 164 | .map(|g| g.get_value().to_owned()) 165 | .ok_or(WasmError::Trap(Trap::Notfound)) 166 | } 167 | 168 | pub fn get_global_ext(&self, idx: &Indice) -> i32 { 169 | self 170 | .get_global(idx) 171 | .map(|g| match g { 172 | Values::I32(ref v) => *v, 173 | x => unreachable!("Expect Values::I32, got {:?}", x), 174 | }) 175 | .unwrap_or_else(|_| panic!("Expect to get {:?} of global instances, got None", idx)) 176 | } 177 | 178 | pub fn set_global(&self, idx: &Indice, value: Values) { 179 | if let Some(g) = self.0.borrow_mut().get_mut(idx.to_usize()) { 180 | g.set_value(value) 181 | }; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/decode/decodable.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use alloc::vec::Vec; 3 | use error::{Result, Trap, WasmError}; 4 | use memory::Limit; 5 | 6 | macro_rules! impl_decode_leb128 { 7 | ($ty: ty, $conv_fn: path, $fn_name: ident) => { 8 | fn $fn_name(&mut self) -> $crate::error::Result<($ty, u32)> { 9 | let mut buf: $ty = 0; 10 | let mut shift = 0; 11 | 12 | // Check whether leftmost bit is 1 or 0, if most significant bit is zero, 13 | // A result of bitwise AND become zero too. 14 | // +------------+------------+ 15 | // N | 0b11111111 | 0b01111111 | 16 | // & + & + & | 17 | // B | 0b10000000 | 0b10000000 | 18 | // +------------+------------+ 19 | // Result | 0b10000000 | 0b00000000 | 20 | // +------------+------------+ 21 | loop { 22 | let raw_code = self.next()?; 23 | let is_msb_zero = raw_code & 0b1000_0000 == 0; 24 | let num = $conv_fn(raw_code & 0b0111_1111); // Drop leftmost bit 25 | // buf = 00000000_00000000_10000000_00000000 26 | // num = 00000000_00000000_00000000_00000001 27 | // num << 7 = 00000000_00000000_00000000_10000000 28 | // buf | num 00000000_00000000_10000000_10000000 29 | let (shifted, is_overflowed) = num.overflowing_shl(shift); 30 | if is_overflowed { 31 | return Err($crate::error::WasmError::Trap($crate::error::Trap::IntegerRepresentationTooLong)); 32 | } 33 | buf |= shifted; 34 | shift += 7; 35 | if is_msb_zero { 36 | break; 37 | } 38 | } 39 | Ok((buf, shift)) 40 | } 41 | }; 42 | } 43 | 44 | macro_rules! impl_decode_signed_integer { 45 | ($fn_name: ident, $decode_name: ident, $buf_ty: ty) => { 46 | fn $fn_name(&mut self) -> Result<$buf_ty> { 47 | let (mut buf, shift) = self.$decode_name()?; 48 | let (signed_bits, overflowed) = (1 as $buf_ty).overflowing_shl(shift - 1); 49 | if overflowed { 50 | return Ok(buf); 51 | } 52 | let is_buf_signed = buf & signed_bits != 0; 53 | if is_buf_signed { 54 | buf |= !0 << shift; 55 | }; 56 | Ok(buf) 57 | } 58 | }; 59 | } 60 | 61 | pub trait AbstractDecodable { 62 | fn bytes(&self) -> &Vec; 63 | fn byte_ptr(&self) -> usize; 64 | fn increment_ptr(&mut self); 65 | } 66 | 67 | pub trait U8Iterator: AbstractDecodable { 68 | fn next(&mut self) -> Option { 69 | let el = self.bytes().get(self.byte_ptr()).cloned(); 70 | self.increment_ptr(); 71 | el 72 | } 73 | } 74 | 75 | pub trait Peekable: AbstractDecodable { 76 | fn peek(&self) -> Option { 77 | self.bytes().get(self.byte_ptr()).cloned() 78 | } 79 | } 80 | 81 | pub trait Leb128Decodable: U8Iterator { 82 | impl_decode_leb128!(u32, u32::from, decode_leb128_u32_internal); 83 | impl_decode_leb128!(u64, u64::from, decode_leb128_u64_internal); 84 | } 85 | 86 | pub trait U32Decodable: Leb128Decodable { 87 | fn decode_leb128_u32(&mut self) -> Result { 88 | let (buf, _) = self.decode_leb128_u32_internal()?; 89 | Ok(buf) 90 | } 91 | } 92 | 93 | pub trait SignedIntegerDecodable: Leb128Decodable { 94 | impl_decode_signed_integer!(decode_leb128_i32, decode_leb128_u32_internal, u32); 95 | impl_decode_signed_integer!(decode_leb128_i64, decode_leb128_u64_internal, u64); 96 | } 97 | 98 | pub trait LimitDecodable: U32Decodable { 99 | fn decode_limit(&mut self) -> Result { 100 | use self::Limit::*; 101 | match self.next() { 102 | Some(0x0) => { 103 | let min = self.decode_leb128_u32()?; 104 | Ok(NoUpperLimit(min)) 105 | } 106 | Some(0x1) => { 107 | let min = self.decode_leb128_u32()?; 108 | let max = self.decode_leb128_u32()?; 109 | Ok(HasUpperLimit(min, max)) 110 | } 111 | x => unreachable!("Expected limit code, got {:?}", x), 112 | } 113 | } 114 | } 115 | 116 | pub trait NameDecodable: U32Decodable { 117 | fn decode_name(&mut self) -> Result { 118 | let size_of_name = self.decode_leb128_u32()?; 119 | let mut buf = vec![]; 120 | for _ in 0..size_of_name { 121 | buf.push(self.next()?); 122 | } 123 | String::from_utf8(buf).map_err(|_| WasmError::Trap(Trap::InvalidUTF8Encoding)) 124 | } 125 | } 126 | 127 | macro_rules! impl_decodable { 128 | ($name: ident) => { 129 | pub struct $name { 130 | bytes: Vec, 131 | byte_ptr: usize, 132 | } 133 | 134 | impl $crate::decode::AbstractDecodable for $name { 135 | fn bytes(&self) -> &Vec { 136 | &self.bytes 137 | } 138 | fn byte_ptr(&self) -> usize { 139 | self.byte_ptr 140 | } 141 | fn increment_ptr(&mut self) { 142 | self.byte_ptr += 1; 143 | } 144 | } 145 | 146 | impl $crate::decode::U8Iterator for $name {} 147 | 148 | impl $name { 149 | pub fn new(bytes: Vec) -> Self { 150 | $name { 151 | bytes: bytes, 152 | byte_ptr: 0, 153 | } 154 | } 155 | } 156 | }; 157 | } 158 | 159 | pub trait Decodable { 160 | type Item; 161 | fn decode(&mut self) -> Result; 162 | } 163 | 164 | #[cfg(test)] 165 | mod tests { 166 | use super::*; 167 | 168 | impl_decodable!(TestDecodable); 169 | impl Leb128Decodable for TestDecodable {} 170 | impl SignedIntegerDecodable for TestDecodable {} 171 | 172 | #[test] 173 | fn decode_i32_positive() { 174 | assert_eq!( 175 | // 128 176 | TestDecodable::new(vec![0x80, 0x01]) 177 | .decode_leb128_i32() 178 | .map(|x| x as i32), 179 | Ok(128) 180 | ); 181 | } 182 | 183 | #[test] 184 | fn decode_i32_negative() { 185 | assert_eq!( 186 | // -128 187 | TestDecodable::new(vec![0x80, 0x7f]) 188 | .decode_leb128_i32() 189 | .map(|x| x as i32), 190 | Ok(-128) 191 | ); 192 | } 193 | 194 | #[test] 195 | fn decode_i32_min() { 196 | assert_eq!( 197 | // -2147483648 198 | TestDecodable::new(vec![0x80, 0x80, 0x80, 0x80, 0x78]) 199 | .decode_leb128_i32() 200 | .map(|x| x as i32), 201 | Ok(std::i32::MIN) 202 | ); 203 | } 204 | 205 | #[test] 206 | fn decode_i32_max() { 207 | assert_eq!( 208 | // 2147483647 209 | TestDecodable::new(vec![0xff, 0xff, 0xff, 0xff, 0x07]) 210 | .decode_leb128_i32() 211 | .map(|x| x as i32), 212 | Ok(std::i32::MAX) 213 | ); 214 | } 215 | 216 | #[test] 217 | fn decode_i64_min() { 218 | assert_eq!( 219 | // -9223372036854775808 220 | TestDecodable::new(vec![ 221 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 222 | ]) 223 | .decode_leb128_i64() 224 | .map(|x| x as i64), 225 | Ok(std::i64::MIN) 226 | ); 227 | } 228 | 229 | #[test] 230 | fn decode_i64_max() { 231 | assert_eq!( 232 | // 9223372036854775807 233 | TestDecodable::new(vec![ 234 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 235 | ]) 236 | .decode_leb128_i64() 237 | .map(|x| x as i64), 238 | Ok(std::i64::MAX) 239 | ); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/function.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use alloc::rc::Rc; 4 | use alloc::string::String; 5 | use alloc::vec::Vec; 6 | use core::cell::RefCell; 7 | use core::fmt; 8 | use error::{Result, TypeError, WasmError}; 9 | use module::ModuleName; 10 | use stack::StackEntry; 11 | use value::Values; 12 | use value_type::ValueTypes; 13 | 14 | #[derive(PartialEq, Clone)] 15 | struct FunctionTypeImpl { 16 | parameters: Vec, 17 | returns: Vec, 18 | } 19 | 20 | #[derive(PartialEq, Clone)] 21 | pub struct FunctionType(Rc); 22 | 23 | impl FunctionType { 24 | pub fn new(parameters: Vec, returns: Vec) -> Self { 25 | FunctionType(Rc::new(FunctionTypeImpl { 26 | parameters, 27 | returns, 28 | })) 29 | } 30 | 31 | pub fn parameters<'a>(&'a self) -> &'a Vec { 32 | &self.0.parameters 33 | } 34 | 35 | pub fn returns<'a>(&'a self) -> &'a Vec { 36 | &self.0.returns 37 | } 38 | 39 | pub fn get_arity(&self) -> u32 { 40 | self.0.parameters.len() as u32 41 | } 42 | } 43 | 44 | impl fmt::Debug for FunctionType { 45 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 46 | write!( 47 | f, 48 | "({}) -> ({})", 49 | self 50 | .0 51 | .parameters 52 | .iter() 53 | .map(|p| format!("{:?}", p)) 54 | .collect::>() 55 | .join(", "), 56 | self 57 | .0 58 | .returns 59 | .iter() 60 | .map(|p| format!("{:?}", p)) 61 | .collect::>() 62 | .join(", "), 63 | ) 64 | } 65 | } 66 | 67 | #[derive(PartialEq)] 68 | pub struct FunctionInstanceImpl { 69 | export_name: Option, 70 | function_type: FunctionType, 71 | local_variables: Vec, 72 | body: Vec, 73 | source_module_name: RefCell>, 74 | } 75 | 76 | impl FunctionInstanceImpl { 77 | pub fn get_expressions_count(&self) -> usize { 78 | self.body.len() 79 | } 80 | 81 | pub fn local_variables(&self) -> Vec { 82 | self.local_variables.clone() 83 | } 84 | 85 | pub fn get(&self, idx: usize) -> Option<&u8> { 86 | self.body.get(idx) 87 | } 88 | 89 | pub(crate) fn body(&self) -> &[u8] { 90 | &self.body 91 | } 92 | } 93 | 94 | pub struct HostFunction { 95 | export_name: Option, 96 | function_type: FunctionType, 97 | source_module_name: RefCell>, 98 | callable: &'static Fn(&[Values]) -> Vec, 99 | } 100 | 101 | impl HostFunction { 102 | pub(crate) fn call(&self, arguments: &[Values]) -> Vec { 103 | let callable = self.callable; 104 | callable(arguments) 105 | } 106 | } 107 | 108 | impl PartialEq for HostFunction { 109 | fn eq(&self, other: &HostFunction) -> bool { 110 | self.export_name == other.export_name 111 | && self.function_type == other.function_type 112 | && self.source_module_name == other.source_module_name 113 | } 114 | } 115 | 116 | #[derive(Clone, PartialEq)] 117 | pub enum FunctionInstance { 118 | LocalFn(Rc), 119 | HostFn(Rc), 120 | } 121 | 122 | impl FunctionInstance { 123 | pub fn new( 124 | export_name: Option, 125 | function_type: FunctionType, 126 | mut locals: Vec, 127 | body: Vec, 128 | ) -> Self { 129 | locals.reverse(); 130 | let local_variables = locals 131 | .iter() 132 | .map(|local| StackEntry::new_value(Values::from(local))) 133 | .collect::>(); 134 | FunctionInstance::LocalFn(Rc::new(FunctionInstanceImpl { 135 | export_name, 136 | function_type, 137 | local_variables, 138 | body, 139 | source_module_name: RefCell::new(None), 140 | })) 141 | } 142 | 143 | pub fn new_host_fn( 144 | export_name: Option, 145 | function_type: FunctionType, 146 | callable: &'static F, 147 | ) -> Self 148 | where 149 | F: Fn(&[Values]) -> Vec, 150 | { 151 | FunctionInstance::HostFn(Rc::new(HostFunction { 152 | export_name, 153 | function_type, 154 | source_module_name: RefCell::new(None), 155 | callable, 156 | })) 157 | } 158 | 159 | pub fn function_type_ref(&self) -> &FunctionType { 160 | match self { 161 | FunctionInstance::LocalFn(f) => &f.function_type, 162 | FunctionInstance::HostFn(f) => &f.function_type, 163 | } 164 | } 165 | 166 | pub fn set_source_module_name(&self, name: &ModuleName) { 167 | if let Some(name) = name { 168 | let mut source_module_name = match self { 169 | FunctionInstance::LocalFn(f) => f.source_module_name.borrow_mut(), 170 | FunctionInstance::HostFn(f) => f.source_module_name.borrow_mut(), 171 | }; 172 | source_module_name.replace(name.to_owned()); 173 | }; 174 | } 175 | 176 | pub fn get_source_module_name(&self) -> Option { 177 | match self { 178 | FunctionInstance::LocalFn(f) => f.source_module_name.borrow().to_owned(), 179 | FunctionInstance::HostFn(f) => f.source_module_name.borrow().to_owned(), 180 | } 181 | } 182 | 183 | pub fn get_arity(&self) -> u32 { 184 | match self { 185 | FunctionInstance::LocalFn(f) => f.function_type.parameters().len() as u32, 186 | FunctionInstance::HostFn(f) => f.function_type.parameters().len() as u32, 187 | } 188 | } 189 | 190 | pub fn get_function_type(&self) -> FunctionType { 191 | match self { 192 | FunctionInstance::LocalFn(f) => f.function_type.to_owned(), 193 | FunctionInstance::HostFn(f) => f.function_type.to_owned(), 194 | } 195 | } 196 | 197 | pub fn get_return_type(&self) -> &Vec { 198 | match self { 199 | FunctionInstance::LocalFn(f) => f.function_type.returns(), 200 | FunctionInstance::HostFn(f) => f.function_type.returns(), 201 | } 202 | } 203 | 204 | pub fn get_return_count(&self) -> u32 { 205 | self.get_return_type().len() as u32 206 | } 207 | 208 | pub fn validate_type(&self, other: &FunctionType) -> Result<()> { 209 | let my = match self { 210 | FunctionInstance::LocalFn(f) => &f.function_type, 211 | FunctionInstance::HostFn(f) => &f.function_type, 212 | }; 213 | if my != other { 214 | Err(WasmError::TypeError(TypeError::TypeMismatch)) 215 | } else { 216 | Ok(()) 217 | } 218 | } 219 | 220 | pub fn is_same_name(&self, other_name: &str) -> bool { 221 | let export_name = match self { 222 | FunctionInstance::LocalFn(f) => &f.export_name, 223 | FunctionInstance::HostFn(f) => &f.export_name, 224 | }; 225 | export_name.as_ref() == Some(&other_name.to_string()) 226 | } 227 | } 228 | 229 | impl fmt::Debug for FunctionInstance { 230 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 231 | let empty: Vec = vec![]; 232 | f.debug_struct("FunctionInstance") 233 | .field( 234 | "export_name", 235 | &match self { 236 | FunctionInstance::LocalFn(ref f) => match **f { 237 | FunctionInstanceImpl { 238 | export_name: Some(ref n), 239 | .. 240 | } => n, 241 | _ => "_", 242 | }, 243 | FunctionInstance::HostFn(ref f) => match **f { 244 | HostFunction { 245 | export_name: Some(ref n), 246 | .. 247 | } => n, 248 | _ => "_", 249 | }, 250 | }, 251 | ) 252 | .field( 253 | "function_type", 254 | &match self { 255 | FunctionInstance::LocalFn(f) => &f.function_type, 256 | FunctionInstance::HostFn(f) => &f.function_type, 257 | }, 258 | ) 259 | .field( 260 | "instructions", 261 | match self { 262 | FunctionInstance::LocalFn(f) => &f.body, 263 | FunctionInstance::HostFn(_) => &empty, 264 | }, 265 | ) 266 | .finish() 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/decode/instruction.rs: -------------------------------------------------------------------------------- 1 | use super::decodable::{Peekable, SignedIntegerDecodable, U32Decodable}; 2 | use alloc::vec::Vec; 3 | use error::{Result, WasmError, Trap}; 4 | use isa::Isa; 5 | 6 | macro_rules! impl_decode_float { 7 | ($buf_ty: ty, $fn_name: ident, $bitwidth: expr) => { 8 | fn $fn_name(&mut self) -> $crate::error::Result<$buf_ty> { 9 | let mut buf = [0u8; $bitwidth]; 10 | for i in 0..$bitwidth { 11 | buf[i] = self.next()?; 12 | } 13 | Ok(unsafe { core::mem::transmute::<_, $buf_ty>(buf)}) 14 | } 15 | }; 16 | } 17 | 18 | macro_rules! impl_push_raw_bytes { 19 | ($name: ident, $ty: ty, $width: expr) => { 20 | fn $name(&self, raw: $ty, expressions: &mut Vec) { 21 | let bytes: [u8; $width] = unsafe { core::mem::transmute(raw) }; 22 | for byte in bytes.iter() { 23 | expressions.push(*byte); 24 | } 25 | } 26 | }; 27 | } 28 | 29 | pub trait InstructionDecodable: U32Decodable + Peekable + SignedIntegerDecodable { 30 | impl_decode_float!(u32, decode_f32, 4); 31 | impl_decode_float!(u64, decode_f64, 8); 32 | impl_push_raw_bytes!(push_u32_as_bytes, u32, 4); 33 | impl_push_raw_bytes!(push_u64_as_bytes, u64, 8); 34 | 35 | fn decode_memory_parameter(&mut self) -> Result<(u32, u32)> { 36 | let align = self.decode_leb128_u32(); 37 | let offset = self.decode_leb128_u32(); 38 | match (align, offset) { 39 | (Ok(align), Ok(offset)) => Ok((align as u32, offset as u32)), 40 | (Err(WasmError::Trap(Trap::BitshiftOverflow)), _) 41 | | (_, Err(WasmError::Trap(Trap::BitshiftOverflow))) => { 42 | Err(WasmError::Trap(Trap::MemoryAccessOutOfBounds)) 43 | } 44 | _ => Err(WasmError::Trap(Trap::Unknown)), 45 | } 46 | } 47 | 48 | fn decode_memory(&mut self, inst: u8, expressions: &mut Vec) -> Result<()> { 49 | let (align, offset) = self.decode_memory_parameter()?; 50 | expressions.push(inst); 51 | self.push_u32_as_bytes(align, expressions); 52 | self.push_u32_as_bytes(offset, expressions); 53 | Ok(()) 54 | } 55 | 56 | fn decode_instructions(&mut self) -> Result> { 57 | use self::Isa::*; 58 | let mut expressions = vec![]; 59 | while !Isa::is_else_or_end(self.peek()) { 60 | let code = self.next()?; 61 | match Isa::from(code) { 62 | // NOTE: Else and End are already consumed at decoding "If" instructions. 63 | Reserved | End | Else => unreachable!("{:?}", code), 64 | Unreachable | Nop | Return | DropInst => expressions.push(code), 65 | 66 | Block => { 67 | let block_type = self.next()?; 68 | let mut instructions = self.decode_instructions()?; 69 | let size = 70 | (2 /* Block inst + Type of block */ + 4 /* size of size */ + instructions.len()) as u32; 71 | expressions.push(code); 72 | self.push_u32_as_bytes(size, &mut expressions); 73 | expressions.push(block_type); 74 | expressions.append(&mut instructions); 75 | } 76 | Loop => { 77 | let block_type = self.next()?; 78 | let mut instructions = self.decode_instructions()?; 79 | expressions.push(code); 80 | expressions.push(block_type); 81 | expressions.append(&mut instructions); 82 | } 83 | If => { 84 | let block_type = self.next()?; 85 | let mut if_insts = self.decode_instructions()?; 86 | let last = *if_insts.last()?; 87 | let mut else_insts = match Isa::from(last) { 88 | Else => self.decode_instructions()?, 89 | End => vec![], 90 | x => unreachable!("{:?}", x), 91 | }; 92 | let size_of_if = (2 /* If inst + Type of block */ + 8 + if_insts.len()) as u32; 93 | let size_of_else = else_insts.len() as u32; 94 | expressions.push(code); 95 | self.push_u32_as_bytes(size_of_if, &mut expressions); 96 | self.push_u32_as_bytes(size_of_else, &mut expressions); 97 | expressions.push(block_type); 98 | expressions.append(&mut if_insts); 99 | expressions.append(&mut else_insts); 100 | } 101 | 102 | GetLocal | SetLocal | TeeLocal | GetGlobal | SetGlobal | Br | BrIf | Call => { 103 | expressions.push(code); 104 | let idx = self.decode_leb128_u32()?; 105 | self.push_u32_as_bytes(idx, &mut expressions); 106 | } 107 | 108 | BrTable => { 109 | expressions.push(code); 110 | let len = self.decode_leb128_u32()?; 111 | self.push_u32_as_bytes(len, &mut expressions); 112 | for _ in 0..len { 113 | let idx = self.decode_leb128_u32()?; 114 | self.push_u32_as_bytes(idx, &mut expressions); 115 | } 116 | let idx = self.decode_leb128_u32()?; 117 | self.push_u32_as_bytes(idx, &mut expressions); 118 | } 119 | CallIndirect => { 120 | expressions.push(code); 121 | let idx = self.decode_leb128_u32()?; 122 | self.push_u32_as_bytes(idx, &mut expressions); 123 | self.next(); // Drop code 0x00. 124 | } 125 | 126 | I32Const => { 127 | expressions.push(code); 128 | let value = self.decode_leb128_i32()?; 129 | self.push_u32_as_bytes(value, &mut expressions); 130 | } 131 | I64Const => { 132 | expressions.push(code); 133 | let value = self.decode_leb128_i64()?; 134 | self.push_u64_as_bytes(value, &mut expressions); 135 | } 136 | F32Const => { 137 | expressions.push(code); 138 | let value = self.decode_f32()?; 139 | self.push_u32_as_bytes(value, &mut expressions); 140 | } 141 | F64Const => { 142 | expressions.push(code); 143 | let value = self.decode_f64()?; 144 | self.push_u64_as_bytes(value, &mut expressions); 145 | } 146 | 147 | I32Load | I64Load | F32Load | F64Load | I32Load8Sign | I32Load8Unsign | I32Load16Sign 148 | | I32Load16Unsign | I64Load8Sign | I64Load8Unsign | I64Load16Sign | I64Load16Unsign 149 | | I64Load32Sign | I64Load32Unsign | I32Store | I64Store | F32Store | F64Store 150 | | I32Store8 | I32Store16 | I64Store8 | I64Store16 | I64Store32 => { 151 | self.decode_memory(code, &mut expressions)? 152 | } 153 | 154 | MemorySize | MemoryGrow => { 155 | self.next()?; // Drop 0x00; 156 | expressions.push(code); 157 | } 158 | 159 | I32CountLeadingZero 160 | | I32CountTrailingZero 161 | | I32CountNonZero 162 | | I32Add 163 | | I32Sub 164 | | I32Mul 165 | | I32DivSign 166 | | I32DivUnsign 167 | | I32RemSign 168 | | I32RemUnsign 169 | | I32And 170 | | I32Or 171 | | I32Xor 172 | | I32ShiftLeft 173 | | I32ShiftRIghtSign 174 | | I32ShiftRightUnsign 175 | | I32RotateLeft 176 | | I32RotateRight 177 | | I64CountLeadingZero 178 | | I64CountTrailingZero 179 | | I64CountNonZero 180 | | I64Add 181 | | I64Sub 182 | | I64Mul 183 | | I64DivSign 184 | | I64DivUnsign 185 | | I64RemSign 186 | | I64RemUnsign 187 | | I64And 188 | | I64Or 189 | | I64Xor 190 | | I64ShiftLeft 191 | | I64ShiftRightSign 192 | | I64ShiftRightUnsign 193 | | I64RotateLeft 194 | | I64RotateRight 195 | | I64EqualZero 196 | | I64Equal 197 | | I64NotEqual 198 | | I64LessThanSign 199 | | I64LessThanUnSign 200 | | I64GreaterThanSign 201 | | I64GreaterThanUnSign 202 | | I64LessEqualSign 203 | | I64LessEqualUnSign 204 | | I64GreaterEqualSign 205 | | I64GreaterEqualUnSign 206 | | I32WrapI64 207 | | I32EqualZero 208 | | I32Equal 209 | | I32NotEqual 210 | | I32LessThanSign 211 | | I32LessThanUnsign 212 | | I32GreaterThanSign 213 | | I32GreaterThanUnsign 214 | | I32LessEqualSign 215 | | I32LessEqualUnsign 216 | | I32GreaterEqualSign 217 | | I32GreaterEqualUnsign 218 | | F32Equal 219 | | F32NotEqual 220 | | F32LessThan 221 | | F32GreaterThan 222 | | F32LessEqual 223 | | F32GreaterEqual 224 | | F64Equal 225 | | F64NotEqual 226 | | F64LessThan 227 | | F64GreaterThan 228 | | F64LessEqual 229 | | F64GreaterEqual 230 | | F32Abs 231 | | F32Neg 232 | | F32Ceil 233 | | F32Floor 234 | | F32Trunc 235 | | F32Nearest 236 | | F32Sqrt 237 | | F32Add 238 | | F32Sub 239 | | F32Mul 240 | | F32Div 241 | | F32Min 242 | | F32Max 243 | | F32Copysign 244 | | F64Abs 245 | | F64Neg 246 | | F64Ceil 247 | | F64Floor 248 | | F64Trunc 249 | | F64Nearest 250 | | F64Sqrt 251 | | F64Add 252 | | F64Sub 253 | | F64Mul 254 | | F64Div 255 | | F64Min 256 | | F64Max 257 | | F64Copysign 258 | | I32TruncSignF32 259 | | I32TruncUnsignF32 260 | | I32TruncSignF64 261 | | I32TruncUnsignF64 262 | | I64ExtendSignI32 263 | | I64ExtendUnsignI32 264 | | I64TruncSignF32 265 | | I64TruncUnsignF32 266 | | I64TruncSignF64 267 | | I64TruncUnsignF64 268 | | F32ConvertSignI32 269 | | F32ConvertUnsignI32 270 | | F32ConvertSignI64 271 | | F32ConvertUnsignI64 272 | | F32DemoteF64 273 | | F64ConvertSignI32 274 | | F64ConvertUnsignI32 275 | | F64ConvertSignI64 276 | | F64ConvertUnsignI64 277 | | F64PromoteF32 278 | | I32ReinterpretF32 279 | | I64ReinterpretF64 280 | | F32ReinterpretI32 281 | | F64ReinterpretI64 282 | | Select => expressions.push(code), 283 | }; 284 | } 285 | let end_code = self.next()?; 286 | match Isa::from(end_code) { 287 | Else | End => expressions.push(end_code), 288 | x => unreachable!("{:?}", x), 289 | } 290 | Ok(expressions) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/stack.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use alloc::rc::Rc; 4 | use alloc::vec::Vec; 5 | use core::cell::{Cell, RefCell}; 6 | use core::fmt; 7 | use error::{Result, Trap, WasmError}; 8 | use frame::Frame; 9 | use indice::Indice; 10 | use label::{Label, LabelKind}; 11 | use value::Values; 12 | use value_type::ValueTypes; 13 | 14 | #[derive(PartialEq)] 15 | enum StackEntryImpl { 16 | Empty, 17 | Value(Values), 18 | Label(Label), 19 | } 20 | 21 | #[derive(PartialEq, Clone)] 22 | pub struct StackEntry(Rc); 23 | 24 | impl fmt::Debug for StackEntry { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | use self::StackEntryImpl::*; 27 | let label = match *self.0 { 28 | Empty => "_".to_owned(), 29 | Value(ref v) => format!("{:?}", v), 30 | Label(ref v) => format!("{:?}", v), 31 | }; 32 | write!(f, "{}", label) 33 | } 34 | } 35 | 36 | impl StackEntry { 37 | fn new(entry: StackEntryImpl) -> Self { 38 | StackEntry(Rc::new(entry)) 39 | } 40 | 41 | pub fn new_empty() -> Self { 42 | StackEntry::new(StackEntryImpl::Empty) 43 | } 44 | 45 | pub fn new_value(value: Values) -> Self { 46 | StackEntry::new(StackEntryImpl::Value(value)) 47 | } 48 | 49 | pub fn new_label( 50 | continuation: u32, 51 | return_type: ValueTypes, 52 | source_instruction: LabelKind, 53 | ) -> Self { 54 | StackEntry::new(StackEntryImpl::Label(Label { 55 | continuation, 56 | return_type, 57 | source_instruction, 58 | })) 59 | } 60 | 61 | fn is_label(&self) -> bool { 62 | use self::StackEntryImpl::*; 63 | match *self.0 { 64 | Label(_) => true, 65 | _ => false, 66 | } 67 | } 68 | } 69 | 70 | macro_rules! impl_pop { 71 | ($name: ident, $name_ext: ident, $path: path, $ret: ty, $error_decription: expr) => { 72 | pub fn $name(&self) -> Result<$ret> { 73 | let value = self.pop()?; 74 | match *value.0 { 75 | $path(ref v) => Ok(v.to_owned()), 76 | _ => { 77 | self.push(value.to_owned())?; 78 | Err(WasmError::Trap(Trap::Notfound)) 79 | } 80 | } 81 | } 82 | 83 | pub fn $name_ext(&self) -> $ret { 84 | self 85 | .$name() 86 | .expect($error_decription) 87 | } 88 | }; 89 | } 90 | 91 | macro_rules! impl_pop_value_ext { 92 | ($name: ident, $path: path, $ret: ty) => { 93 | pub fn $name(&self) -> $ret { 94 | match self.pop_value_ext() { 95 | $path(n) => n, 96 | _ => unreachable!(), 97 | } 98 | } 99 | }; 100 | } 101 | 102 | /// Layout of Operand Stack 103 | /// 104 | /// +---------------+ 105 | /// | .. | 106 | /// +---------------+ 107 | /// | Empty* | <- Stack pointer 108 | /// +---------------+ 109 | /// | Locals* | 110 | /// +---------------+ 111 | /// | Local 1 | 112 | /// +---------------+ 113 | /// | Local 0 | 114 | /// +---------------+ 115 | /// | Args* | 116 | /// +---------------+ 117 | /// | Args 1 | 118 | /// +---------------+ 119 | /// | Args 0 | Indices are starts by zero. 120 | /// +---------------+ 121 | /// | ... | <- Frame pointer 122 | /// +---------------+ 123 | pub struct Stack { 124 | pub(crate) stack_size: usize, 125 | operand_stack: RefCell>, 126 | call_stack: RefCell>, 127 | pub(crate) stack_ptr: Cell, 128 | pub(crate) frame_ptr: Cell, 129 | } 130 | 131 | impl Stack { 132 | pub fn new(stack_size: usize) -> Self { 133 | let operand_stack = RefCell::new(vec![StackEntry::new_empty(); stack_size]); 134 | let call_stack = RefCell::new(Vec::with_capacity(stack_size)); 135 | Stack { 136 | stack_size, 137 | operand_stack, 138 | call_stack, 139 | stack_ptr: Cell::new(0), 140 | frame_ptr: Cell::new(0), 141 | } 142 | } 143 | 144 | pub(crate) fn stack_ptr(&self) -> usize { 145 | self.stack_ptr.get() 146 | } 147 | 148 | pub(crate) fn frame_ptr(&self) -> usize { 149 | self.frame_ptr.get() 150 | } 151 | 152 | pub fn get(&self, ptr: usize) -> Option { 153 | self.operand_stack.borrow().get(ptr).cloned() 154 | } 155 | 156 | pub fn set(&self, ptr: usize, entry: StackEntry) -> Result<()> { 157 | if ptr >= self.stack_size { 158 | return Err(WasmError::Trap(Trap::StackOverflow)); 159 | } 160 | self.operand_stack.borrow_mut()[ptr] = entry; 161 | Ok(()) 162 | } 163 | 164 | pub fn push(&self, entry: StackEntry) -> Result<()> { 165 | if self.stack_ptr() >= self.stack_size { 166 | return Err(WasmError::Trap(Trap::StackOverflow)); 167 | } 168 | self.operand_stack.borrow_mut()[self.stack_ptr()] = entry; 169 | self.stack_ptr.set(self.stack_ptr() + 1); 170 | Ok(()) 171 | } 172 | 173 | /// From entries [2,1,0,..]; 174 | /// To stack below. 175 | /// +---------+ 176 | /// | Val* | 177 | /// +---------+ 178 | /// | Val2 | 179 | /// +---------+ 180 | /// | Val1 | 181 | /// +---------+ 182 | /// | Val0 | 183 | /// +---------+ 184 | pub fn push_entries(&self, entries: &mut Vec) -> Result<()> { 185 | let len = entries.len(); 186 | let stack_ptr = self.stack_ptr(); 187 | let stack_ptr_end = stack_ptr + len; 188 | if stack_ptr_end >= self.stack_size { 189 | Err(WasmError::Trap(Trap::StackOverflow)) 190 | } else { 191 | entries.reverse(); 192 | entries.swap_with_slice(&mut self.operand_stack.borrow_mut()[stack_ptr..stack_ptr_end]); 193 | self.stack_ptr.set(stack_ptr_end); 194 | Ok(()) 195 | } 196 | } 197 | 198 | pub fn push_frame(&self, frame: Frame) -> Result<()> { 199 | self.call_stack.borrow_mut().push(frame); 200 | Ok(()) 201 | } 202 | 203 | pub fn push_back_frame(&self, frame: Frame) { 204 | let mut calls = self.call_stack.borrow_mut(); 205 | let len = calls.len(); 206 | calls.push(frame); 207 | calls.swap(len, len - 1); 208 | } 209 | 210 | pub fn pop_frame(&self) -> Option { 211 | let mut calls = self.call_stack.borrow_mut(); 212 | calls.pop() 213 | } 214 | 215 | pub fn call_stack_is_empty(&self) -> bool { 216 | let calls = self.call_stack.borrow(); 217 | calls.is_empty() 218 | } 219 | 220 | pub fn peek(&self) -> Option { 221 | if self.stack_ptr() >= self.stack_size { 222 | return None; 223 | } 224 | if self.stack_ptr() == 0 { 225 | return None; 226 | } 227 | self 228 | .operand_stack 229 | .borrow_mut() 230 | .get(self.stack_ptr() - 1) 231 | .cloned() 232 | } 233 | 234 | pub fn pop(&self) -> Result { 235 | if self.stack_ptr() == 0 { 236 | return Err(WasmError::Trap(Trap::StackUnderflow)); 237 | } 238 | self.stack_ptr.set(self.stack_ptr() - 1); 239 | match self.operand_stack.borrow_mut().get(self.stack_ptr()) { 240 | Some(entry) => Ok(entry.clone()), 241 | None => Err(WasmError::Trap(Trap::Unknown)), 242 | } 243 | } 244 | 245 | impl_pop!( 246 | pop_value, 247 | pop_value_ext, 248 | StackEntryImpl::Value, 249 | Values, 250 | "Expect to pop up Value, but got None" 251 | ); 252 | impl_pop!( 253 | pop_label, 254 | pop_label_ext, 255 | StackEntryImpl::Label, 256 | Label, 257 | "Expect to pop up Label, but got None" 258 | ); 259 | 260 | pub fn pop_until_label(&self) -> Result> { 261 | let mut entry_buffer = vec![]; 262 | while !self.peek().map_or(true, |entry| entry.is_label()) { 263 | entry_buffer.push(self.pop()?); 264 | } 265 | Ok(entry_buffer) 266 | } 267 | 268 | pub fn jump_to_label(&self, depth_of_label: &Indice) -> Result /* point to continue */ { 269 | let mut buf_values = vec![]; 270 | let mut label = None; 271 | for _ in 0..=depth_of_label.to_u32() { 272 | let mut bufs = self.pop_until_label()?; 273 | buf_values.append(&mut bufs); 274 | label = Some(self.pop_label_ext()); 275 | } 276 | let continuation = match label { 277 | Some(Label { 278 | return_type: ValueTypes::Unit, 279 | continuation, 280 | .. 281 | }) => continuation, 282 | Some(Label { 283 | continuation, 284 | source_instruction, 285 | .. 286 | }) => { 287 | match source_instruction { 288 | LabelKind::Loop => { 289 | self.push_entries(&mut buf_values)?; 290 | } 291 | _ => { 292 | // FIXME: Prefer to pop and push with count of return_types. 293 | let return_val = buf_values 294 | .first() 295 | .expect("At least one return value should exists.") 296 | .to_owned(); 297 | self.push(return_val)?; 298 | } 299 | }; 300 | continuation 301 | } 302 | x => unreachable!("At least one label should exists.\n{:?}", x), 303 | }; 304 | Ok(continuation) 305 | } 306 | 307 | impl_pop_value_ext!(pop_value_ext_i32, Values::I32, i32); 308 | 309 | pub fn update_frame_ptr(&self, frame: &Frame) { 310 | self.stack_ptr.set(self.frame_ptr()); 311 | self.frame_ptr.set(frame.prev_return_ptr); 312 | } 313 | } 314 | 315 | impl fmt::Debug for Stack { 316 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 317 | let operands = self.operand_stack.borrow(); 318 | let (entries, _) = operands.split_at(self.stack_ptr()); 319 | let entries = entries 320 | .iter() 321 | .enumerate() 322 | .map(|(i, entry)| match i + 1 { 323 | x if x == self.frame_ptr() => format!("F-> {:?}", entry), 324 | x if x == self.stack_ptr() => format!("S-> {:?}", entry), 325 | _ => format!(" {:?}", entry), 326 | }) 327 | .rev(); 328 | f.debug_list().entries(entries).finish() 329 | } 330 | } 331 | 332 | #[cfg(test)] 333 | mod tests { 334 | use super::*; 335 | 336 | #[test] 337 | fn stack_entry_size() { 338 | assert_eq!(core::mem::size_of::(), 8); 339 | } 340 | 341 | #[test] 342 | fn stack_push() { 343 | let stack = Stack::new(4); 344 | let value = StackEntry::new_value(Values::I32(1)); 345 | stack.push(value).unwrap(); 346 | assert_eq!(stack.pop().unwrap(), StackEntry::new_value(Values::I32(1))); 347 | } 348 | #[test] 349 | fn stack_set() { 350 | let stack = Stack::new(4); 351 | let value = StackEntry::new_value(Values::I32(2)); 352 | stack.set(2, value).unwrap(); 353 | assert_eq!(stack.get(2).unwrap(), StackEntry::new_value(Values::I32(2))); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "byteorder" 3 | version = "1.2.7" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "cc" 8 | version = "1.0.25" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | 11 | [[package]] 12 | name = "cmake" 13 | version = "0.1.35" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "flame" 21 | version = "0.2.2" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 26 | "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 27 | "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 28 | "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 29 | ] 30 | 31 | [[package]] 32 | name = "generic-array" 33 | version = "0.11.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | dependencies = [ 36 | "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 37 | ] 38 | 39 | [[package]] 40 | name = "glob" 41 | version = "0.2.11" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | 44 | [[package]] 45 | name = "hash32" 46 | version = "0.1.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | dependencies = [ 49 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 50 | ] 51 | 52 | [[package]] 53 | name = "heapless" 54 | version = "0.4.1" 55 | source = "git+https://github.com/japaric/heapless#9588c8554db9e6a09d0c780b328e79f5f78544df" 56 | dependencies = [ 57 | "generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 59 | ] 60 | 61 | [[package]] 62 | name = "itoa" 63 | version = "0.4.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | 66 | [[package]] 67 | name = "lazy_static" 68 | version = "0.2.11" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | 71 | [[package]] 72 | name = "libc" 73 | version = "0.2.45" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | 76 | [[package]] 77 | name = "libm" 78 | version = "0.1.2" 79 | source = "git+https://github.com/kogai/libm#46c84888367f8b0b19cbb6cae5a142b0d4dce22f" 80 | 81 | [[package]] 82 | name = "proc-macro2" 83 | version = "0.4.24" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | dependencies = [ 86 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 87 | ] 88 | 89 | [[package]] 90 | name = "quote" 91 | version = "0.6.10" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | dependencies = [ 94 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 95 | ] 96 | 97 | [[package]] 98 | name = "redox_syscall" 99 | version = "0.1.44" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | 102 | [[package]] 103 | name = "ryu" 104 | version = "0.2.7" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | 107 | [[package]] 108 | name = "serde" 109 | version = "1.0.80" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | 112 | [[package]] 113 | name = "serde_derive" 114 | version = "1.0.80" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | dependencies = [ 117 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "syn 0.15.21 (registry+https://github.com/rust-lang/crates.io-index)", 120 | ] 121 | 122 | [[package]] 123 | name = "serde_json" 124 | version = "1.0.33" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | dependencies = [ 127 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 128 | "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 130 | ] 131 | 132 | [[package]] 133 | name = "syn" 134 | version = "0.15.21" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | dependencies = [ 137 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 140 | ] 141 | 142 | [[package]] 143 | name = "thread-id" 144 | version = "3.3.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | dependencies = [ 147 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 149 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 150 | ] 151 | 152 | [[package]] 153 | name = "typenum" 154 | version = "1.10.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | 157 | [[package]] 158 | name = "unicode-xid" 159 | version = "0.1.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | 162 | [[package]] 163 | name = "wabt" 164 | version = "0.7.3" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | dependencies = [ 167 | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 170 | "wabt-sys 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 171 | ] 172 | 173 | [[package]] 174 | name = "wabt-sys" 175 | version = "0.5.3" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | dependencies = [ 178 | "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", 179 | "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 180 | "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 181 | ] 182 | 183 | [[package]] 184 | name = "wasvm" 185 | version = "0.2.0" 186 | dependencies = [ 187 | "flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "heapless 0.4.1 (git+https://github.com/japaric/heapless)", 189 | "libm 0.1.2 (git+https://github.com/kogai/libm)", 190 | "wabt 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", 191 | ] 192 | 193 | [[package]] 194 | name = "winapi" 195 | version = "0.3.6" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | dependencies = [ 198 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 200 | ] 201 | 202 | [[package]] 203 | name = "winapi-i686-pc-windows-gnu" 204 | version = "0.4.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | 207 | [[package]] 208 | name = "winapi-x86_64-pc-windows-gnu" 209 | version = "0.4.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | 212 | [metadata] 213 | "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" 214 | "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" 215 | "checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" 216 | "checksum flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc2706461e1ee94f55cab2ed2e3d34ae9536cfa830358ef80acff1a3dacab30" 217 | "checksum generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8107dafa78c80c848b71b60133954b4a58609a3a1a5f9af037ecc7f67280f369" 218 | "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 219 | "checksum hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12d790435639c06a7b798af9e1e331ae245b7ef915b92f70a39b4cf8c00686af" 220 | "checksum heapless 0.4.1 (git+https://github.com/japaric/heapless)" = "" 221 | "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" 222 | "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" 223 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" 224 | "checksum libm 0.1.2 (git+https://github.com/kogai/libm)" = "" 225 | "checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" 226 | "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" 227 | "checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" 228 | "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" 229 | "checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" 230 | "checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" 231 | "checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" 232 | "checksum syn 0.15.21 (registry+https://github.com/rust-lang/crates.io-index)" = "816b7af21405b011a23554ea2dc3f6576dc86ca557047c34098c1d741f10f823" 233 | "checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" 234 | "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" 235 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 236 | "checksum wabt 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0246aa1834c47c5f3dd87c0932a2c78edd1e9318ced5e92a41f30a0ab4e3a91f" 237 | "checksum wabt-sys 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "462336bb61096e64761730e0dea1bc8b777bd4e3689c7e813c81f1cfdf4e8a51" 238 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" 239 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 240 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 241 | -------------------------------------------------------------------------------- /discovery/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aligned" 3 | version = "0.2.0" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "alloc-cortex-m" 8 | version = "0.3.5" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | dependencies = [ 11 | "cortex-m 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 13 | ] 14 | 15 | [[package]] 16 | name = "bare-metal" 17 | version = "0.2.4" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | dependencies = [ 20 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 21 | ] 22 | 23 | [[package]] 24 | name = "byteorder" 25 | version = "1.3.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | 28 | [[package]] 29 | name = "cortex-m" 30 | version = "0.1.8" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | dependencies = [ 33 | "volatile-register 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 34 | ] 35 | 36 | [[package]] 37 | name = "cortex-m" 38 | version = "0.5.8" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | dependencies = [ 41 | "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 42 | "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 43 | "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 44 | ] 45 | 46 | [[package]] 47 | name = "cortex-m-rt" 48 | version = "0.6.7" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | dependencies = [ 51 | "cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 53 | ] 54 | 55 | [[package]] 56 | name = "cortex-m-rt-macros" 57 | version = "0.1.5" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | dependencies = [ 60 | "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", 64 | ] 65 | 66 | [[package]] 67 | name = "cortex-m-semihosting" 68 | version = "0.3.2" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | dependencies = [ 71 | "cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", 72 | ] 73 | 74 | [[package]] 75 | name = "generic-array" 76 | version = "0.11.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | dependencies = [ 79 | "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 80 | ] 81 | 82 | [[package]] 83 | name = "hash32" 84 | version = "0.1.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | dependencies = [ 87 | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 88 | ] 89 | 90 | [[package]] 91 | name = "heapless" 92 | version = "0.4.2" 93 | source = "git+https://github.com/japaric/heapless#773360b5fd702697866fd20b6ebb245360733d6b" 94 | dependencies = [ 95 | "generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 97 | ] 98 | 99 | [[package]] 100 | name = "libm" 101 | version = "0.1.2" 102 | source = "git+https://github.com/kogai/libm#46c84888367f8b0b19cbb6cae5a142b0d4dce22f" 103 | 104 | [[package]] 105 | name = "linked_list_allocator" 106 | version = "0.6.3" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | 109 | [[package]] 110 | name = "panic-halt" 111 | version = "0.2.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | 114 | [[package]] 115 | name = "proc-macro2" 116 | version = "0.4.26" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | dependencies = [ 119 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 120 | ] 121 | 122 | [[package]] 123 | name = "quote" 124 | version = "0.6.11" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | dependencies = [ 127 | "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", 128 | ] 129 | 130 | [[package]] 131 | name = "r0" 132 | version = "0.2.2" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | 135 | [[package]] 136 | name = "rand" 137 | version = "0.5.5" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | dependencies = [ 140 | "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 141 | ] 142 | 143 | [[package]] 144 | name = "rand_core" 145 | version = "0.2.2" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | dependencies = [ 148 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 149 | ] 150 | 151 | [[package]] 152 | name = "rand_core" 153 | version = "0.3.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | 156 | [[package]] 157 | name = "rustc_version" 158 | version = "0.2.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 162 | ] 163 | 164 | [[package]] 165 | name = "semver" 166 | version = "0.9.0" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | dependencies = [ 169 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 170 | ] 171 | 172 | [[package]] 173 | name = "semver-parser" 174 | version = "0.7.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | 177 | [[package]] 178 | name = "syn" 179 | version = "0.15.26" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | dependencies = [ 182 | "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", 183 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 184 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 185 | ] 186 | 187 | [[package]] 188 | name = "typenum" 189 | version = "1.10.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | 192 | [[package]] 193 | name = "unicode-xid" 194 | version = "0.1.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | 197 | [[package]] 198 | name = "vcell" 199 | version = "0.1.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | 202 | [[package]] 203 | name = "volatile-register" 204 | version = "0.1.2" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | 207 | [[package]] 208 | name = "volatile-register" 209 | version = "0.2.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | dependencies = [ 212 | "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 213 | ] 214 | 215 | [[package]] 216 | name = "wasvm" 217 | version = "0.2.0" 218 | dependencies = [ 219 | "heapless 0.4.2 (git+https://github.com/japaric/heapless)", 220 | "libm 0.1.2 (git+https://github.com/kogai/libm)", 221 | ] 222 | 223 | [[package]] 224 | name = "wasvm-discovery" 225 | version = "0.1.0" 226 | dependencies = [ 227 | "alloc-cortex-m 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 228 | "cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", 229 | "cortex-m-rt 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 230 | "cortex-m-semihosting 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "wasvm 0.2.0", 233 | ] 234 | 235 | [metadata] 236 | "checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" 237 | "checksum alloc-cortex-m 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d5f7d01bc93ce089de636f946f7f1fdc5e5d751732367e019c9755440e7aef4" 238 | "checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a" 239 | "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" 240 | "checksum cortex-m 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3df5de9a9829f2ccb7defa8945fa020c6614cd2f6ba9b5f33db9241dcc01985e" 241 | "checksum cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dab2164a0fc216781a47fc343347365112ae6917421d3fa4bac6faf0fbaaaec7" 242 | "checksum cortex-m-rt 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f69d2beca37acc3776c17201c9d1f8904fb9139fa3a4d2cf28c8436a07b21a88" 243 | "checksum cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ae692573e0acccb1579fef1abf5a5bf1d2f3f0149a22b16870ec9309aee25f" 244 | "checksum cortex-m-semihosting 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1dc2abec1a772e8bb697cad17d5710f180043caf8939820f0f6ba4b7ae2a4b5" 245 | "checksum generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8107dafa78c80c848b71b60133954b4a58609a3a1a5f9af037ecc7f67280f369" 246 | "checksum hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12d790435639c06a7b798af9e1e331ae245b7ef915b92f70a39b4cf8c00686af" 247 | "checksum heapless 0.4.2 (git+https://github.com/japaric/heapless)" = "" 248 | "checksum libm 0.1.2 (git+https://github.com/kogai/libm)" = "" 249 | "checksum linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "655d57c71827fe0891ce72231b6aa5e14033dae3f604609e6a6f807267c1678d" 250 | "checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" 251 | "checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" 252 | "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" 253 | "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" 254 | "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" 255 | "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" 256 | "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" 257 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 258 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 259 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 260 | "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" 261 | "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" 262 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 263 | "checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" 264 | "checksum volatile-register 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a470889aa8f2d3ad893bd43cd90c824e63e8ac0ee5fe64c5d81a932d184fd549" 265 | "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" 266 | -------------------------------------------------------------------------------- /src/decode/section.rs: -------------------------------------------------------------------------------- 1 | use super::sec_element::Element; 2 | use super::sec_table::TableType; 3 | use super::Data; 4 | #[cfg(not(test))] 5 | use alloc::prelude::*; 6 | use alloc::string::String; 7 | use alloc::vec::Vec; 8 | use core::convert::TryFrom; 9 | use core::default::Default; 10 | use error::{Result, Trap, WasmError}; 11 | use function::{FunctionInstance, FunctionType}; 12 | use global::{GlobalInstances, GlobalType}; 13 | use memory::{Limit, MemoryInstance, MemoryInstances}; 14 | use module::{ 15 | ExternalInterface, ExternalInterfaces, ExternalModules, InternalModule, FUNCTION_DESCRIPTOR, 16 | GLOBAL_DESCRIPTOR, MEMORY_DESCRIPTOR, TABLE_DESCRIPTOR, 17 | }; 18 | use store::Store; 19 | use table::{TableInstance, TableInstances}; 20 | use value_type::ValueTypes; 21 | 22 | #[derive(Debug, PartialEq, Clone)] 23 | pub enum SectionCode { 24 | Custom, 25 | Type, 26 | Import, 27 | Function, 28 | Table, 29 | Memory, 30 | Global, 31 | Export, 32 | Start, 33 | Element, 34 | Code, 35 | Data, 36 | } 37 | 38 | impl TryFrom> for SectionCode { 39 | type Error = Trap; 40 | fn try_from(code: Option) -> core::result::Result { 41 | use self::SectionCode::*; 42 | match code { 43 | Some(0x0) => Ok(Custom), 44 | Some(0x1) => Ok(Type), 45 | Some(0x2) => Ok(Import), 46 | Some(0x3) => Ok(Function), 47 | Some(0x4) => Ok(Table), 48 | Some(0x5) => Ok(Memory), 49 | Some(0x6) => Ok(Global), 50 | Some(0x7) => Ok(Export), 51 | Some(0x8) => Ok(Start), 52 | Some(0x9) => Ok(Element), 53 | Some(0xa) => Ok(Code), 54 | Some(0xb) => Ok(Data), 55 | _ => Err(Trap::InvalidSectionId), 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub struct Module { 62 | pub(crate) function_types: Vec, 63 | pub(crate) functions: Vec, 64 | pub(crate) exports: ExternalInterfaces, 65 | pub(crate) codes: Vec, Vec)>>, 66 | pub(crate) datas: Vec, 67 | pub(crate) limits: Vec, 68 | pub(crate) tables: Vec, 69 | pub(crate) globals: Vec<(GlobalType, Vec)>, 70 | pub(crate) elements: Vec, 71 | pub(crate) customs: Vec<(String, Vec)>, 72 | pub(crate) imports: ExternalInterfaces, 73 | pub(crate) start: Option, 74 | } 75 | 76 | impl Default for Module { 77 | fn default() -> Module { 78 | Module { 79 | function_types: vec![], 80 | functions: vec![], 81 | exports: ExternalInterfaces::default(), 82 | codes: vec![], 83 | datas: vec![], 84 | limits: vec![], 85 | tables: vec![], 86 | globals: vec![], 87 | elements: vec![], 88 | customs: vec![], 89 | imports: ExternalInterfaces::default(), 90 | start: None, 91 | } 92 | } 93 | } 94 | 95 | macro_rules! impl_builder { 96 | ($name: ident, $prop: ident, $ty: ty) => { 97 | pub fn $name<'a>(&'a mut self, xs: &mut Vec<$ty>) -> &'a mut Self { 98 | self.$prop.append(xs); 99 | self 100 | } 101 | }; 102 | } 103 | 104 | impl Module { 105 | impl_builder!(function_types, function_types, FunctionType); 106 | impl_builder!(functions, functions, u32); 107 | impl_builder!(codes, codes, Result<(Vec, Vec)>); 108 | impl_builder!(datas, datas, Data); 109 | impl_builder!(limits, limits, Limit); 110 | impl_builder!(tables, tables, TableType); 111 | impl_builder!(globals, globals, (GlobalType, Vec)); 112 | impl_builder!(elements, elements, Element); 113 | impl_builder!(customs, customs, (String, Vec)); 114 | 115 | pub fn imports(&mut self, xs: ExternalInterfaces) -> &mut Self { 116 | self.imports = xs; 117 | self 118 | } 119 | 120 | pub fn exports(&mut self, xs: ExternalInterfaces) -> &mut Self { 121 | self.exports = xs; 122 | self 123 | } 124 | 125 | pub fn start(&mut self, x: u32) -> &mut Self { 126 | self.start = Some(x); 127 | self 128 | } 129 | 130 | fn validate_memory( 131 | datas: &[Data], 132 | limits: &[Limit], 133 | imports: &[ExternalInterface], 134 | external_modules: &ExternalModules, 135 | global_instances: &GlobalInstances, 136 | ) -> Result<()> { 137 | let memory_idx = 0; 138 | let external_memory_instances = imports 139 | .get(memory_idx as usize) 140 | .map(|key| external_modules.find_memory_instances(key)); 141 | 142 | if limits.get(memory_idx as usize).is_some() && external_memory_instances.is_none() { 143 | return Ok(()); 144 | } 145 | if external_memory_instances.is_some() { 146 | MemoryInstances::validate( 147 | &external_memory_instances??, 148 | &limits 149 | .get(memory_idx as usize) 150 | .map(|limit| limit.to_owned()), 151 | imports.get(memory_idx as usize)?, 152 | datas, 153 | global_instances, 154 | ) 155 | } else { 156 | Ok(()) 157 | } 158 | } 159 | 160 | fn validate_table( 161 | elements: &[Element], 162 | tables: &[TableType], 163 | imports: &[ExternalInterface], 164 | external_modules: &ExternalModules, 165 | global_instances: &GlobalInstances, 166 | function_instances: &[FunctionInstance], 167 | ) -> Result<()> { 168 | if !tables.is_empty() { 169 | tables 170 | .iter() 171 | .map(|table_type| { 172 | TableInstance::validate(elements, table_type, global_instances, function_instances) 173 | }) 174 | .collect::>>() 175 | .and_then(|_| Ok(())) 176 | } else { 177 | match imports.first() { 178 | Some(import) => external_modules 179 | .find_table_instances(import) 180 | .and_then(|table_instances| { 181 | table_instances.validate(elements, global_instances, function_instances) 182 | }), 183 | None => Ok(()), 184 | } 185 | } 186 | } 187 | 188 | fn memory_instances( 189 | datas: Vec, 190 | limits: &[Limit], 191 | exports: &ExternalInterfaces, 192 | imports: &[ExternalInterface], 193 | external_modules: &ExternalModules, 194 | global_instances: &GlobalInstances, 195 | ) -> Result { 196 | // NOTE: Currently WASM specification assumed only one memory instance; 197 | let memory_idx = 0; 198 | let export_name = exports 199 | .find_kind_by_idx(memory_idx, &MEMORY_DESCRIPTOR) 200 | .map(|x| x.name.to_owned()); 201 | 202 | let external_memory_instances = imports 203 | .get(memory_idx as usize) 204 | .map(|key| external_modules.find_memory_instances(key)); 205 | 206 | if let Some(limit) = limits.get(memory_idx as usize) { 207 | if external_memory_instances.is_none() { 208 | return Ok(MemoryInstances::new(vec![MemoryInstance::new( 209 | datas, 210 | limit.to_owned(), 211 | export_name, 212 | global_instances, 213 | )?])); 214 | } 215 | } 216 | if external_memory_instances.is_some() { 217 | MemoryInstances::from( 218 | &external_memory_instances??, 219 | limits 220 | .get(memory_idx as usize) 221 | .map(|limit| limit.to_owned()), 222 | datas, 223 | global_instances, 224 | ) 225 | } else { 226 | Ok(MemoryInstances::empty()) 227 | } 228 | } 229 | 230 | fn table_instances( 231 | elements: &[Element], 232 | tables: Vec, 233 | exports: &ExternalInterfaces, 234 | imports: &[ExternalInterface], 235 | external_modules: &ExternalModules, 236 | global_instances: &GlobalInstances, 237 | function_instances: &[FunctionInstance], 238 | ) -> Result { 239 | if !tables.is_empty() { 240 | tables 241 | .into_iter() 242 | .map(|table_type| { 243 | let export_name = exports 244 | .find_kind_by_idx(0, &TABLE_DESCRIPTOR) 245 | .map(|x| x.name.to_owned()); 246 | TableInstance::new( 247 | elements.to_vec(), 248 | table_type, 249 | export_name, 250 | global_instances, 251 | function_instances, 252 | ) 253 | }) 254 | .collect::>>() 255 | .map(TableInstances::new) 256 | } else { 257 | // NOTE: Only one table instance allowed. 258 | match imports.first() { 259 | Some(import) => external_modules 260 | .find_table_instances(import) 261 | .map(|table_instances| { 262 | table_instances.link(&elements, global_instances, function_instances)?; 263 | Ok(table_instances) 264 | })?, 265 | None => Ok(TableInstances::empty()), 266 | } 267 | } 268 | } 269 | 270 | fn function_type(idx: usize, function_types: &[FunctionType]) -> FunctionType { 271 | function_types 272 | .get(idx) 273 | .expect("Function type can't found.") 274 | .to_owned() 275 | } 276 | 277 | fn function_instances( 278 | function_types: &[FunctionType], 279 | functions: &[u32], 280 | exports: &ExternalInterfaces, 281 | codes: Vec, Vec)>>, 282 | ) -> Result> { 283 | codes 284 | .into_iter() 285 | .enumerate() 286 | .map(|(idx, code)| { 287 | let export_name = exports 288 | .find_kind_by_idx(idx as u32, &FUNCTION_DESCRIPTOR) 289 | .map(|x| x.name.to_owned()); 290 | let index_of_type = match functions.get(idx) { 291 | Some(n) => *n, 292 | None => return Err(WasmError::Trap(Trap::FunctionAndCodeInconsitent)), 293 | }; 294 | let function_type = Module::function_type(index_of_type as usize, function_types); 295 | let (expressions, locals) = code?; 296 | Ok(FunctionInstance::new( 297 | export_name, 298 | function_type, 299 | locals, 300 | expressions, 301 | )) 302 | }) 303 | .collect::>>() 304 | } 305 | 306 | fn external_function_instances( 307 | function_types: &[FunctionType], 308 | imports: &[ExternalInterface], 309 | external_modules: &ExternalModules, 310 | ) -> Result> { 311 | imports 312 | .iter() 313 | .map(|value| external_modules.find_function_instances(value, function_types)) 314 | .collect::>>() 315 | } 316 | 317 | pub fn complete( 318 | self, 319 | external_modules: &ExternalModules, 320 | store: &mut Store, 321 | ) -> Result { 322 | match self { 323 | Module { 324 | function_types, 325 | functions, 326 | codes, 327 | exports, 328 | datas, 329 | limits, 330 | tables, 331 | elements, 332 | globals, 333 | imports, 334 | start, 335 | .. 336 | } => { 337 | let grouped_imports = imports.group_by_kind()?; 338 | let imports_function = grouped_imports.get(&FUNCTION_DESCRIPTOR)?; 339 | let imports_table = grouped_imports.get(&TABLE_DESCRIPTOR)?; 340 | let imports_memory = grouped_imports.get(&MEMORY_DESCRIPTOR)?; 341 | let imports_global = grouped_imports.get(&GLOBAL_DESCRIPTOR)?; 342 | 343 | let mut internal_function_instances = 344 | Module::function_instances(&function_types, &functions, &exports, codes)?; 345 | 346 | let mut function_instances = Module::external_function_instances( 347 | &function_types, 348 | &imports_function, 349 | &external_modules, 350 | )?; 351 | 352 | function_instances.append(&mut internal_function_instances); 353 | 354 | let global_instances = GlobalInstances::new_with_external( 355 | globals, 356 | &exports, 357 | &imports_global, 358 | &external_modules, 359 | )?; 360 | 361 | // TODO: Move to context mod. 362 | let (validate_memory, validate_table) = ( 363 | Module::validate_memory( 364 | &datas, 365 | &limits, 366 | &imports_memory, 367 | &external_modules, 368 | &global_instances, 369 | ), 370 | Module::validate_table( 371 | &elements, 372 | &tables, 373 | &imports_table, 374 | &external_modules, 375 | &global_instances, 376 | &function_instances, 377 | ), 378 | ); 379 | validate_memory?; 380 | validate_table?; 381 | 382 | let memory_instances = Module::memory_instances( 383 | datas, 384 | &limits, 385 | &exports, 386 | &imports_memory, 387 | &external_modules, 388 | &global_instances, 389 | )?; 390 | 391 | let mut table_instances = Module::table_instances( 392 | &elements, 393 | tables, 394 | &exports, 395 | &imports_table, 396 | &external_modules, 397 | &global_instances, 398 | &function_instances, 399 | )?; 400 | 401 | store.function_instances = function_instances; 402 | store.function_types = function_types; 403 | store.memory_instances = memory_instances; 404 | store.table_instances = table_instances; 405 | store.global_instances = global_instances; 406 | let internal_module = InternalModule::new(exports, start); 407 | Ok(internal_module) 408 | } 409 | } 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /src/module.rs: -------------------------------------------------------------------------------- 1 | use alloc::rc::Rc; 2 | use alloc::string::String; 3 | use alloc::vec::Vec; 4 | use core::cell::RefCell; 5 | use core::convert::From; 6 | use core::default::Default; 7 | use core::fmt; 8 | use core::iter::Iterator; 9 | use core::slice::Iter; 10 | use decode::TableType; 11 | use error::{Result, Trap, WasmError}; 12 | use function::{FunctionInstance, FunctionType}; 13 | use global::{GlobalInstance, GlobalInstances, GlobalType}; 14 | use heapless::consts::{U32, U4}; 15 | use heapless::LinearMap; 16 | use indice::Indice; 17 | use memory::{Limit, MemoryInstance, MemoryInstances}; 18 | use store::Store; 19 | use table::{TableInstance, TableInstances}; 20 | 21 | #[derive(Debug, Clone)] 22 | pub enum ImportDescriptor { 23 | Function(Indice), // NOTE: Index of FunctionTypes 24 | Table(TableType), 25 | Memory(Limit), 26 | Global(GlobalType), 27 | } 28 | 29 | #[derive(Debug, Clone)] 30 | pub enum ExportDescriptor { 31 | Function(Indice), 32 | Table(Indice), 33 | Memory(Indice), 34 | Global(Indice), 35 | } 36 | 37 | impl From<(Option, u32)> for ExportDescriptor { 38 | fn from(codes: (Option, u32)) -> Self { 39 | use self::ExportDescriptor::*; 40 | match codes.0 { 41 | Some(0x00) => Function(From::from(codes.1)), 42 | Some(0x01) => Table(From::from(codes.1)), 43 | Some(0x02) => Memory(From::from(codes.1)), 44 | Some(0x03) => Global(From::from(codes.1)), 45 | x => unreachable!("Expected exports descriptor, got {:?}", x), 46 | } 47 | } 48 | } 49 | 50 | #[derive(Debug, Clone)] 51 | pub enum ModuleDescriptor { 52 | ImportDescriptor(ImportDescriptor), 53 | ExportDescriptor(ExportDescriptor), 54 | } 55 | 56 | #[derive(Eq, PartialEq, Debug, Clone, Hash)] 57 | pub enum ModuleDescriptorKind { 58 | Function, 59 | Table, 60 | Memory, 61 | Global, 62 | } 63 | 64 | pub const FUNCTION_DESCRIPTOR: ModuleDescriptorKind = ModuleDescriptorKind::Function; 65 | pub const TABLE_DESCRIPTOR: ModuleDescriptorKind = ModuleDescriptorKind::Table; 66 | pub const MEMORY_DESCRIPTOR: ModuleDescriptorKind = ModuleDescriptorKind::Memory; 67 | pub const GLOBAL_DESCRIPTOR: ModuleDescriptorKind = ModuleDescriptorKind::Global; 68 | 69 | impl From> for ModuleDescriptorKind { 70 | fn from(code: Option) -> Self { 71 | use self::ModuleDescriptorKind::*; 72 | match code { 73 | Some(0x0) => Function, 74 | Some(0x1) => Table, 75 | Some(0x2) => Memory, 76 | Some(0x3) => Global, 77 | x => unreachable!("Expected import descriptor, got {:x?}", x), 78 | } 79 | } 80 | } 81 | 82 | pub type ModuleName = Option; 83 | type Name = String; 84 | 85 | #[derive(Debug, Clone)] 86 | pub struct ExternalInterface { 87 | pub module_name: ModuleName, 88 | pub name: Name, 89 | pub descriptor: ModuleDescriptor, 90 | } 91 | 92 | impl ExternalInterface { 93 | pub fn new(module_name: ModuleName, name: Name, descriptor: ModuleDescriptor) -> Self { 94 | ExternalInterface { 95 | module_name, 96 | name, 97 | descriptor, 98 | } 99 | } 100 | } 101 | 102 | #[derive(Debug)] 103 | pub struct ExternalInterfaces(Vec); 104 | 105 | impl ExternalInterfaces { 106 | pub fn push(&mut self, value: ExternalInterface) { 107 | self.0.push(value); 108 | } 109 | 110 | pub fn len(&self) -> usize { 111 | self.0.len() 112 | } 113 | 114 | pub fn find_kind_by_idx( 115 | &self, 116 | idx: u32, 117 | kind: &ModuleDescriptorKind, 118 | ) -> Option<&ExternalInterface> { 119 | self 120 | .0 121 | .iter() 122 | .find(|ExternalInterface { descriptor, .. }| match descriptor { 123 | ModuleDescriptor::ExportDescriptor(ExportDescriptor::Function(x)) => { 124 | &FUNCTION_DESCRIPTOR == kind && x.to_u32() == idx 125 | } 126 | ModuleDescriptor::ExportDescriptor(ExportDescriptor::Table(x)) => { 127 | &TABLE_DESCRIPTOR == kind && x.to_u32() == idx 128 | } 129 | ModuleDescriptor::ExportDescriptor(ExportDescriptor::Memory(x)) => { 130 | &MEMORY_DESCRIPTOR == kind && x.to_u32() == idx 131 | } 132 | ModuleDescriptor::ExportDescriptor(ExportDescriptor::Global(x)) => { 133 | &GLOBAL_DESCRIPTOR == kind && x.to_u32() == idx 134 | } 135 | _ => unreachable!(), 136 | }) 137 | } 138 | 139 | pub fn iter(&self) -> Iter { 140 | self.0.iter() 141 | } 142 | 143 | pub fn group_by_kind( 144 | &self, 145 | ) -> Result, U4>> { 146 | let mut buf_function = vec![]; 147 | let mut buf_table = vec![]; 148 | let mut buf_memory = vec![]; 149 | let mut buf_global = vec![]; 150 | let mut buf = LinearMap::new(); 151 | 152 | for x in self.iter() { 153 | match &x.descriptor { 154 | ModuleDescriptor::ImportDescriptor(ImportDescriptor::Function(_)) => { 155 | buf_function.push(x.clone()); 156 | } 157 | ModuleDescriptor::ImportDescriptor(ImportDescriptor::Table(_)) => { 158 | buf_table.push(x.clone()); 159 | } 160 | ModuleDescriptor::ImportDescriptor(ImportDescriptor::Memory(_)) => { 161 | buf_memory.push(x.clone()); 162 | } 163 | ModuleDescriptor::ImportDescriptor(ImportDescriptor::Global(_)) => { 164 | buf_global.push(x.clone()); 165 | } 166 | y => unimplemented!("{:?}", y), 167 | }; 168 | } 169 | buf 170 | .insert(ModuleDescriptorKind::Function, buf_function) 171 | .map_err(|_| Trap::LinearMapOverflowed)?; 172 | buf 173 | .insert(ModuleDescriptorKind::Table, buf_table) 174 | .map_err(|_| Trap::LinearMapOverflowed)?; 175 | buf 176 | .insert(ModuleDescriptorKind::Memory, buf_memory) 177 | .map_err(|_| Trap::LinearMapOverflowed)?; 178 | buf 179 | .insert(ModuleDescriptorKind::Global, buf_global) 180 | .map_err(|_| Trap::LinearMapOverflowed)?; 181 | Ok(buf) 182 | } 183 | } 184 | 185 | impl Default for ExternalInterfaces { 186 | fn default() -> Self { 187 | ExternalInterfaces(vec![]) 188 | } 189 | } 190 | 191 | #[derive(Debug)] 192 | pub struct InternalModule { 193 | exports: ExternalInterfaces, 194 | pub start: Option, 195 | } 196 | 197 | impl InternalModule { 198 | pub fn new(exports: ExternalInterfaces, start: Option) -> Self { 199 | InternalModule { 200 | exports, 201 | start: start.map(Indice::from), 202 | } 203 | } 204 | 205 | pub fn get_export_by_key(&self, invoke: &str) -> Option<&ExternalInterface> { 206 | self.exports.0.iter().find(|x| x.name == invoke) 207 | } 208 | } 209 | 210 | #[derive(Debug, Clone)] 211 | pub struct ExternalModule { 212 | pub function_instances: Vec, 213 | function_types: Vec, 214 | pub(crate) memory_instances: MemoryInstances, 215 | table_instances: TableInstances, 216 | global_instances: GlobalInstances, 217 | } 218 | 219 | impl ExternalModule { 220 | pub fn new( 221 | function_instances: Vec, 222 | function_types: Vec, 223 | memory_instances: Vec, 224 | table_instances: Vec, 225 | global_instances: Vec, 226 | ) -> Self { 227 | ExternalModule { 228 | function_instances, 229 | function_types, 230 | memory_instances: MemoryInstances::new(memory_instances), 231 | table_instances: TableInstances::new(table_instances), 232 | global_instances: GlobalInstances::new(global_instances), 233 | } 234 | } 235 | 236 | // FIXME: Consider to rename import-function-instance 237 | fn find_function_instance( 238 | &self, 239 | key: &ExternalInterface, 240 | function_types: &[FunctionType], 241 | ) -> Result { 242 | match key { 243 | ExternalInterface { 244 | descriptor: ModuleDescriptor::ImportDescriptor(ImportDescriptor::Function(idx)), 245 | name, 246 | module_name, 247 | } => { 248 | let expected_type = function_types.get(idx.to_usize())?; 249 | let instance = self 250 | .function_instances 251 | .iter() 252 | .find(|instance| instance.is_same_name(name)) 253 | .ok_or(Trap::UnknownImport) 254 | .map(|x| x.clone())?; 255 | 256 | instance 257 | .validate_type(expected_type) 258 | .map_err(|_| Trap::IncompatibleImportType)?; 259 | 260 | instance.set_source_module_name(module_name); 261 | Ok(instance) 262 | } 263 | x => unreachable!("Expected function descriptor, got {:?}", x), 264 | } 265 | } 266 | 267 | fn find_table_instance( 268 | &self, 269 | key: &ExternalInterface, // import section of table 270 | ) -> Result { 271 | match key { 272 | ExternalInterface { 273 | descriptor: ModuleDescriptor::ImportDescriptor(ImportDescriptor::Table(table_type)), 274 | name, 275 | .. 276 | } => { 277 | if !self.table_instances.find_by_name(name) { 278 | return Err(WasmError::Trap(Trap::UnknownImport)); 279 | } 280 | if self.table_instances.gt_table_type(table_type) { 281 | return Err(WasmError::Trap(Trap::IncompatibleImportType)); 282 | } 283 | Ok(self.table_instances.clone()) 284 | } 285 | x => unreachable!("Expected table descriptor, got {:?}", x), 286 | } 287 | } 288 | } 289 | 290 | impl Default for ExternalModule { 291 | fn default() -> Self { 292 | ExternalModule { 293 | function_instances: vec![], 294 | function_types: vec![], 295 | memory_instances: MemoryInstances::empty(), 296 | table_instances: TableInstances::empty(), 297 | global_instances: GlobalInstances::empty(), 298 | } 299 | } 300 | } 301 | 302 | impl From<&Store> for ExternalModule { 303 | fn from(store: &Store) -> Self { 304 | ExternalModule { 305 | function_instances: store.function_instances.clone(), 306 | function_types: store.function_types.clone(), 307 | memory_instances: store.memory_instances.clone(), 308 | table_instances: store.table_instances.clone(), 309 | global_instances: store.global_instances.clone(), 310 | } 311 | } 312 | } 313 | 314 | #[derive(Clone)] 315 | pub struct ExternalModules(Rc>>); 316 | 317 | impl fmt::Debug for ExternalModules { 318 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 319 | f.debug_map() 320 | .entries(self.0.borrow().iter().map(|(k, v)| (k, v))) 321 | .finish() 322 | } 323 | } 324 | 325 | impl Default for ExternalModules { 326 | fn default() -> Self { 327 | ExternalModules(Rc::new(RefCell::new(LinearMap::new()))) 328 | } 329 | } 330 | 331 | impl ExternalModules { 332 | pub fn get(&self, module_name: &ModuleName) -> Option { 333 | self.0.borrow().get(module_name).cloned() 334 | } 335 | 336 | pub fn register_module(&mut self, key: ModuleName, value: ExternalModule) -> Result<()> { 337 | self 338 | .0 339 | .borrow_mut() 340 | .insert(key, value) 341 | .map_err(|_| Trap::LinearMapOverflowed)?; 342 | Ok(()) 343 | } 344 | 345 | pub fn get_table_instance( 346 | &self, 347 | module_name: &ModuleName, 348 | idx: &Indice, 349 | ) -> Result { 350 | self 351 | .0 352 | .borrow() 353 | .get(module_name) 354 | .ok_or(Trap::UnknownImport)? 355 | .table_instances 356 | .get_table_at(idx) 357 | .ok_or(WasmError::Trap(Trap::Notfound)) 358 | } 359 | 360 | pub fn get_function_type(&self, module_name: &ModuleName, idx: u32) -> Result { 361 | self 362 | .0 363 | .borrow() 364 | .get(module_name) 365 | .ok_or(WasmError::Trap(Trap::UnknownImport))? 366 | .function_types 367 | .get(idx as usize) 368 | .ok_or(WasmError::Trap(Trap::Notfound)) 369 | .map(|x| x.clone()) 370 | } 371 | 372 | pub fn get_function_instance( 373 | &self, 374 | module_name: &ModuleName, 375 | idx: usize, 376 | ) -> Result { 377 | self 378 | .0 379 | .borrow() 380 | .get(module_name) 381 | .ok_or(Trap::UnknownImport)? 382 | .function_instances 383 | .get(idx) 384 | .cloned() 385 | .ok_or(WasmError::Trap(Trap::Notfound)) 386 | } 387 | 388 | pub fn find_function_instances( 389 | &self, 390 | import: &ExternalInterface, 391 | function_types: &[FunctionType], 392 | ) -> Result { 393 | self 394 | .0 395 | .borrow() 396 | .get(&import.module_name) 397 | .ok_or(Trap::UnknownImport)? 398 | .find_function_instance(import, function_types) 399 | } 400 | 401 | pub fn find_memory_instances(&self, import: &ExternalInterface) -> Result { 402 | self 403 | .0 404 | .borrow() 405 | .get(&import.module_name) 406 | .ok_or(WasmError::Trap(Trap::UnknownImport)) 407 | .map(|x| x.memory_instances.clone()) 408 | } 409 | 410 | pub fn find_table_instances(&self, import: &ExternalInterface) -> Result { 411 | self 412 | .0 413 | .borrow() 414 | .get(&import.module_name) 415 | .ok_or(Trap::UnknownImport)? 416 | .find_table_instance(import) 417 | } 418 | 419 | pub fn find_global_instances(&self, module_name: &ModuleName) -> Result { 420 | self 421 | .0 422 | .borrow() 423 | .get(module_name) 424 | .ok_or(WasmError::Trap(Trap::UnknownImport)) 425 | .map(|x| x.global_instances.clone()) 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | use alloc::prelude::*; 3 | use alloc::rc::Rc; 4 | use alloc::string::String; 5 | use alloc::vec::Vec; 6 | use core::cell::RefCell; 7 | use core::cmp::{Ordering, PartialOrd}; 8 | use core::fmt; 9 | use core::mem::transmute; 10 | use core::u32; 11 | use decode::Data; 12 | use error::{Result, Trap, WasmError}; 13 | use global::GlobalInstances; 14 | use isa::Isa; 15 | use module::{ExternalInterface, ImportDescriptor, ModuleDescriptor}; 16 | use value::Values; 17 | 18 | // NOTE: 65536(64KiB) is constant data size per page. 19 | const PAGE_SIZE: u32 = 65536; 20 | 21 | // Prefer to rename MemoryType 22 | #[derive(Clone, PartialEq)] 23 | pub enum Limit { 24 | // (min) 25 | NoUpperLimit(u32), 26 | // (min, max) 27 | HasUpperLimit(u32, u32), 28 | } 29 | 30 | impl Limit { 31 | fn initial_min_size(&self) -> usize { 32 | let min_size = match self { 33 | Limit::NoUpperLimit(min) => min, 34 | Limit::HasUpperLimit(min, _) => min, 35 | }; 36 | (PAGE_SIZE * min_size) as usize 37 | } 38 | } 39 | impl PartialOrd for Limit { 40 | fn partial_cmp(&self, other: &Self) -> Option { 41 | use self::Limit::*; 42 | match (self, other) { 43 | // NOTE: In the mean of Limit, to compare min bound `A.min < B.min` represents self > other. 44 | // However, `A.max < B.max` self < other. 45 | // It seems the specification assumes to compare range of limitations. 46 | // 47 | // Example: in case of `A < B`. 48 | // +---+---+---+---+---+---+---+---+---+---+ 49 | // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 50 | // +---+---+---+---+---+---+---+---+---+---+ 51 | // ^ ^ ^ ^ 52 | // B.min A.min ----- A.max B.max 53 | (NoUpperLimit(min), NoUpperLimit(min_other)) => { 54 | if min > min_other { 55 | Some(Ordering::Less) 56 | } else if min == min_other { 57 | Some(Ordering::Equal) 58 | } else { 59 | Some(Ordering::Greater) 60 | } 61 | } 62 | (NoUpperLimit(min), HasUpperLimit(min_other, _)) => { 63 | if min > min_other { 64 | Some(Ordering::Less) 65 | } else { 66 | Some(Ordering::Greater) 67 | } 68 | } 69 | (HasUpperLimit(min, _), NoUpperLimit(min_other)) => { 70 | if min < min_other { 71 | Some(Ordering::Greater) 72 | } else { 73 | Some(Ordering::Less) 74 | } 75 | } 76 | (HasUpperLimit(min, max), HasUpperLimit(min_other, max_other)) => { 77 | if min > min_other || max < max_other { 78 | return Some(Ordering::Less); 79 | } 80 | if min < min_other || max > max_other { 81 | return Some(Ordering::Greater); 82 | } 83 | Some(Ordering::Equal) 84 | } 85 | } 86 | } 87 | } 88 | 89 | impl fmt::Debug for Limit { 90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 91 | use self::Limit::*; 92 | match self { 93 | NoUpperLimit(min) => f 94 | .debug_tuple("Limit") 95 | .field(&format!("min:{}", min)) 96 | .finish(), 97 | HasUpperLimit(min, max) => f 98 | .debug_tuple("Limit") 99 | .field(&format!("min:{},max:{}", min, max)) 100 | .finish(), 101 | } 102 | } 103 | } 104 | 105 | #[derive(Clone)] 106 | pub struct MemoryInstance { 107 | data: Vec, 108 | limit: Limit, 109 | export_name: Option, 110 | surface_size: u32, 111 | } 112 | 113 | macro_rules! impl_load_data { 114 | ($name: ident, $ty: ty, $conv_fn: path) => { 115 | pub fn $name(&mut self, from: u32, to: u32) -> $ty { 116 | if (to as usize) > self.data.len() { 117 | self.data.resize(to as usize, 0); 118 | }; 119 | let data = &self.data[(from as usize)..(to as usize)]; 120 | let mut bit_buf: $ty = 0; 121 | for (idx, d) in data.iter().enumerate() { 122 | let bits = $conv_fn(*d) << idx * 8; 123 | bit_buf ^= bits; 124 | } 125 | bit_buf 126 | } 127 | }; 128 | } 129 | 130 | macro_rules! impl_store_data { 131 | ($name: ident, $length: expr, $ty: ty) => { 132 | fn $name (&mut self, v: $ty, from: u32, to: u32) { 133 | let bytes: [u8; $length] = unsafe { transmute(v) }; 134 | let data: &mut Vec = self.data.as_mut(); 135 | MemoryInstance::allocate(data, &bytes[0..(to - from) as usize], from as usize); 136 | } 137 | }; 138 | } 139 | 140 | impl MemoryInstance { 141 | impl_load_data!(load_data_32, u32, u32::from); 142 | impl_load_data!(load_data_64, u64, u64::from); 143 | 144 | fn allocate(data: &mut Vec, allocatable: &[u8], offset: usize) { 145 | let end = offset + allocatable.len(); 146 | if end > data.len() { 147 | data.resize(end, 0); 148 | } 149 | data[offset..end].copy_from_slice(allocatable); 150 | } 151 | 152 | pub fn new( 153 | datas: Vec, 154 | limit: Limit, 155 | export_name: Option, 156 | global_instances: &GlobalInstances, 157 | ) -> Result { 158 | let initial_size = limit.initial_min_size(); 159 | let mut data = Vec::new(); 160 | for Data { offset, init, .. } in datas.into_iter() { 161 | let offset = Isa::constant_expression(&offset, global_instances)?; 162 | let size = offset + init.len(); 163 | if size > initial_size { 164 | return Err(WasmError::Trap(Trap::DataSegmentDoesNotFit)); 165 | } 166 | MemoryInstance::allocate(&mut data, &init, offset); 167 | } 168 | 169 | Ok(MemoryInstance { 170 | data, 171 | limit, 172 | export_name, 173 | surface_size: initial_size as u32, 174 | }) 175 | } 176 | 177 | fn link( 178 | &mut self, 179 | datas: Vec, 180 | limit: Option, 181 | global_instances: &GlobalInstances, 182 | ) -> Result<()> { 183 | if let Some(limit) = limit { 184 | self.limit = limit; 185 | }; 186 | let data: &mut Vec = self.data.as_mut(); 187 | for Data { offset, init, .. } in datas.into_iter() { 188 | let offset = Isa::constant_expression(&offset, global_instances)?; 189 | MemoryInstance::allocate(data, &init, offset); 190 | } 191 | Ok(()) 192 | } 193 | 194 | fn validate( 195 | &mut self, 196 | datas: &[Data], 197 | limit: &Option, 198 | global_instances: &GlobalInstances, 199 | ) -> Result<()> { 200 | let initial_size = match limit { 201 | Some(limit) => limit.initial_min_size(), 202 | None => self.limit.initial_min_size(), 203 | }; 204 | for Data { offset, init, .. } in datas.iter() { 205 | let offset = Isa::constant_expression(&offset, global_instances)?; 206 | let size = offset + init.len(); 207 | if size > initial_size { 208 | return Err(WasmError::Trap(Trap::DataSegmentDoesNotFit)); 209 | } 210 | } 211 | Ok(()) 212 | } 213 | 214 | fn data_size(&self) -> u32 { 215 | self.surface_size 216 | } 217 | 218 | pub fn data_size_smaller_than(&self, ptr: u32) -> bool { 219 | ptr > self.data_size() 220 | } 221 | 222 | pub fn size_by_pages(&self) -> u32 { 223 | self.data_size() / PAGE_SIZE 224 | } 225 | 226 | pub fn memory_grow(&mut self, increase_page: u32) -> Result<()> { 227 | match self.limit { 228 | Limit::HasUpperLimit(_, max) if self.size_by_pages() + increase_page > max => { 229 | Err(WasmError::Trap(Trap::FailToGrow)) 230 | } 231 | _ => { 232 | let current_size = self.data_size(); 233 | match increase_page.checked_mul(PAGE_SIZE) { 234 | Some(growing_size) => match current_size.checked_add(growing_size) { 235 | Some(next_size) => { 236 | self.surface_size = next_size; 237 | Ok(()) 238 | } 239 | None => Err(WasmError::Trap(Trap::FailToGrow)), 240 | }, 241 | None => Err(WasmError::Trap(Trap::FailToGrow)), 242 | } 243 | } 244 | } 245 | } 246 | 247 | pub fn load_data_f32(&mut self, from: u32, to: u32) -> f32 { 248 | f32::from_bits(self.load_data_32(from, to)) 249 | } 250 | 251 | pub fn load_data_f64(&mut self, from: u32, to: u32) -> f64 { 252 | f64::from_bits(self.load_data_64(from, to)) 253 | } 254 | 255 | impl_store_data!(store_data_i32, 4, i32); 256 | impl_store_data!(store_data_f32, 4, f32); 257 | impl_store_data!(store_data_i64, 8, i64); 258 | impl_store_data!(store_data_f64, 8, f64); 259 | 260 | pub fn store_data(&mut self, from: u32, to: u32, value: &Values) { 261 | match value { 262 | Values::I32(v) => self.store_data_i32(*v, from, to), 263 | Values::F32(v) => self.store_data_f32(*v, from, to), 264 | Values::I64(v) => self.store_data_i64(*v, from, to), 265 | Values::F64(v) => self.store_data_f64(*v, from, to), 266 | }; 267 | } 268 | 269 | pub fn limit_gt(&self, other_limit: &Limit) -> bool { 270 | &self.limit > other_limit 271 | } 272 | } 273 | 274 | impl fmt::Debug for MemoryInstance { 275 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 276 | f.debug_struct("MemoryInstance") 277 | .field("export_name", &self.export_name) 278 | .field( 279 | "data", 280 | &self 281 | .data 282 | .iter() 283 | .filter(|d| d != &&0) 284 | .map(|d| format!("{}", d)) 285 | .collect::>() 286 | .join(", "), 287 | ) 288 | .field("data.len()", &self.data.len()) 289 | .field("limit", &self.limit) 290 | .finish() 291 | } 292 | } 293 | 294 | #[derive(Debug, Clone)] 295 | pub struct MemoryInstances(Rc>>); 296 | 297 | impl MemoryInstances { 298 | pub fn new(memory_instances: Vec) -> Self { 299 | MemoryInstances(Rc::new(RefCell::new(memory_instances))) 300 | } 301 | 302 | pub fn empty() -> Self { 303 | MemoryInstances(Rc::new(RefCell::new(vec![]))) 304 | } 305 | 306 | pub fn from( 307 | that: &MemoryInstances, 308 | limit: Option, 309 | datas: Vec, 310 | global_instances: &GlobalInstances, 311 | ) -> Result { 312 | that 313 | .0 314 | .borrow_mut() 315 | .get_mut(0) 316 | .ok_or(Trap::UnknownImport)? 317 | .link(datas, limit, global_instances)?; 318 | Ok(that.clone()) 319 | } 320 | 321 | pub fn validate( 322 | that: &MemoryInstances, 323 | limit: &Option, 324 | import: &ExternalInterface, 325 | datas: &[Data], 326 | global_instances: &GlobalInstances, 327 | ) -> Result<()> { 328 | that 329 | .0 330 | .borrow_mut() 331 | .get_mut(0) 332 | .ok_or(Trap::UnknownImport) 333 | .and_then(|instance| { 334 | if instance.export_name.as_ref() != Some(&import.name) { 335 | Err(Trap::UnknownImport) 336 | } else { 337 | Ok(instance) 338 | } 339 | }) 340 | .and_then(|instance| match import { 341 | ExternalInterface { 342 | descriptor: ModuleDescriptor::ImportDescriptor(ImportDescriptor::Memory(limit)), 343 | .. 344 | } => { 345 | if instance.limit_gt(limit) { 346 | Err(Trap::IncompatibleImportType) 347 | } else { 348 | Ok(instance) 349 | } 350 | } 351 | x => unreachable!("Expected memory descriptor, got {:?}", x), 352 | })? 353 | .validate(datas, limit, global_instances) 354 | } 355 | 356 | pub fn data_size_small_than(&self, ptr: u32) -> bool { 357 | self 358 | .0 359 | .borrow() 360 | .get(0) 361 | .expect("At least one memory instance expected") 362 | .data_size_smaller_than(ptr) 363 | } 364 | 365 | pub fn load_data_32(&self, from: u32, to: u32) -> u32 { 366 | self 367 | .0 368 | .borrow_mut() 369 | .get_mut(0) 370 | .expect("At least one memory instance expected") 371 | .load_data_32(from, to) 372 | } 373 | 374 | pub fn load_data_64(&self, from: u32, to: u32) -> u64 { 375 | self 376 | .0 377 | .borrow_mut() 378 | .get_mut(0) 379 | .expect("At least one memory instance expected") 380 | .load_data_64(from, to) 381 | } 382 | 383 | pub fn load_data_f32(&self, from: u32, to: u32) -> f32 { 384 | self 385 | .0 386 | .borrow_mut() 387 | .get_mut(0) 388 | .expect("At least one memory instance expected") 389 | .load_data_f32(from, to) 390 | } 391 | 392 | pub fn load_data_f64(&self, from: u32, to: u32) -> f64 { 393 | self 394 | .0 395 | .borrow_mut() 396 | .get_mut(0) 397 | .expect("At least one memory instance expected") 398 | .load_data_f64(from, to) 399 | } 400 | 401 | pub fn size_by_pages(&self) -> u32 { 402 | self 403 | .0 404 | .borrow() 405 | .get(0) 406 | .expect("At least one memory instance expected") 407 | .size_by_pages() 408 | } 409 | 410 | pub fn store_data(&self, from: u32, to: u32, value: &Values) { 411 | self 412 | .0 413 | .borrow_mut() 414 | .get_mut(0) 415 | .expect("At least one memory instance expected") 416 | .store_data(from, to, value) 417 | } 418 | 419 | pub fn memory_grow(&self, increase_page: u32) -> Result<()> { 420 | self 421 | .0 422 | .borrow_mut() 423 | .get_mut(0) 424 | .expect("At least one memory instance expected") 425 | .memory_grow(increase_page) 426 | } 427 | 428 | pub fn clone_instance_by_name(&self, name: &str) -> Option { 429 | let instance = self.0.borrow().get(0)?.clone(); 430 | if instance.export_name == Some(name.to_owned()) { 431 | Some(instance) 432 | } else { 433 | None 434 | } 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /tests/run.rs: -------------------------------------------------------------------------------- 1 | extern crate wabt; 2 | 3 | #[cfg(test)] 4 | extern crate wasvm; 5 | use std::cell::RefCell; 6 | use std::collections::HashMap; 7 | use std::fs::File; 8 | use std::io::Read; 9 | use std::rc::Rc; 10 | use std::{f32, f64}; 11 | use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value}; 12 | use wasvm::{ 13 | create_spectest, decode_module, init_store, instantiate_module, validate_module, ExternalModules, 14 | ModuleInstance, Trap, Values, WasmError, 15 | }; 16 | 17 | fn get_args(args: &[Value]) -> Vec { 18 | args 19 | .iter() 20 | .map(|v| match v { 21 | Value::I32(value) => Values::I32(*value), 22 | Value::I64(value) => Values::I64(*value), 23 | Value::F32(value) => Values::F32(*value), 24 | Value::F64(value) => Values::F64(*value), 25 | }) 26 | .collect() 27 | } 28 | 29 | fn get_expectation(expected: &[Value]) -> Values { 30 | match expected.get(0) { 31 | Some(Value::I32(v)) => Values::I32(*v), 32 | Some(Value::I64(v)) => Values::I64(*v), 33 | Some(Value::F32(v)) => Values::F32(*v), 34 | Some(Value::F64(v)) => Values::F64(*v), 35 | None => Values::I32(0), 36 | } 37 | } 38 | 39 | struct E2ETest<'a> { 40 | parser: ScriptParser, 41 | modules: HashMap, Rc>>, 42 | external_modules: ExternalModules, 43 | file_name: &'a str, 44 | } 45 | 46 | impl<'a> E2ETest<'a> { 47 | fn new(file_name: &'a str) -> Self { 48 | let mut buf = String::new(); 49 | let test_filename = format!("./testsuite/{}.wast", file_name); 50 | let mut json = File::open(&test_filename).unwrap(); 51 | json.read_to_string(&mut buf).unwrap(); 52 | let parser = ScriptParser::from_str(&buf).unwrap(); 53 | let modules = HashMap::new(); 54 | let mut external_modules = ExternalModules::default(); 55 | external_modules 56 | .register_module(Some("spectest".to_owned()), create_spectest()) 57 | .unwrap(); 58 | 59 | E2ETest { 60 | parser, 61 | modules, 62 | external_modules, 63 | file_name, 64 | } 65 | } 66 | 67 | fn do_instantiate(&mut self, module: &ModuleBinary, name: &Option) { 68 | let bytes = module.clone().into_vec(); 69 | let store = init_store(); 70 | let section = decode_module(&bytes); 71 | let vm_ref = Rc::new(RefCell::new( 72 | instantiate_module(store, section, self.external_modules.clone(), 65536).unwrap(), 73 | )); 74 | self.modules.insert(None, vm_ref.clone()); 75 | self.modules.insert(name.clone(), vm_ref.clone()); 76 | } 77 | 78 | fn do_action(&mut self, field: &str, args: &[Value], module: &Option, line: u64) { 79 | println!("Perform action at {}:{}.", field, line); 80 | let vm_ref: Rc> = self.modules[module].clone(); 81 | let mut vm = vm_ref.borrow_mut(); 82 | vm.run(field, get_args(args)).unwrap(); 83 | } 84 | 85 | fn do_register(&mut self, name: &Option, as_name: &str) { 86 | println!( 87 | "Register importable module, key={:?} import_name={}.", 88 | name, as_name 89 | ); 90 | let vm_ref: Rc> = self.modules[name].clone(); 91 | let vm = vm_ref.borrow(); 92 | let importable_module = vm.export_module(); 93 | self 94 | .external_modules 95 | .register_module(Some(as_name.to_owned()), importable_module) 96 | .unwrap(); 97 | } 98 | 99 | fn assert_return(&self, action: &Action, expected: &[Value], line: u64) { 100 | let (field, args, module) = match action { 101 | Action::Invoke { 102 | ref field, 103 | ref args, 104 | ref module, 105 | } => (field, get_args(args), module), 106 | Action::Get { 107 | ref field, 108 | ref module, 109 | } => (field, vec![], module), 110 | }; 111 | println!("Assert return at {}:{}.", field, line); 112 | let vm_ref: Rc> = self.modules[module].clone(); 113 | let mut vm = vm_ref.borrow_mut(); 114 | let actual = vm.run(field.as_ref(), args).unwrap(); 115 | let expectation = get_expectation(expected); 116 | match actual { 117 | Values::F32(n) if n.is_nan() => match expectation { 118 | Values::F32(m) => assert!(m.is_nan()), 119 | _ => unreachable!(), 120 | }, 121 | Values::F64(n) if n.is_nan() => match expectation { 122 | Values::F64(m) => assert!(m.is_nan()), 123 | _ => unreachable!(), 124 | }, 125 | _ => assert_eq!(actual, expectation), 126 | }; 127 | } 128 | fn assert_trap(&mut self, action: &Action, _message: &str, line: u64) { 129 | match action { 130 | Action::Invoke { 131 | ref field, 132 | ref args, 133 | ref module, 134 | } => { 135 | println!("Assert trap at {}:{}.", field, line,); 136 | let vm_ref: Rc> = self.modules[module].clone(); 137 | let mut vm = vm_ref.borrow_mut(); 138 | vm.run(field, get_args(args)).unwrap_err(); 139 | } 140 | x => unreachable!("{:?}", x), 141 | } 142 | } 143 | 144 | fn assert_uninstantiable(&mut self, module: &ModuleBinary, _message: &str, line: u64) { 145 | println!("Assert uninstantiable at line:{}.", line); 146 | let bytes = module.clone().into_vec(); 147 | let store = init_store(); 148 | let module = decode_module(&bytes); 149 | instantiate_module(store, module, Default::default(), 65536).unwrap_err(); 150 | } 151 | 152 | fn assert_malformed(&self, module: &ModuleBinary, _message: &str, line: u64) { 153 | if (self.file_name == "custom_section" && line == 77) 154 | || (self.file_name == "custom_section" && line == 94) 155 | || (self.file_name == "custom" && line == 85) 156 | { 157 | println!("Skip {}, it seems can't resolvable yet...", line); 158 | return; 159 | }; 160 | let bytes = module.clone().into_vec(); 161 | let store = init_store(); 162 | let module = decode_module(&bytes); 163 | let err = instantiate_module(store, module, Default::default(), 65536).unwrap_err(); 164 | if let WasmError::Trap(Trap::UnsupportedTextform) = err { 165 | println!("Skip malformed text form at line:{}.", line); 166 | return; 167 | }; 168 | println!("Assert malformed at {}.", line,); 169 | } 170 | 171 | fn assert_invalid(&self, message: &str, module: &ModuleBinary, line: u64) { 172 | println!("Assert invalid at {}:{}.", message, line); 173 | let bytes = module.clone().into_vec(); 174 | let section = decode_module(&bytes); 175 | validate_module(§ion).unwrap_err(); 176 | } 177 | 178 | fn assert_nan(&self, action: &Action, line: u64) { 179 | match action { 180 | Action::Invoke { 181 | ref field, 182 | ref args, 183 | ref module, 184 | } => { 185 | println!("Assert NaN at '{}:{}'.", field, line); 186 | let vm_ref: Rc> = self.modules[module].clone(); 187 | let mut vm = vm_ref.borrow_mut(); 188 | let actual = vm.run(field.as_ref(), get_args(args)).unwrap(); 189 | match actual { 190 | Values::F32(n) => assert!(n.is_nan()), 191 | Values::F64(n) => assert!(n.is_nan()), 192 | _ => unreachable!(), 193 | }; 194 | } 195 | x => unreachable!("{:?}", x), 196 | } 197 | } 198 | 199 | fn assert_unlinkable(&self, module: &ModuleBinary, _message: &str, line: u64) { 200 | let bytes = module.clone().into_vec(); 201 | let tmp_bytes = bytes.clone(); 202 | let (magic_numbers, _) = tmp_bytes.split_at(8); 203 | if magic_numbers == [0u8, 97, 115, 109, 1, 0, 0, 0] { 204 | println!("Assert unlinkable at {}.", line,); 205 | let store = init_store(); 206 | let section = decode_module(&bytes); 207 | instantiate_module(store, section, self.external_modules.clone(), 65536).unwrap_err(); 208 | } else { 209 | println!("Skip unlinkable text form at line:{}.", line); 210 | }; 211 | } 212 | 213 | fn do_test(&mut self) { 214 | while let Ok(Some(Command { kind, line, .. })) = self.parser.next() { 215 | match kind { 216 | CommandKind::Module { 217 | ref module, 218 | ref name, 219 | } => self.do_instantiate(module, name), 220 | CommandKind::PerformAction(Action::Invoke { 221 | ref field, 222 | ref args, 223 | ref module, 224 | }) => self.do_action(field, args, module, line), 225 | CommandKind::Register { 226 | ref name, 227 | ref as_name, 228 | .. 229 | } => self.do_register(name, as_name), 230 | CommandKind::AssertReturn { 231 | ref action, 232 | ref expected, 233 | } => self.assert_return(action, expected, line), 234 | CommandKind::AssertTrap { 235 | ref action, 236 | ref message, 237 | } => self.assert_trap(action, message, line), 238 | CommandKind::AssertUninstantiable { 239 | ref module, 240 | ref message, 241 | } => self.assert_uninstantiable(module, message, line), 242 | CommandKind::AssertMalformed { 243 | ref module, 244 | ref message, 245 | } => self.assert_malformed(module, message, line), 246 | CommandKind::AssertReturnCanonicalNan { ref action } => self.assert_nan(action, line), 247 | CommandKind::AssertReturnArithmeticNan { ref action } => self.assert_nan(action, line), 248 | CommandKind::AssertUnlinkable { 249 | ref module, 250 | ref message, 251 | } => self.assert_unlinkable(module, message, line), 252 | // FIXME: Enable specs 253 | CommandKind::AssertExhaustion { 254 | action: Action::Invoke { ref field, .. }, 255 | } => println!("Skip exhaustion line:{}:{}.", field, line), 256 | CommandKind::AssertInvalid { 257 | ref message, 258 | ref module, 259 | } => self.assert_invalid(message, module, line), 260 | x => unreachable!( 261 | "there are no other commands apart from that defined above {:?}", 262 | x 263 | ), 264 | } 265 | } 266 | } 267 | } 268 | 269 | macro_rules! impl_e2e { 270 | ($test_name: ident, $file_name: expr) => { 271 | #[test] 272 | fn $test_name() { 273 | let mut t = E2ETest::new($file_name); 274 | t.do_test() 275 | } 276 | }; 277 | } 278 | 279 | // NOTE: Convient to debug wast specs. 280 | // impl_e2e!(test_sandbox, "sandbox"); 281 | 282 | impl_e2e!(test_address, "address"); 283 | impl_e2e!(test_align, "align"); 284 | // impl_e2e!(test_binary, "binary"); 285 | impl_e2e!(test_block, "block"); 286 | impl_e2e!(test_br_if, "br_if"); 287 | impl_e2e!(test_br_table, "br_table"); 288 | impl_e2e!(test_br_only, "br"); 289 | impl_e2e!(test_break_drop, "break-drop"); 290 | impl_e2e!(test_call_indirect, "call_indirect"); 291 | impl_e2e!(test_call, "call"); 292 | impl_e2e!(test_comments, "comments"); 293 | impl_e2e!(test_const, "const"); /* All specs suppose Text-format */ 294 | impl_e2e!(test_conversions, "conversions"); 295 | impl_e2e!(test_custom_section, "custom_section"); 296 | impl_e2e!(test_custom_simple, "custom"); 297 | impl_e2e!(test_data, "data"); 298 | impl_e2e!(test_elem, "elem"); 299 | impl_e2e!(test_endianness, "endianness"); 300 | impl_e2e!(test_exports, "exports"); 301 | impl_e2e!(test_f32_bitwise, "f32_bitwise"); 302 | impl_e2e!(test_f32_cmp, "f32_cmp"); 303 | impl_e2e!(test_f32, "f32"); 304 | impl_e2e!(test_f64_bitwise, "f64_bitwise"); 305 | impl_e2e!(test_f64_cmp, "f64_cmp"); 306 | impl_e2e!(test_f64, "f64"); 307 | impl_e2e!(test_fac, "fac"); 308 | impl_e2e!(test_float_exprs, "float_exprs"); 309 | impl_e2e!(test_float_literals, "float_literals"); 310 | impl_e2e!(test_float_memory, "float_memory"); 311 | impl_e2e!(test_float_misc, "float_misc"); 312 | impl_e2e!(test_forward, "forward"); 313 | impl_e2e!(test_func_ptrs, "func_ptrs"); 314 | impl_e2e!(test_func, "func"); 315 | impl_e2e!(test_get_local, "get_local"); 316 | impl_e2e!(test_globals, "globals"); 317 | impl_e2e!(test_i32, "i32"); 318 | impl_e2e!(test_i64, "i64"); 319 | impl_e2e!(test_if, "if"); 320 | impl_e2e!(test_imports, "imports"); 321 | impl_e2e!(test_inline_module, "inline-module"); 322 | impl_e2e!(test_int_exprs, "int_exprs"); 323 | impl_e2e!(test_int_literals, "int_literals"); 324 | impl_e2e!(test_labels, "labels"); 325 | impl_e2e!(test_left_to_right, "left-to-right"); 326 | impl_e2e!(test_linking, "linking"); 327 | impl_e2e!(test_loop, "loop"); 328 | impl_e2e!(test_memory_grow, "memory_grow"); 329 | impl_e2e!(test_memory_redundancy, "memory_redundancy"); 330 | impl_e2e!(test_memory_trap, "memory_trap"); 331 | impl_e2e!(test_memory_only, "memory"); 332 | impl_e2e!(test_names, "names"); 333 | impl_e2e!(test_nop, "nop"); 334 | impl_e2e!(test_resizing, "resizing"); 335 | impl_e2e!(test_return, "return"); 336 | impl_e2e!(test_select, "select"); 337 | impl_e2e!(test_set_local, "set_local"); 338 | impl_e2e!(test_skip_stack_guard_page, "skip-stack-guard-page"); 339 | impl_e2e!(test_stack, "stack"); 340 | impl_e2e!(test_start, "start"); 341 | impl_e2e!(test_store_retval, "store_retval"); 342 | impl_e2e!(test_switch, "switch"); 343 | impl_e2e!(test_tee_local, "tee_local"); 344 | impl_e2e!(test_token, "token"); 345 | impl_e2e!(test_traps, "traps"); 346 | impl_e2e!(test_type, "type"); 347 | impl_e2e!(test_typecheck, "typecheck"); 348 | impl_e2e!(test_unreachable, "unreachable"); 349 | impl_e2e!(test_unreached_invalid, "unreached-invalid"); 350 | impl_e2e!(test_unwind, "unwind"); 351 | impl_e2e!(test_utf8_custom_section_id, "utf8-custom-section-id"); 352 | impl_e2e!(test_utf8_import_field, "utf8-import-field"); 353 | impl_e2e!(test_utf8_import_module, "utf8-import-module"); 354 | impl_e2e!(test_utf8_invalid_encoding, "utf8-invalid-encoding"); 355 | -------------------------------------------------------------------------------- /src/isa.rs: -------------------------------------------------------------------------------- 1 | use core::convert::From; 2 | use core::convert::Into; 3 | use error::{Result, Trap, WasmError}; 4 | use global::GlobalInstances; 5 | use indice::Indice; 6 | 7 | #[derive(Debug, PartialEq, Clone)] 8 | pub enum Isa { 9 | Reserved, 10 | Unreachable, 11 | Nop, 12 | Block, 13 | Loop, 14 | If, 15 | Else, 16 | End, 17 | Br, 18 | BrIf, 19 | BrTable, 20 | Return, 21 | Call, 22 | CallIndirect, 23 | Select, 24 | DropInst, 25 | I32Const, 26 | I64Const, 27 | F32Const, 28 | F64Const, 29 | GetLocal, 30 | TeeLocal, 31 | SetLocal, 32 | GetGlobal, 33 | SetGlobal, 34 | I32Load, 35 | I64Load, 36 | F32Load, 37 | F64Load, 38 | I32Load8Sign, 39 | I32Load8Unsign, 40 | I32Load16Sign, 41 | I32Load16Unsign, 42 | I64Load8Sign, 43 | I64Load8Unsign, 44 | I64Load16Sign, 45 | I64Load16Unsign, 46 | I64Load32Sign, 47 | I64Load32Unsign, 48 | I32Store, 49 | I64Store, 50 | F32Store, 51 | F64Store, 52 | I32Store8, 53 | I32Store16, 54 | I64Store8, 55 | I64Store16, 56 | I64Store32, 57 | MemorySize, 58 | MemoryGrow, 59 | I32CountLeadingZero, 60 | I32CountTrailingZero, 61 | I32CountNonZero, 62 | I32Add, 63 | I32Sub, 64 | I32Mul, 65 | I32WrapI64, 66 | I32DivSign, 67 | I32DivUnsign, 68 | I32RemSign, 69 | I32RemUnsign, 70 | I32And, 71 | I32Or, 72 | I32Xor, 73 | I32ShiftLeft, 74 | I32ShiftRIghtSign, 75 | I32ShiftRightUnsign, 76 | I32RotateLeft, 77 | I32RotateRight, 78 | I64CountLeadingZero, 79 | I64CountTrailingZero, 80 | I64CountNonZero, 81 | I64Add, 82 | I64Sub, 83 | I64Mul, 84 | I64DivSign, 85 | I64DivUnsign, 86 | I64RemSign, 87 | I64RemUnsign, 88 | I64And, 89 | I64Or, 90 | I64Xor, 91 | I64ShiftLeft, 92 | I64ShiftRightSign, 93 | I64ShiftRightUnsign, 94 | I64RotateLeft, 95 | I64RotateRight, 96 | I32EqualZero, 97 | I32Equal, 98 | I32NotEqual, 99 | I32LessThanSign, 100 | I32LessThanUnsign, 101 | I32GreaterThanSign, 102 | I32GreaterThanUnsign, 103 | I32LessEqualSign, 104 | I32LessEqualUnsign, 105 | I32GreaterEqualSign, 106 | I32GreaterEqualUnsign, 107 | I64EqualZero, 108 | I64Equal, 109 | I64NotEqual, 110 | I64LessThanSign, 111 | I64LessThanUnSign, 112 | I64GreaterThanSign, 113 | I64GreaterThanUnSign, 114 | I64LessEqualSign, 115 | I64LessEqualUnSign, 116 | I64GreaterEqualSign, 117 | I64GreaterEqualUnSign, 118 | F32Equal, 119 | F32NotEqual, 120 | F32LessThan, 121 | F32GreaterThan, 122 | F32LessEqual, 123 | F32GreaterEqual, 124 | F64Equal, 125 | F64NotEqual, 126 | F64LessThan, 127 | F64GreaterThan, 128 | F64LessEqual, 129 | F64GreaterEqual, 130 | F32Abs, 131 | F32Neg, 132 | F32Ceil, 133 | F32Floor, 134 | F32Trunc, 135 | F32Nearest, 136 | F32Sqrt, 137 | F32Add, 138 | F32Sub, 139 | F32Mul, 140 | F32Div, 141 | F32Min, 142 | F32Max, 143 | F32Copysign, 144 | F64Abs, 145 | F64Neg, 146 | F64Ceil, 147 | F64Floor, 148 | F64Trunc, 149 | F64Nearest, 150 | F64Sqrt, 151 | F64Add, 152 | F64Sub, 153 | F64Mul, 154 | F64Div, 155 | F64Min, 156 | F64Max, 157 | F64Copysign, 158 | I32TruncSignF32, 159 | I32TruncUnsignF32, 160 | I32TruncSignF64, 161 | I32TruncUnsignF64, 162 | I64ExtendSignI32, 163 | I64ExtendUnsignI32, 164 | I64TruncSignF32, 165 | I64TruncUnsignF32, 166 | I64TruncSignF64, 167 | I64TruncUnsignF64, 168 | F32ConvertSignI32, 169 | F32ConvertUnsignI32, 170 | F32ConvertSignI64, 171 | F32ConvertUnsignI64, 172 | F32DemoteF64, 173 | F64ConvertSignI32, 174 | F64ConvertUnsignI32, 175 | F64ConvertSignI64, 176 | F64ConvertUnsignI64, 177 | F64PromoteF32, 178 | I32ReinterpretF32, 179 | I64ReinterpretF64, 180 | F32ReinterpretI32, 181 | F64ReinterpretI64, 182 | } 183 | 184 | impl Isa { 185 | pub(crate) fn constant_expression( 186 | source: &[u8], 187 | global_instances: &GlobalInstances, 188 | ) -> Result { 189 | use self::Isa::*; 190 | let constant_instruction = Isa::from(*source.first()?); 191 | match constant_instruction { 192 | I32Const => { 193 | let mut buf = [0; 4]; 194 | buf.clone_from_slice(&source[1..5]); 195 | let offset = unsafe { core::mem::transmute::<_, u32>(buf) } as i32; 196 | if offset < 0 { 197 | return Err(WasmError::Trap(Trap::DataSegmentDoesNotFit)); 198 | } 199 | Ok(offset as usize) 200 | } 201 | GetGlobal => { 202 | let mut buf = [0; 4]; 203 | buf.clone_from_slice(&source[1..5]); 204 | let idx = Indice::from(unsafe { core::mem::transmute::<_, u32>(buf) }); 205 | Ok(global_instances.get_global_ext(&idx) as usize) 206 | } 207 | _ => unreachable!("Expected offset value of memory"), 208 | } 209 | } 210 | } 211 | 212 | impl From for Isa { 213 | fn from(code: u8) -> Self { 214 | use self::Isa::*; 215 | match code { 216 | 0x0 => Unreachable, 217 | 0x1 => Nop, 218 | 0x2 => Block, 219 | 0x3 => Loop, 220 | 0x4 => If, 221 | 0x5 => Else, 222 | 0x06 | 0x07 | 0x08 | 0x09 | 0x0A => Reserved, 223 | 0x0b => End, 224 | 0x0C => Br, 225 | 0x0D => BrIf, 226 | 0x0e => BrTable, 227 | 0x0f => Return, 228 | 0x10 => Call, 229 | 0x11 => CallIndirect, 230 | 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 => Reserved, 231 | 0x1a => DropInst, 232 | 0x1b => Select, 233 | 0x20 => GetLocal, 234 | 0x21 => SetLocal, 235 | 0x22 => TeeLocal, 236 | 0x23 => GetGlobal, 237 | 0x24 => SetGlobal, 238 | 0x25 | 0x26 | 0x27 => Reserved, 239 | 0x28 => I32Load, 240 | 0x29 => I64Load, 241 | 0x2a => F32Load, 242 | 0x2b => F64Load, 243 | 0x2c => I32Load8Sign, 244 | 0x2d => I32Load8Unsign, 245 | 0x2e => I32Load16Sign, 246 | 0x2f => I32Load16Unsign, 247 | 0x30 => I64Load8Sign, 248 | 0x31 => I64Load8Unsign, 249 | 0x32 => I64Load16Sign, 250 | 0x33 => I64Load16Unsign, 251 | 0x34 => I64Load32Sign, 252 | 0x35 => I64Load32Unsign, 253 | 0x36 => I32Store, 254 | 0x37 => I64Store, 255 | 0x38 => F32Store, 256 | 0x39 => F64Store, 257 | 0x3a => I32Store8, 258 | 0x3b => I32Store16, 259 | 0x3c => I64Store8, 260 | 0x3d => I64Store16, 261 | 0x3e => I64Store32, 262 | 0x3f => MemorySize, 263 | 0x40 => MemoryGrow, 264 | 0x41 => I32Const, 265 | 0x42 => I64Const, 266 | 0x43 => F32Const, 267 | 0x44 => F64Const, 268 | 0x45 => I32EqualZero, 269 | 0x46 => I32Equal, 270 | 0x47 => I32NotEqual, 271 | 0x48 => I32LessThanSign, 272 | 0x49 => I32LessThanUnsign, 273 | 0x4a => I32GreaterThanSign, 274 | 0x4b => I32GreaterThanUnsign, 275 | 0x4c => I32LessEqualSign, 276 | 0x4d => I32LessEqualUnsign, 277 | 0x4e => I32GreaterEqualSign, 278 | 0x4f => I32GreaterEqualUnsign, 279 | 0x50 => I64EqualZero, 280 | 0x51 => I64Equal, 281 | 0x52 => I64NotEqual, 282 | 0x53 => I64LessThanSign, 283 | 0x54 => I64LessThanUnSign, 284 | 0x55 => I64GreaterThanSign, 285 | 0x56 => I64GreaterThanUnSign, 286 | 0x57 => I64LessEqualSign, 287 | 0x58 => I64LessEqualUnSign, 288 | 0x59 => I64GreaterEqualSign, 289 | 0x5a => I64GreaterEqualUnSign, 290 | 0x5B => F32Equal, 291 | 0x5C => F32NotEqual, 292 | 0x5D => F32LessThan, 293 | 0x5E => F32GreaterThan, 294 | 0x5F => F32LessEqual, 295 | 0x60 => F32GreaterEqual, 296 | 0x61 => F64Equal, 297 | 0x62 => F64NotEqual, 298 | 0x63 => F64LessThan, 299 | 0x64 => F64GreaterThan, 300 | 0x65 => F64LessEqual, 301 | 0x66 => F64GreaterEqual, 302 | 0x67 => I32CountLeadingZero, 303 | 0x68 => I32CountTrailingZero, 304 | 0x69 => I32CountNonZero, 305 | 0x6a => I32Add, 306 | 0x6b => I32Sub, 307 | 0x6c => I32Mul, 308 | 0x6d => I32DivSign, 309 | 0x6e => I32DivUnsign, 310 | 0x6f => I32RemSign, 311 | 0x70 => I32RemUnsign, 312 | 0x71 => I32And, 313 | 0x72 => I32Or, 314 | 0x73 => I32Xor, 315 | 0x74 => I32ShiftLeft, 316 | 0x75 => I32ShiftRIghtSign, 317 | 0x76 => I32ShiftRightUnsign, 318 | 0x77 => I32RotateLeft, 319 | 0x78 => I32RotateRight, 320 | 0x79 => I64CountLeadingZero, 321 | 0x7a => I64CountTrailingZero, 322 | 0x7b => I64CountNonZero, 323 | 0x7c => I64Add, 324 | 0x7d => I64Sub, 325 | 0x7e => I64Mul, 326 | 0x7f => I64DivSign, 327 | 0x80 => I64DivUnsign, 328 | 0x81 => I64RemSign, 329 | 0x82 => I64RemUnsign, 330 | 0x83 => I64And, 331 | 0x84 => I64Or, 332 | 0x85 => I64Xor, 333 | 0x86 => I64ShiftLeft, 334 | 0x87 => I64ShiftRightSign, 335 | 0x88 => I64ShiftRightUnsign, 336 | 0x89 => I64RotateLeft, 337 | 0x8a => I64RotateRight, 338 | 0x8b => F32Abs, 339 | 0x8c => F32Neg, 340 | 0x8d => F32Ceil, 341 | 0x8e => F32Floor, 342 | 0x8f => F32Trunc, 343 | 0x90 => F32Nearest, 344 | 0x91 => F32Sqrt, 345 | 0x92 => F32Add, 346 | 0x93 => F32Sub, 347 | 0x94 => F32Mul, 348 | 0x95 => F32Div, 349 | 0x96 => F32Min, 350 | 0x97 => F32Max, 351 | 0x98 => F32Copysign, 352 | 0x99 => F64Abs, 353 | 0x9a => F64Neg, 354 | 0x9b => F64Ceil, 355 | 0x9c => F64Floor, 356 | 0x9d => F64Trunc, 357 | 0x9e => F64Nearest, 358 | 0x9f => F64Sqrt, 359 | 0xa0 => F64Add, 360 | 0xa1 => F64Sub, 361 | 0xa2 => F64Mul, 362 | 0xa3 => F64Div, 363 | 0xa4 => F64Min, 364 | 0xa5 => F64Max, 365 | 0xa6 => F64Copysign, 366 | 0xa7 => I32WrapI64, 367 | 0xa8 => I32TruncSignF32, 368 | 0xa9 => I32TruncUnsignF32, 369 | 0xaa => I32TruncSignF64, 370 | 0xab => I32TruncUnsignF64, 371 | 0xac => I64ExtendSignI32, 372 | 0xad => I64ExtendUnsignI32, 373 | 0xae => I64TruncSignF32, 374 | 0xaf => I64TruncUnsignF32, 375 | 0xb0 => I64TruncSignF64, 376 | 0xb1 => I64TruncUnsignF64, 377 | 0xb2 => F32ConvertSignI32, 378 | 0xb3 => F32ConvertUnsignI32, 379 | 0xb4 => F32ConvertSignI64, 380 | 0xb5 => F32ConvertUnsignI64, 381 | 0xb6 => F32DemoteF64, 382 | 0xb7 => F64ConvertSignI32, 383 | 0xb8 => F64ConvertUnsignI32, 384 | 0xb9 => F64ConvertSignI64, 385 | 0xba => F64ConvertUnsignI64, 386 | 0xbb => F64PromoteF32, 387 | 0xbc => I32ReinterpretF32, 388 | 0xbd => I64ReinterpretF64, 389 | 0xbe => F32ReinterpretI32, 390 | 0xbf => F64ReinterpretI64, 391 | x => unreachable!("Code {:x?} does not supported yet.", x), 392 | } 393 | } 394 | } 395 | 396 | impl Into for Isa { 397 | fn into(self) -> u8 { 398 | use self::Isa::*; 399 | match self { 400 | Reserved => unreachable!(), 401 | Unreachable => 0x0, 402 | Nop => 0x1, 403 | Block => 0x2, 404 | Loop => 0x3, 405 | If => 0x4, 406 | Else => 0x5, 407 | End => 0x0b, 408 | Br => 0x0C, 409 | BrIf => 0x0D, 410 | BrTable => 0x0e, 411 | Return => 0x0f, 412 | Call => 0x10, 413 | CallIndirect => 0x11, 414 | DropInst => 0x1a, 415 | Select => 0x1b, 416 | GetLocal => 0x20, 417 | SetLocal => 0x21, 418 | TeeLocal => 0x22, 419 | GetGlobal => 0x23, 420 | SetGlobal => 0x24, 421 | I32Load => 0x28, 422 | I64Load => 0x29, 423 | F32Load => 0x2a, 424 | F64Load => 0x2b, 425 | I32Load8Sign => 0x2c, 426 | I32Load8Unsign => 0x2d, 427 | I32Load16Sign => 0x2e, 428 | I32Load16Unsign => 0x2f, 429 | I64Load8Sign => 0x30, 430 | I64Load8Unsign => 0x31, 431 | I64Load16Sign => 0x32, 432 | I64Load16Unsign => 0x33, 433 | I64Load32Sign => 0x34, 434 | I64Load32Unsign => 0x35, 435 | I32Store => 0x36, 436 | I64Store => 0x37, 437 | F32Store => 0x38, 438 | F64Store => 0x39, 439 | I32Store8 => 0x3a, 440 | I32Store16 => 0x3b, 441 | I64Store8 => 0x3c, 442 | I64Store16 => 0x3d, 443 | I64Store32 => 0x3e, 444 | MemorySize => 0x3f, 445 | MemoryGrow => 0x40, 446 | I32Const => 0x41, 447 | I64Const => 0x42, 448 | F32Const => 0x43, 449 | F64Const => 0x44, 450 | I32EqualZero => 0x45, 451 | I32Equal => 0x46, 452 | I32NotEqual => 0x47, 453 | I32LessThanSign => 0x48, 454 | I32LessThanUnsign => 0x49, 455 | I32GreaterThanSign => 0x4a, 456 | I32GreaterThanUnsign => 0x4b, 457 | I32LessEqualSign => 0x4c, 458 | I32LessEqualUnsign => 0x4d, 459 | I32GreaterEqualSign => 0x4e, 460 | I32GreaterEqualUnsign => 0x4f, 461 | I64EqualZero => 0x50, 462 | I64Equal => 0x51, 463 | I64NotEqual => 0x52, 464 | I64LessThanSign => 0x53, 465 | I64LessThanUnSign => 0x54, 466 | I64GreaterThanSign => 0x55, 467 | I64GreaterThanUnSign => 0x56, 468 | I64LessEqualSign => 0x57, 469 | I64LessEqualUnSign => 0x58, 470 | I64GreaterEqualSign => 0x59, 471 | I64GreaterEqualUnSign => 0x5a, 472 | F32Equal => 0x5B, 473 | F32NotEqual => 0x5C, 474 | F32LessThan => 0x5D, 475 | F32GreaterThan => 0x5E, 476 | F32LessEqual => 0x5F, 477 | F32GreaterEqual => 0x60, 478 | F64Equal => 0x61, 479 | F64NotEqual => 0x62, 480 | F64LessThan => 0x63, 481 | F64GreaterThan => 0x64, 482 | F64LessEqual => 0x65, 483 | F64GreaterEqual => 0x66, 484 | I32CountLeadingZero => 0x67, 485 | I32CountTrailingZero => 0x68, 486 | I32CountNonZero => 0x69, 487 | I32Add => 0x6a, 488 | I32Sub => 0x6b, 489 | I32Mul => 0x6c, 490 | I32DivSign => 0x6d, 491 | I32DivUnsign => 0x6e, 492 | I32RemSign => 0x6f, 493 | I32RemUnsign => 0x70, 494 | I32And => 0x71, 495 | I32Or => 0x72, 496 | I32Xor => 0x73, 497 | I32ShiftLeft => 0x74, 498 | I32ShiftRIghtSign => 0x75, 499 | I32ShiftRightUnsign => 0x76, 500 | I32RotateLeft => 0x77, 501 | I32RotateRight => 0x78, 502 | I64CountLeadingZero => 0x79, 503 | I64CountTrailingZero => 0x7a, 504 | I64CountNonZero => 0x7b, 505 | I64Add => 0x7c, 506 | I64Sub => 0x7d, 507 | I64Mul => 0x7e, 508 | I64DivSign => 0x7f, 509 | I64DivUnsign => 0x80, 510 | I64RemSign => 0x81, 511 | I64RemUnsign => 0x82, 512 | I64And => 0x83, 513 | I64Or => 0x84, 514 | I64Xor => 0x85, 515 | I64ShiftLeft => 0x86, 516 | I64ShiftRightSign => 0x87, 517 | I64ShiftRightUnsign => 0x88, 518 | I64RotateLeft => 0x89, 519 | I64RotateRight => 0x8a, 520 | F32Abs => 0x8b, 521 | F32Neg => 0x8c, 522 | F32Ceil => 0x8d, 523 | F32Floor => 0x8e, 524 | F32Trunc => 0x8f, 525 | F32Nearest => 0x90, 526 | F32Sqrt => 0x91, 527 | F32Add => 0x92, 528 | F32Sub => 0x93, 529 | F32Mul => 0x94, 530 | F32Div => 0x95, 531 | F32Min => 0x96, 532 | F32Max => 0x97, 533 | F32Copysign => 0x98, 534 | F64Abs => 0x99, 535 | F64Neg => 0x9a, 536 | F64Ceil => 0x9b, 537 | F64Floor => 0x9c, 538 | F64Trunc => 0x9d, 539 | F64Nearest => 0x9e, 540 | F64Sqrt => 0x9f, 541 | F64Add => 0xa0, 542 | F64Sub => 0xa1, 543 | F64Mul => 0xa2, 544 | F64Div => 0xa3, 545 | F64Min => 0xa4, 546 | F64Max => 0xa5, 547 | F64Copysign => 0xa6, 548 | I32WrapI64 => 0xa7, 549 | I32TruncSignF32 => 0xa8, 550 | I32TruncUnsignF32 => 0xa9, 551 | I32TruncSignF64 => 0xaa, 552 | I32TruncUnsignF64 => 0xab, 553 | I64ExtendSignI32 => 0xac, 554 | I64ExtendUnsignI32 => 0xad, 555 | I64TruncSignF32 => 0xae, 556 | I64TruncUnsignF32 => 0xaf, 557 | I64TruncSignF64 => 0xb0, 558 | I64TruncUnsignF64 => 0xb1, 559 | F32ConvertSignI32 => 0xb2, 560 | F32ConvertUnsignI32 => 0xb3, 561 | F32ConvertSignI64 => 0xb4, 562 | F32ConvertUnsignI64 => 0xb5, 563 | F32DemoteF64 => 0xb6, 564 | F64ConvertSignI32 => 0xb7, 565 | F64ConvertUnsignI32 => 0xb8, 566 | F64ConvertSignI64 => 0xb9, 567 | F64ConvertUnsignI64 => 0xba, 568 | F64PromoteF32 => 0xbb, 569 | I32ReinterpretF32 => 0xbc, 570 | I64ReinterpretF64 => 0xbd, 571 | F32ReinterpretI32 => 0xbe, 572 | F64ReinterpretI64 => 0xbf, 573 | } 574 | } 575 | } 576 | 577 | #[cfg(test)] 578 | pub enum ComposedCode { 579 | Code(Isa), 580 | Byte(u8), 581 | } 582 | 583 | #[cfg(test)] 584 | pub fn into_vec_u8(this: &[ComposedCode]) -> alloc::vec::Vec { 585 | this 586 | .iter() 587 | .map(|x| match x { 588 | ComposedCode::Code(code) => Isa::into(code.clone()), 589 | ComposedCode::Byte(byte) => *byte, 590 | }) 591 | .collect() 592 | } 593 | 594 | impl Isa { 595 | pub fn is_else_or_end(code: Option) -> bool { 596 | match code { 597 | Some(0x5) | Some(0x0b) => true, 598 | _ => false, 599 | } 600 | } 601 | } 602 | 603 | #[cfg(test)] 604 | mod tests { 605 | use super::*; 606 | 607 | #[test] 608 | fn instruction_size() { 609 | assert_eq!(core::mem::size_of::(), 1); 610 | } 611 | } 612 | --------------------------------------------------------------------------------