├── src ├── parser │ ├── section.rs │ ├── mod.rs │ ├── util.rs │ ├── error.rs │ ├── writer.rs │ ├── validator.rs │ ├── types.rs │ └── reader.rs ├── types.rs ├── no_log.rs ├── lib.rs ├── error.rs ├── small_vec.rs ├── environ.rs ├── bin │ ├── wasm-board.rs │ ├── wasm-interp.rs │ └── wasm-objdump.rs ├── stack.rs ├── floathex.rs ├── page_table.rs ├── memory_inst.rs ├── cursor.rs ├── reader.rs └── module_inst.rs ├── test ├── local_test ├── basic.wasm ├── empty.wasm ├── add.wasm ├── tee.wasm ├── call.wasm ├── local.wasm ├── if-false.wasm ├── if-true.wasm ├── if-else-false.wasm ├── if-else-true.wasm ├── param.wasm ├── import.wasm ├── math.wasm ├── double2.wasm ├── fac0.wasm ├── fac1.wasm ├── fac2.wasm ├── fac3.wasm ├── global.wasm ├── load-32.wasm ├── binary-32.wasm ├── fac10.wasm ├── unary-32.wasm ├── multi-basic.wasm ├── test-import.wasm ├── wasm_hello.wasm ├── select.wasm ├── host.wasm ├── empty.txt ├── return-void.wasm ├── basic.txt ├── math.txt ├── add.txt ├── tee.txt ├── import.txt ├── local.txt ├── call.txt ├── param.txt ├── if-false.txt ├── if-true.txt ├── if-else-false.txt ├── if-else-true.txt ├── global.txt ├── host.txt ├── multi-basic.txt ├── select.txt ├── double2.txt ├── fac0.txt ├── fac1.txt ├── fac2.txt ├── fac3.txt ├── fac10.txt ├── unary-32.txt ├── return-void.txt ├── load-32.txt └── binary-32.txt ├── wasm-blinky ├── .gitignore ├── Cargo.toml ├── Makefile └── src │ └── lib.rs ├── wasm-hello ├── .gitignore ├── src │ └── lib.rs ├── Cargo.toml └── Makefile ├── .gitignore ├── Makefile ├── Cargo.toml ├── LICENSE-MIT ├── MOTIVATION.md ├── test-interp.txt ├── TESTING.md ├── test-dump.txt ├── gen_opcode.py ├── README.md ├── opcodes.csv ├── LICENSE-APACHE └── Cargo.lock /src/parser/section.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | /Users/jcsoo/work/wabt/test -------------------------------------------------------------------------------- /local_test/basic.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 | A* -------------------------------------------------------------------------------- /local_test/empty.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  -------------------------------------------------------------------------------- /local_test/add.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 | 3 | AAj -------------------------------------------------------------------------------- /local_test/tee.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  A" -------------------------------------------------------------------------------- /local_test/call.wasm: -------------------------------------------------------------------------------- 1 | asm`  callempty 2 |  A* -------------------------------------------------------------------------------- /local_test/local.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  A!  -------------------------------------------------------------------------------- /wasm-blinky/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | *.wasm -------------------------------------------------------------------------------- /wasm-hello/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | *.wasm -------------------------------------------------------------------------------- /local_test/if-false.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  A@A A 3 |  -------------------------------------------------------------------------------- /local_test/if-true.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  A@A A 3 |  -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */target/* 2 | target/* 3 | out/* 4 | bin/* 5 | .crates.toml 6 | test -------------------------------------------------------------------------------- /local_test/if-else-false.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  AA 3 | A  -------------------------------------------------------------------------------- /local_test/if-else-true.wasm: -------------------------------------------------------------------------------- 1 | asm`main 2 |  AA 3 | A  -------------------------------------------------------------------------------- /local_test/param.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | ``main 3 | A*   -------------------------------------------------------------------------------- /local_test/import.wasm: -------------------------------------------------------------------------------- 1 | asm``mathdoublemain 2 | A* -------------------------------------------------------------------------------- /local_test/math.wasm: -------------------------------------------------------------------------------- 1 | asm ``hosthello 2 | double 3 |   j -------------------------------------------------------------------------------- /local_test/double2.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | `` double1 3 | A  j  j -------------------------------------------------------------------------------- /local_test/fac0.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | ``fac0 3 | "A  AJ AklA -------------------------------------------------------------------------------- /local_test/fac1.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | ``fac1 3 | "A  AJ AklA -------------------------------------------------------------------------------- /local_test/fac2.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | ``fac2 3 | "A  AJ AklA -------------------------------------------------------------------------------- /local_test/fac3.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | ``fac3 3 | "A  AJ AklA -------------------------------------------------------------------------------- /local_test/global.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobbin-rs/bobbin-wasm/HEAD/local_test/global.wasm -------------------------------------------------------------------------------- /local_test/load-32.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobbin-rs/bobbin-wasm/HEAD/local_test/load-32.wasm -------------------------------------------------------------------------------- /local_test/binary-32.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobbin-rs/bobbin-wasm/HEAD/local_test/binary-32.wasm -------------------------------------------------------------------------------- /local_test/fac10.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | `` fac10 3 | "A 4 |   AJ AklA -------------------------------------------------------------------------------- /local_test/unary-32.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobbin-rs/bobbin-wasm/HEAD/local_test/unary-32.wasm -------------------------------------------------------------------------------- /local_test/multi-basic.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobbin-rs/bobbin-wasm/HEAD/local_test/multi-basic.wasm -------------------------------------------------------------------------------- /local_test/test-import.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobbin-rs/bobbin-wasm/HEAD/local_test/test-import.wasm -------------------------------------------------------------------------------- /local_test/wasm_hello.wasm: -------------------------------------------------------------------------------- 1 | asm` envhellomemory run_hello 2 |  linking -------------------------------------------------------------------------------- /local_test/select.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | `` 3 | test_i32_l 4 | test_i32_r 5 |  AA  A A -------------------------------------------------------------------------------- /local_test/host.wasm: -------------------------------------------------------------------------------- 1 | asm````&hosthellohostprinthostaddmain 2 | A*AA -------------------------------------------------------------------------------- /local_test/empty.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") 4 | return)) 5 | (;; STDOUT ;;; 6 | main() => 7 | ;;; STDOUT ;;) 8 | -------------------------------------------------------------------------------- /local_test/return-void.wasm: -------------------------------------------------------------------------------- 1 | asm ```#test1check1test2check2 2 | 2 AF@ AA6 A A( A A( -------------------------------------------------------------------------------- /local_test/basic.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 42 5 | return)) 6 | (;; STDOUT ;;; 7 | main() => i32:42 8 | ;;; STDOUT ;;) 9 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | pub use parser::module::{Id, Global, Import, Export, Element, Data, Initializer, ImportDesc, ExportDesc, Immediate}; 2 | pub use parser::types::{Index, Limits, MemoryType, TableType, ValueType, GlobalType}; -------------------------------------------------------------------------------- /local_test/math.txt: -------------------------------------------------------------------------------- 1 | (module 2 | (import "host" "hello" (func $hello)) 3 | (func $double (export "double") (param i32) (result i32) 4 | call $hello 5 | get_local 0 6 | get_local 0 7 | i32.add 8 | ) 9 | ) -------------------------------------------------------------------------------- /local_test/add.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 2 5 | i32.const 3 6 | i32.add 7 | return)) 8 | (;; STDOUT ;;; 9 | main() => i32:5 10 | ;;; STDOUT ;;) 11 | -------------------------------------------------------------------------------- /local_test/tee.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | (local i32) 5 | i32.const 5 6 | tee_local 0 7 | return)) 8 | (;; STDOUT ;;; 9 | main() => i32:5 10 | ;;; STDOUT ;;) 11 | -------------------------------------------------------------------------------- /wasm-hello/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern { 2 | fn hello(); 3 | } 4 | 5 | // #[no_mangle] 6 | // extern "C" fn add_one(x: i32) -> i32 { 7 | // x + 1 8 | // } 9 | 10 | #[no_mangle] 11 | pub extern "C" fn run_hello() { 12 | unsafe { hello(); } 13 | } -------------------------------------------------------------------------------- /local_test/import.txt: -------------------------------------------------------------------------------- 1 | (module 2 | (import "math" "double" (func $double)) 3 | (func (export "main") (result i32) 4 | i32.const 42 5 | call $double 6 | return) 7 | ) 8 | (;; STDOUT ;;; 9 | main() => i32:84 10 | ;;; STDOUT ;;) -------------------------------------------------------------------------------- /local_test/local.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | (local i32) 5 | i32.const 5 6 | set_local 0 7 | get_local 0 8 | return)) 9 | (;; STDOUT ;;; 10 | main() => i32:5 11 | ;;; STDOUT ;;) 12 | -------------------------------------------------------------------------------- /local_test/call.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "callempty") (result i32) 4 | call $empty) 5 | 6 | 7 | (func $empty (result i32) 8 | i32.const 42 9 | ) 10 | ) 11 | (;; STDOUT ;;; 12 | callempty() => i32:42 13 | ;;; STDOUT ;;) 14 | -------------------------------------------------------------------------------- /wasm-blinky/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-blinky" 3 | version = "0.1.0" 4 | authors = ["Jonathan Soo "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | 11 | [profile.release] 12 | debug = false 13 | lto = true 14 | opt-level = 's' -------------------------------------------------------------------------------- /wasm-hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-hello" 3 | version = "0.1.0" 4 | authors = ["Jonathan Soo "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | 11 | [profile.release] 12 | debug = false 13 | lto = true 14 | opt-level = 's' -------------------------------------------------------------------------------- /local_test/param.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 42 5 | call $id 6 | return) 7 | (func $id (param i32) (result i32) 8 | get_local 0 9 | return) 10 | ) 11 | (;; STDOUT ;;; 12 | main() => i32:42 13 | ;;; STDOUT ;;) 14 | -------------------------------------------------------------------------------- /local_test/if-false.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 0 5 | if 6 | i32.const 20 7 | return 8 | end 9 | i32.const 10 10 | return 11 | ) 12 | ) 13 | (;; STDOUT ;;; 14 | main() => i32:10 15 | ;;; STDOUT ;;) 16 | -------------------------------------------------------------------------------- /local_test/if-true.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 1 5 | if 6 | i32.const 20 7 | return 8 | end 9 | i32.const 10 10 | return 11 | ) 12 | ) 13 | (;; STDOUT ;;; 14 | main() => i32:20 15 | ;;; STDOUT ;;) 16 | -------------------------------------------------------------------------------- /local_test/if-else-false.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 0 5 | if (result i32) 6 | i32.const 10 7 | else 8 | i32.const 20 9 | end 10 | return 11 | ) 12 | ) 13 | (;; STDOUT ;;; 14 | main() => i32:10 15 | ;;; STDOUT ;;) 16 | -------------------------------------------------------------------------------- /local_test/if-else-true.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 1 5 | if (result i32) 6 | i32.const 10 7 | else 8 | i32.const 20 9 | end 10 | return 11 | ) 12 | ) 13 | (;; STDOUT ;;; 14 | main() => i32:20 15 | ;;; STDOUT ;;) 16 | -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod util; 2 | pub mod error; 3 | pub mod reader; 4 | pub mod types; 5 | pub mod section; 6 | pub mod module; 7 | pub mod validator; 8 | pub mod opcode; 9 | 10 | pub use self::error::*; 11 | pub use self::reader::*; 12 | pub use self::types::*; 13 | pub use self::section::*; 14 | pub use self::module::*; 15 | pub use self::validator::*; -------------------------------------------------------------------------------- /local_test/global.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (global i32 i32.const 42) 4 | (func (export "get_global") (result i32) 5 | get_global 0 6 | return) 7 | (func (export "set_global") (result i32) 8 | i32.const 99 9 | set_global 0 10 | get_global 0 11 | return) 12 | ) 13 | (;; STDOUT ;;; 14 | get_global() => i32:42 15 | set_global() => i32:99 16 | ;;; STDOUT ;;) 17 | -------------------------------------------------------------------------------- /local_test/host.txt: -------------------------------------------------------------------------------- 1 | (module 2 | (import "host" "hello" (func $hello)) 3 | (import "host" "print" (func $print (param i32))) 4 | (import "host" "add" (func $add (param i32 i32) (result i32))) 5 | (func (export "main") (result i32) 6 | call $hello 7 | i32.const 42 8 | call $print 9 | i32.const 2 10 | i32.const 3 11 | call $add 12 | return) 13 | ) 14 | -------------------------------------------------------------------------------- /wasm-blinky/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run clean 2 | 3 | OUT=wasm_blinky 4 | 5 | all: $(OUT).wasm 6 | 7 | $(OUT).wasm: src/lib.rs 8 | cargo build --release --target wasm32-unknown-unknown 9 | wasm-gc target/wasm32-unknown-unknown/release/$(OUT).wasm -o $(OUT).gc.wasm 10 | wasm-opt -Os $(OUT).gc.wasm -o $(OUT).wasm 11 | 12 | run: $(OUT).wasm 13 | wasm-board $(OUT).wasm 14 | 15 | clean: 16 | cargo clean 17 | rm -f $(OUT).wasm $(OUT).gc.wasm -------------------------------------------------------------------------------- /local_test/multi-basic.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "main") (result i32) 4 | i32.const 42 5 | return) 6 | (func (export "hello") (result i32) 7 | i32.const 1234 8 | return) 9 | (func (export "goodbye") (result i32) 10 | i32.const 5678 11 | return) 12 | 13 | ) 14 | 15 | (;; STDOUT ;;; 16 | main() => i32:42 17 | hello() => i32:1234 18 | goodbye() => i32:5678 19 | ;;; STDOUT ;;) 20 | -------------------------------------------------------------------------------- /local_test/select.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func $i32 (param i32) (result i32) 4 | i32.const 1 5 | i32.const 2 6 | get_local 0 7 | select) 8 | 9 | (func (export "test_i32_l") (result i32) 10 | i32.const 0 11 | call $i32) 12 | (func (export "test_i32_r") (result i32) 13 | i32.const 1 14 | call $i32) 15 | ) 16 | (;; STDOUT ;;; 17 | test_i32_l() => i32:2 18 | test_i32_r() => i32:1 19 | ;;; STDOUT ;;) 20 | -------------------------------------------------------------------------------- /wasm-hello/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run clean 2 | 3 | all: wasm_hello.wasm 4 | 5 | wasm_hello.wasm: src/lib.rs 6 | cargo build --release --target wasm32-unknown-unknown 7 | wasm-gc target/wasm32-unknown-unknown/release/wasm_hello.wasm -o wasm_hello.gc.wasm 8 | wasm-opt -Os wasm_hello.gc.wasm -o wasm_hello.wasm 9 | 10 | run: wasm_hello.wasm 11 | wasm-interp --run-all-exports wasm_hello.wasm 12 | 13 | clean: 14 | cargo clean 15 | rm -f wasm_hello.wasm wasm_hello.gc.wasm -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test test-dump install install-test 2 | 3 | BINDIR=./bin 4 | TEST_ARGS=--bindir $(BINDIR) 5 | 6 | install: 7 | cargo install --force --debug 8 | 9 | install-test: 10 | cargo -q install --path . --root . --force 11 | 12 | test: install-test test-dump test-interp 13 | 14 | test-dump: 15 | cat test-dump.txt | grep -v \# | xargs test/run-tests.py $(TEST_ARGS) 16 | 17 | test-interp: 18 | cat test-interp.txt | grep -v \# | xargs test/run-tests.py $(TEST_ARGS) 19 | -------------------------------------------------------------------------------- /local_test/double2.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "double1") (result i32) 4 | i32.const 4 5 | call $double) 6 | 7 | 8 | (func $double (param i32) (result i32) 9 | get_local 0 10 | call $double2 11 | get_local 0 12 | call $double2 13 | i32.add 14 | ) 15 | 16 | (func $double2 (param i32) (result i32) 17 | get_local 0 18 | get_local 0 19 | i32.add 20 | ) 21 | ) 22 | (;; STDOUT ;;; 23 | fac10() => i32:16 24 | ;;; STDOUT ;;) 25 | -------------------------------------------------------------------------------- /src/parser/util.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::slice; 3 | 4 | pub fn from_byte_slice(buf: &[u8]) -> &[T] { 5 | let size = mem::size_of::(); 6 | assert!(buf.len() % size == 0); 7 | let t_len = buf.len() / size; 8 | unsafe { slice::from_raw_parts(buf.as_ptr() as *const T, t_len) } 9 | } 10 | 11 | // pub fn into_byte_slice(buf: &[T]) -> &[u8] { 12 | // debug_assert!(mem::size_of::() == 1); 13 | // unsafe { slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len()) } 14 | // } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bobbin-wasm" 3 | version = "0.1.0" 4 | authors = ["Jonathan Soo "] 5 | 6 | [dependencies] 7 | fallible-iterator = { version = "0.1.4", default-features = false } 8 | byteorder = { version = "1", default-features = false } 9 | log = { version = "0.4", optional = true } 10 | clap = { version = "2.29.4", optional = true } 11 | env_logger = {version = "0.5.3", optional = true } 12 | 13 | [features] 14 | default = ["log", "clap", "env_logger"] 15 | enable-log = ["log"] 16 | enable-log-off = [] -------------------------------------------------------------------------------- /local_test/fac0.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "fac0") (result i32) 4 | i32.const 0 5 | call $fac) 6 | 7 | 8 | (func $fac (param i32) (result i32) 9 | get_local 0 10 | i32.const 0 11 | i32.gt_s 12 | if (result i32) 13 | get_local 0 14 | get_local 0 15 | i32.const 1 16 | i32.sub 17 | call $fac 18 | i32.mul 19 | return 20 | else 21 | i32.const 1 22 | return 23 | end) 24 | ) 25 | (;; STDOUT ;;; 26 | fac10() => i32:1 27 | ;;; STDOUT ;;) 28 | -------------------------------------------------------------------------------- /local_test/fac1.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "fac1") (result i32) 4 | i32.const 1 5 | call $fac) 6 | 7 | 8 | (func $fac (param i32) (result i32) 9 | get_local 0 10 | i32.const 0 11 | i32.gt_s 12 | if (result i32) 13 | get_local 0 14 | get_local 0 15 | i32.const 1 16 | i32.sub 17 | call $fac 18 | i32.mul 19 | return 20 | else 21 | i32.const 1 22 | return 23 | end) 24 | ) 25 | (;; STDOUT ;;; 26 | fac10() => i32:1 27 | ;;; STDOUT ;;) 28 | -------------------------------------------------------------------------------- /local_test/fac2.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "fac2") (result i32) 4 | i32.const 2 5 | call $fac) 6 | 7 | 8 | (func $fac (param i32) (result i32) 9 | get_local 0 10 | i32.const 0 11 | i32.gt_s 12 | if (result i32) 13 | get_local 0 14 | get_local 0 15 | i32.const 1 16 | i32.sub 17 | call $fac 18 | i32.mul 19 | return 20 | else 21 | i32.const 1 22 | return 23 | end) 24 | ) 25 | (;; STDOUT ;;; 26 | fac10() => i32:2 27 | ;;; STDOUT ;;) 28 | -------------------------------------------------------------------------------- /local_test/fac3.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "fac3") (result i32) 4 | i32.const 3 5 | call $fac) 6 | 7 | 8 | (func $fac (param i32) (result i32) 9 | get_local 0 10 | i32.const 0 11 | i32.gt_s 12 | if (result i32) 13 | get_local 0 14 | get_local 0 15 | i32.const 1 16 | i32.sub 17 | call $fac 18 | i32.mul 19 | return 20 | else 21 | i32.const 1 22 | return 23 | end) 24 | ) 25 | (;; STDOUT ;;; 26 | fac10() => i32:6 27 | ;;; STDOUT ;;) 28 | -------------------------------------------------------------------------------- /local_test/fac10.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (func (export "fac10") (result i32) 4 | i32.const 10 5 | call $fac) 6 | 7 | 8 | (func $fac (param i32) (result i32) 9 | get_local 0 10 | i32.const 0 11 | i32.gt_s 12 | if (result i32) 13 | get_local 0 14 | get_local 0 15 | i32.const 1 16 | i32.sub 17 | call $fac 18 | i32.mul 19 | return 20 | else 21 | i32.const 1 22 | return 23 | end) 24 | ) 25 | (;; STDOUT ;;; 26 | fac10() => i32:3628800 27 | ;;; STDOUT ;;) 28 | -------------------------------------------------------------------------------- /src/parser/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 2 | pub enum Error { 3 | UnexpectedEof, 4 | InvalidU0, 5 | InvalidU1, 6 | InvalidU7, 7 | InvalidU32, 8 | InvalidI32, 9 | InvalidU64, 10 | InvalidI64, 11 | InvalidUtf8, 12 | InvalidOpcode, 13 | InvalidEnd, 14 | InvalidValueType, 15 | InvalidBlockType, 16 | InvalidFunctionType, 17 | InvalidTableType, 18 | InvalidSectionId, 19 | InvalidImportDesc, 20 | InvalidExportDesc, 21 | 22 | InvalidMagic, 23 | InvalidVersion, 24 | } -------------------------------------------------------------------------------- /local_test/unary-32.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | ;; i32 4 | (func (export "i32_eqz_100") (result i32) 5 | i32.const 100 6 | i32.eqz) 7 | (func (export "i32_eqz_0") (result i32) 8 | i32.const 0 9 | i32.eqz) 10 | (func (export "i32_clz") (result i32) 11 | i32.const 128 12 | i32.clz) 13 | (func (export "i32_ctz") (result i32) 14 | i32.const 128 15 | i32.ctz) 16 | (func (export "i32_popcnt") (result i32) 17 | i32.const 128 18 | i32.popcnt) 19 | ) 20 | (;; STDOUT ;;; 21 | i32_eqz_100() => i32:0 22 | i32_eqz_0() => i32:1 23 | i32_clz() => i32:24 24 | i32_ctz() => i32:7 25 | i32_popcnt() => i32:1 26 | ;;; STDOUT ;;) 27 | -------------------------------------------------------------------------------- /local_test/return-void.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (memory 1) 4 | (func $store_unless (param i32) 5 | get_local 0 6 | i32.const 0 7 | i32.eq 8 | if 9 | return 10 | end 11 | i32.const 0 12 | i32.const 1 13 | i32.store) 14 | 15 | (func (export "test1") 16 | i32.const 0 17 | call $store_unless) 18 | 19 | (func (export "check1") (result i32) 20 | i32.const 0 21 | i32.load) 22 | 23 | (func (export "test2") 24 | i32.const 1 25 | call $store_unless) 26 | 27 | (func (export "check2") (result i32) 28 | i32.const 0 29 | i32.load)) 30 | (;; STDOUT ;;; 31 | test1() => 32 | check1() => i32:0 33 | test2() => 34 | check2() => i32:1 35 | ;;; STDOUT ;;) 36 | -------------------------------------------------------------------------------- /src/no_log.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! debug { 3 | (target: $target:expr, $($arg:tt)*) => { }; 4 | ($($arg:tt)*) => { }; 5 | } 6 | 7 | #[macro_export] 8 | macro_rules! error { 9 | (target: $target:expr, $($arg:tt)*) => { }; 10 | ($($arg:tt)*) => { }; 11 | } 12 | 13 | #[macro_export] 14 | macro_rules! info { 15 | (target: $target:expr, $($arg:tt)*) => { }; 16 | ($($arg:tt)*) => { }; 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! trace { 21 | (target: $target:expr, $($arg:tt)*) => { }; 22 | ($($arg:tt)*) => { }; 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! warn { 27 | (target: $target:expr, $($arg:tt)*) => { }; 28 | ($($arg:tt)*) => { }; 29 | } 30 | 31 | #[macro_export] 32 | macro_rules! panic { 33 | (target: $target:expr, $($arg:tt)*) => { unsafe { ::core::intrinsics::abort() } }; 34 | ($($arg:tt)*) => { unsafe { ::core::intrinsics::abort() } }; 35 | } 36 | -------------------------------------------------------------------------------- /local_test/load-32.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | (memory 1) 4 | (data (i32.const 0) "\ff\ff\ff\ff") 5 | (data (i32.const 4) "\00\00\ce\41") 6 | (data (i32.const 8) "\00\00\00\00\00\ff\8f\40") 7 | (data (i32.const 16) "\ff\ff\ff\ff\ff\ff\ff\ff") 8 | 9 | (func (export "i32_load8_s") (result i32) 10 | i32.const 0 11 | i32.load8_s) 12 | (func (export "i32_load16_s") (result i32) 13 | i32.const 0 14 | i32.load16_s) 15 | (func (export "i32_load") (result i32) 16 | i32.const 0 17 | i32.load) 18 | 19 | (func (export "i32_load8_u") (result i32) 20 | i32.const 0 21 | i32.load8_u) 22 | (func (export "i32_load16_u") (result i32) 23 | i32.const 0 24 | i32.load16_u) 25 | ) 26 | (;; STDOUT ;;; 27 | i32_load8_s() => i32:4294967295 28 | i32_load16_s() => i32:4294967295 29 | i32_load() => i32:4294967295 30 | i32_load8_u() => i32:255 31 | i32_load16_u() => i32:65535 32 | ;;; STDOUT ;;) 33 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(ptr_offset_from, core_intrinsics)] 3 | 4 | extern crate fallible_iterator; 5 | extern crate byteorder; 6 | #[cfg(not(feature="enable-log-off"))] 7 | #[macro_use] extern crate log; 8 | #[cfg(feature="enable-log-off")] 9 | #[macro_use] pub mod no_log; 10 | 11 | pub mod error; 12 | pub mod types; 13 | pub mod cursor; 14 | pub mod reader; 15 | pub mod writer; 16 | pub mod stack; 17 | pub mod small_vec; 18 | pub mod compiler; 19 | pub mod typeck; 20 | pub mod interp; 21 | pub mod memory_inst; 22 | pub mod module_inst; 23 | pub mod environ; 24 | pub mod floathex; 25 | pub mod parser; 26 | pub mod page_table; 27 | 28 | use parser::opcode as opcode; 29 | 30 | pub use error::*; 31 | pub use types::*; 32 | pub use cursor::*; 33 | pub use writer::*; 34 | 35 | 36 | pub const MAGIC_COOKIE: u32 = 0x6d736100; 37 | pub const VERSION: u32 = 0x1; 38 | pub const FIXUP: u32 = 0xffff_ffff; 39 | pub const PAGE_SIZE: usize = 65535; 40 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Jonathan Soo 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/parser/writer.rs: -------------------------------------------------------------------------------- 1 | use parser::Error; 2 | use parser::util::*; 3 | 4 | pub struct Writer<'a> { 5 | buf: &'a mut [u8], 6 | pos: usize, 7 | } 8 | 9 | impl<'a> Writer<'a> { 10 | pub fn new(buf: &'a mut [u8]) -> Self { 11 | Writer { buf, pos: 0 } 12 | } 13 | 14 | pub fn cap(&self) -> usize { 15 | self.buf.len() 16 | } 17 | 18 | pub fn write_u8(&mut self, val: u8) -> Result<(), Error> { 19 | let pos = self.pos + 1; 20 | if pos > self.cap() { 21 | Err(Error::UnexpectedEof) 22 | } else { 23 | self.buf[self.pos] = val; 24 | self.pos = pos; 25 | Ok(()) 26 | } 27 | } 28 | } 29 | 30 | pub trait Write { 31 | fn write(&mut self, val: T) -> Result<(), Error>; 32 | } 33 | 34 | impl<'a> Write for Writer<'a> { 35 | fn write(&mut self, val: u32) -> Result<(), Error> { 36 | Ok({ 37 | self.write_u8((val >> 0) as u8)?; 38 | self.write_u8((val >> 8) as u8)?; 39 | self.write_u8((val >> 16) as u8)?; 40 | self.write_u8((val >> 24) as u8)?; 41 | }) 42 | } 43 | } 44 | 45 | impl<'a,'b, T> Write<&'b [T]> for Writer<'a> { 46 | fn write(&mut self, value: &'b [T]) -> Result<(), Error> { 47 | Ok({ 48 | let value: &[u8] = into_byte_slice(value); 49 | self.write_u8(value.len() as u8)?; 50 | for v in value { 51 | self.write_u8(*v)?; 52 | } 53 | }) 54 | } 55 | } -------------------------------------------------------------------------------- /MOTIVATION.md: -------------------------------------------------------------------------------- 1 | # Motivation 2 | 3 | WebAssembly is great, and it's not just for browsers. It's a well 4 | designed, security-conscious virtual machine that has the potential 5 | to be used anywhere a platform-independent, language-agnostic, sandboxed computing 6 | environment is needed. 7 | 8 | WebAssembly provides a way for a device vendor to become a platform vendor 9 | by providing an API and host environment for end users and third parties. 10 | 11 | Web browsers are one example of this, but there are countless other examples: 12 | applications such as Photoshop plugins, text editors, even industrial devices 13 | and robots. 14 | 15 | There are other examples where a platform approach makes sense even if the API 16 | isn't intended to be open to end users. For instance, game engines often use scripting 17 | languages so that their content developers don't need to use the same language as the 18 | engine developers and to reduce porting costs. Similarly, embedded developers with a 19 | family of products may want to keep their higher level application code separate from 20 | their underlying device-specific code, and maybe even use different programming 21 | languages and teams. 22 | 23 | Finally, many vendors face the challenge of securely distributing firmware updates. 24 | Code signing is the first line of defense, but sandboxed firmware can prevent 25 | jailbreaks even if that fails, even on devices without memory protection. 26 | 27 | **bobbin-wasm** is a WebAssembly engine designed to enable these applications. It's aim 28 | is to be small, fast, and secure. -------------------------------------------------------------------------------- /test-interp.txt: -------------------------------------------------------------------------------- 1 | # interp/atomic-load.txt 2 | # interp/atomic-rmw-add.txt 3 | # interp/atomic-rmw-and.txt 4 | # interp/atomic-rmw-cmpxchg.txt 5 | # interp/atomic-rmw-or.txt 6 | # interp/atomic-rmw-sub.txt 7 | # interp/atomic-rmw-xchg.txt 8 | # interp/atomic-rmw-xor.txt 9 | # interp/atomic-store.txt 10 | # interp/basic-logging.txt 11 | # interp/basic-tracing.txt 12 | interp/basic.txt 13 | # interp/binary.txt 14 | interp/binary-i32.txt 15 | interp/br.txt 16 | interp/brif-loop.txt 17 | interp/brif.txt 18 | interp/brtable.txt 19 | interp/call-zero-args.txt 20 | # interp/call.txt 21 | # interp/callimport-zero-args.txt 22 | interp/callindirect.txt 23 | # interp/cast.txt 24 | # interp/compare.txt 25 | interp/compare-i32.txt 26 | # interp/convert-sat.txt 27 | # interp/convert.txt 28 | interp/empty.txt 29 | interp/expr-block.txt 30 | interp/expr-br.txt 31 | interp/expr-brif.txt 32 | interp/expr-if.txt 33 | interp/if.txt 34 | # interp/import.txt 35 | # interp/load.txt 36 | interp/load-32.txt 37 | # interp/logging-all-opcodes.txt 38 | interp/loop.txt 39 | interp/memory-empty-segment.txt 40 | interp/nested-if.txt 41 | interp/return-void.txt 42 | interp/return.txt 43 | # interp/select.txt 44 | interp/select-i32.txt 45 | # interp/simd-basic.txt 46 | # interp/simd-binary.txt 47 | # interp/simd-splat.txt 48 | # interp/simd-unary.txt 49 | # interp/start.txt 50 | # interp/store.txt 51 | interp/store-i32.txt 52 | # interp/tracing-all-opcodes.txt 53 | # interp/trap-with-callstack.txt 54 | # interp/unary-extend.txt 55 | # interp/unary-extend-i32.txt 56 | # interp/unary.txt 57 | interp/unary-i32.txt 58 | interp/unreachable.txt 59 | -------------------------------------------------------------------------------- /wasm-blinky/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![feature(wasm_import_module)] 3 | 4 | const DIGITS: &[u8; 16] = b"0123456789abcdef"; 5 | 6 | mod host { 7 | #[wasm_import_module="host"] 8 | extern { 9 | pub fn write(ptr: *const u8, len: i32); 10 | pub fn led(id: i32); 11 | pub fn delay(ms: i32); 12 | } 13 | } 14 | 15 | fn u8_to_hex(c: u8) -> [u8; 2] { 16 | [DIGITS[((c >> 4) & 0xf) as usize], DIGITS[(c & 0xf) as usize]] 17 | } 18 | 19 | fn u16_to_hex(c: u16) -> [u8; 4] { 20 | let (a, b) = (u8_to_hex((c >> 8) as u8), u8_to_hex(c as u8)); 21 | [a[0], a[1], b[0], b[1]] 22 | } 23 | 24 | fn u32_to_hex(c: u32) -> [u8; 8] { 25 | let (a, b) = (u16_to_hex((c >> 16) as u16), u16_to_hex(c as u16)); 26 | [a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]] 27 | } 28 | 29 | fn write(buf: &[u8]) { 30 | unsafe { host::write(buf.as_ptr(), buf.len() as i32) }; 31 | } 32 | 33 | fn write_str(buf: &str) { 34 | write(buf.as_bytes()) 35 | } 36 | 37 | fn write_u8(value: u8) { 38 | write(u8_to_hex(value).as_ref()) 39 | } 40 | 41 | fn write_hex(value: u32) { 42 | let buf = u32_to_hex(value); 43 | write(buf.as_ref()); 44 | } 45 | 46 | static mut LED_STATE: bool = false; 47 | 48 | fn led() -> bool { 49 | unsafe { LED_STATE } 50 | } 51 | 52 | fn set_led(state: bool) { 53 | unsafe { 54 | LED_STATE = state; 55 | host::led(if LED_STATE { 1 } else { 0 }) 56 | } 57 | } 58 | 59 | fn toggle_led() { 60 | set_led(!led()) 61 | } 62 | 63 | fn delay(ms: u32) { 64 | unsafe { host::delay(ms as i32) } 65 | } 66 | 67 | #[no_mangle] 68 | pub extern "C" fn main() { 69 | let mut i = 0u8; 70 | loop { 71 | write_str("Hello, World: "); 72 | write_u8(i); 73 | write_str("\r\n"); 74 | toggle_led(); 75 | delay(500); 76 | i = i.wrapping_add(1); 77 | } 78 | } -------------------------------------------------------------------------------- /local_test/binary-32.txt: -------------------------------------------------------------------------------- 1 | ;;; TOOL: run-interp 2 | (module 3 | ;; i32 4 | (func (export "i32_add") (result i32) 5 | i32.const 1 6 | i32.const 2 7 | i32.add) 8 | (func (export "i32_sub") (result i32) 9 | i32.const 20 10 | i32.const 4 11 | i32.sub) 12 | (func (export "i32_mul") (result i32) 13 | i32.const 3 14 | i32.const 7 15 | i32.mul) 16 | (func (export "i32_div_s") (result i32) 17 | i32.const -4 18 | i32.const 2 19 | i32.div_s) 20 | (func (export "i32_div_u") (result i32) 21 | i32.const -4 22 | i32.const 2 23 | i32.div_u) 24 | (func (export "i32_rem_s") (result i32) 25 | i32.const -5 26 | i32.const 2 27 | i32.rem_s) 28 | (func (export "i32_rem_u") (result i32) 29 | i32.const -5 30 | i32.const 2 31 | i32.rem_u) 32 | (func (export "i32_and") (result i32) 33 | i32.const 11 34 | i32.const 5 35 | i32.and) 36 | (func (export "i32_or") (result i32) 37 | i32.const 11 38 | i32.const 5 39 | i32.or) 40 | (func (export "i32_xor") (result i32) 41 | i32.const 11 42 | i32.const 5 43 | i32.xor) 44 | (func (export "i32_shl") (result i32) 45 | i32.const -100 46 | i32.const 3 47 | i32.shl) 48 | (func (export "i32_shr_u") (result i32) 49 | i32.const -100 50 | i32.const 3 51 | i32.shr_u) 52 | (func (export "i32_shr_s") (result i32) 53 | i32.const -100 54 | i32.const 3 55 | i32.shr_s) 56 | (func (export "i32_rotl") (result i32) 57 | i32.const -100 58 | i32.const 3 59 | i32.rotl) 60 | (func (export "i32_rotr") (result i32) 61 | i32.const -100 62 | i32.const 3 63 | i32.rotr) 64 | ) 65 | (;; STDOUT ;;; 66 | i32_add() => i32:3 67 | i32_sub() => i32:16 68 | i32_mul() => i32:21 69 | i32_div_s() => i32:4294967294 70 | i32_div_u() => i32:2147483646 71 | i32_rem_s() => i32:4294967295 72 | i32_rem_u() => i32:1 73 | i32_and() => i32:1 74 | i32_or() => i32:15 75 | i32_xor() => i32:14 76 | i32_shl() => i32:4294966496 77 | i32_shr_u() => i32:536870899 78 | i32_shr_s() => i32:4294967283 79 | i32_rotl() => i32:4294966503 80 | i32_rotr() => i32:2684354547 81 | ;;; STDOUT ;;) 82 | -------------------------------------------------------------------------------- /TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | bobbin-wasm leverages the [wabt](https://github.com/WebAssembly/wabt) test suite by implementing binaries namded 4 | `wasm-objdump` and `wasm-interp` that generate output byte-for-byte identical to their wabt counterparts. 5 | 6 | ## Preparing the test environment 7 | 8 | Download and install the current version of [wabt](https://github.com/WebAssembly/wabt), and make a symlink from 9 | ./test/ to wabt/test. 10 | 11 | Create a local directory `./bin/`. 12 | 13 | ``` 14 | # mkdir ./bin/ 15 | ``` 16 | 17 | Add symlinks for all of the wabt binaries to the ./bin directory: 18 | 19 | ``` 20 | ln -s path_to_wabt/bin/ bin/ 21 | ``` 22 | 23 | Build and install the `wasm-objdump` and `wasm-interp` binaries into your test-bin directory. 24 | 25 | ``` 26 | $ cargo install --path . --root . 27 | ``` 28 | 29 | ## Running Tests 30 | 31 | To run a subset of the test suite, use `make test`. You can also run `make test-dump` or `make test-interp` to 32 | run just the `wasm-objdump` or `wasm-interp` tests. 33 | 34 | You should see something like this as the result: 35 | 36 | ``` 37 | $ make test 38 | cargo -q install --path . --root . --force 39 | cat test-dump.txt | grep -v \# | xargs test/run-tests.py --bindir /Users/jcsoo/.cargo/bin 40 | [+71|-0|%100] (0.96s) test/dump/unreachable.txt 41 | cat test-interp.txt | grep -v \# | xargs test/run-tests.py --bindir /Users/jcsoo/.cargo/bin 42 | [+19|-0|%100] (0.18s) test/interp/unreachable.txt 43 | $ 44 | ``` 45 | 46 | ## Configuring Tests 47 | 48 | bobbin-wasm doesn't currently support all the functionality of the real versions of `wasm-objdump` and `wasm-interp`, so 49 | testing them against the full test suite would produce an unreasonable number of failures. 50 | 51 | Instead, the files [test-dump.txt](./test-dump.txt) and [test-interp.txt](./test-interp.txt) list the names of the tests that 52 | should be run for each tool. Tests that are known to fail are commented out with a hash mark in the first column. 53 | 54 | Additionally, `local_test` contains copies of tests that have been useful during development. Some of these are tests 55 | from the wabt test suite that have had specific unsupported functionality removed. There is not currently a way to 56 | run these tests in an automated fashion. -------------------------------------------------------------------------------- /test-dump.txt: -------------------------------------------------------------------------------- 1 | # dump/atomic.txt 2 | # dump/bad-version-logging.txt 3 | # dump/bad-version.txt 4 | dump/basic.txt 5 | dump/basic_dump_only.txt 6 | dump/binary.txt 7 | dump/block-257-exprs-br.txt 8 | dump/block-257-exprs.txt 9 | dump/block.txt 10 | dump/br-block-named.txt 11 | dump/br-block.txt 12 | dump/br-loop-inner-expr.txt 13 | dump/br-loop-inner.txt 14 | dump/br-loop.txt 15 | dump/brif-loop.txt 16 | dump/brif.txt 17 | dump/brtable-empty.txt 18 | dump/brtable.txt 19 | dump/call.txt 20 | dump/callimport.txt 21 | dump/callindirect.txt 22 | dump/cast.txt 23 | dump/compare.txt 24 | dump/const.txt 25 | # dump/convert-sat.txt 26 | dump/convert.txt 27 | # dump/current-memory.txt 28 | # dump/debug-import-names.txt 29 | # dump/debug-names.txt 30 | dump/dedupe-sig.txt 31 | dump/drop.txt 32 | dump/export-multi.txt 33 | dump/expr-br.txt 34 | dump/expr-brif.txt 35 | dump/func-exported.txt 36 | dump/func-multi.txt 37 | dump/func-named.txt 38 | dump/getglobal.txt 39 | dump/getlocal-param.txt 40 | dump/getlocal.txt 41 | # dump/global.txt 42 | # dump/grow-memory.txt 43 | dump/hexfloat_f32.txt 44 | dump/hexfloat_f64.txt 45 | dump/if-then-else-list.txt 46 | dump/if-then-list.txt 47 | dump/if.txt 48 | # dump/import.txt 49 | dump/invalid-data-segment-no-memory.txt 50 | dump/invalid-data-segment-offset.txt 51 | dump/invalid-elem-segment-no-table.txt 52 | dump/invalid-elem-segment-offset.txt 53 | dump/load-aligned.txt 54 | dump/load.txt 55 | dump/locals.txt 56 | dump/loop-257-exprs-br.txt 57 | dump/loop-257-exprs.txt 58 | dump/loop.txt 59 | dump/memory-1-byte.txt 60 | # dump/memory-data-size.txt 61 | dump/memory-hex.txt 62 | # dump/memory.txt 63 | # dump/multi_file.txt 64 | # dump/mutable-global.txt 65 | dump/no-canonicalize.txt 66 | dump/nocheck.txt 67 | dump/nop.txt 68 | dump/param-multi.txt 69 | # dump/relocations.txt 70 | dump/result.txt 71 | dump/return.txt 72 | dump/select.txt 73 | dump/setglobal.txt 74 | dump/setlocal-param.txt 75 | dump/setlocal.txt 76 | dump/signatures.txt 77 | dump/start.txt 78 | dump/store-aligned.txt 79 | dump/store.txt 80 | dump/string-escape.txt 81 | dump/string-hex.txt 82 | dump/table.txt 83 | dump/tee_local.txt 84 | # dump/try-details.txt 85 | # dump/try-exports-details.txt 86 | # dump/try-exports.txt 87 | # dump/try-imports-details.txt 88 | # dump/try-imports.txt 89 | # dump/try.txt 90 | dump/unary-extend.txt 91 | dump/unary.txt 92 | dump/unreachable.txt 93 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use {ValueType}; 2 | use stack; 3 | use parser; 4 | use parser::module::Id; 5 | 6 | use core::{fmt, str}; 7 | 8 | #[derive(Debug, PartialEq)] 9 | pub enum Error { 10 | Unreachable, 11 | Return, 12 | End, 13 | Unimplemented(&'static str), 14 | InvalidOpcode(u8), 15 | UnimplementedOpcode(u8), 16 | InvalidBlockType, 17 | ScopesFull, 18 | FixupsFull, 19 | OutOfBounds, 20 | Leb128Overflow, 21 | UndefinedTableIndex { id: i32 }, 22 | SignatureMismatch, 23 | TypeCheck(&'static str), 24 | 25 | MissingSection { id: Id }, 26 | OutOfMemory, 27 | ReservedMemoryArea, 28 | NoHostFunction, 29 | NoHostImportFunction, 30 | InvalidHeader, 31 | InvalidSection { id: u32 }, 32 | InvalidGlobalKind { id: u8 }, 33 | UnknownSignatureType, 34 | UnknownExternalKind, 35 | InvalidReturnType, 36 | InvalidIfSignature, 37 | InvalidReservedValue, 38 | InvalidBranchTableDefault { id: u32, len: u32}, 39 | InvalidImport, 40 | InvalidLocal { id: u32 }, 41 | InvalidGlobal { id: u32 }, 42 | InvalidFunction { id: u32 }, 43 | InvalidSignature { id: u32 }, 44 | UnexpectedData { wanted: u32, got: u32 }, 45 | UnexpectedStackDepth { wanted: u32, got: u32}, 46 | UnexpectedTypeStackDepth { wanted: u32, got: u32}, 47 | UnexpectedType { wanted: ValueType, got: ValueType }, 48 | UnexpectedReturnValue { wanted: ValueType, got: ValueType}, 49 | UnexpectedReturnLength { got: u32 }, 50 | FmtError(fmt::Error), 51 | Utf8Error(str::Utf8Error), 52 | // OpcodeError(opcode::Error), 53 | StackError(stack::Error), 54 | ParserError(parser::Error) 55 | } 56 | 57 | impl From for Error { 58 | fn from(other: fmt::Error) -> Error { 59 | Error::FmtError(other) 60 | } 61 | } 62 | 63 | // impl From for Error { 64 | // fn from(other: opcode::Error) -> Error { 65 | // Error::OpcodeError(other) 66 | // } 67 | // } 68 | 69 | impl From for Error { 70 | fn from(other: stack::Error) -> Error { 71 | Error::StackError(other) 72 | } 73 | } 74 | 75 | impl From for Error { 76 | fn from(other: parser::Error) -> Error { 77 | Error::ParserError(other) 78 | } 79 | } 80 | 81 | impl From for Error { 82 | fn from(other: str::Utf8Error) -> Error { 83 | Error::Utf8Error(other) 84 | } 85 | } -------------------------------------------------------------------------------- /src/small_vec.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Index, IndexMut}; 2 | 3 | pub struct SmallVec<'a, T: 'a> { 4 | buf: &'a mut [T], 5 | pos: usize, 6 | } 7 | 8 | impl<'a, T: 'a> SmallVec<'a, T> { 9 | pub fn new(buf: &'a mut [T]) -> Self { 10 | SmallVec { buf, pos: 0} 11 | } 12 | 13 | pub fn into_buf(self) -> &'a mut [T] { 14 | self.buf 15 | } 16 | 17 | pub fn cap(&self) -> usize { 18 | self.buf.len() 19 | } 20 | 21 | pub fn len(&self) -> usize { 22 | self.pos 23 | } 24 | 25 | pub fn rem(&self) -> usize { 26 | self.cap() - self.len() 27 | } 28 | 29 | pub fn push(&mut self, value: T) { 30 | self.buf[self.pos] = value; 31 | self.pos += 1; 32 | } 33 | } 34 | 35 | impl<'a, T: 'a + Copy> SmallVec<'a, T> { 36 | pub fn pop(&mut self) -> Option { 37 | if self.pos > 0 { 38 | self.pos -= 1; 39 | Some(self.buf[self.pos]) 40 | } else { 41 | None 42 | } 43 | } 44 | } 45 | 46 | impl<'a, T: 'a> AsRef<[T]> for SmallVec<'a, T> { 47 | fn as_ref(&self) -> &[T] { 48 | &self.buf[..self.pos] 49 | } 50 | } 51 | 52 | impl<'a, T: 'a> AsMut<[T]> for SmallVec<'a, T> { 53 | fn as_mut(&mut self) -> &mut [T] { 54 | &mut self.buf[..self.pos] 55 | } 56 | } 57 | 58 | 59 | impl<'a, T: 'a> Index for SmallVec<'a, T> { 60 | type Output = T; 61 | fn index(&self, i: usize) -> &T { 62 | &self.buf[i] 63 | } 64 | } 65 | 66 | impl<'a, T: 'a> IndexMut for SmallVec<'a, T> { 67 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 68 | &mut self.buf[index] 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | 76 | #[test] 77 | fn test_smallvec() { 78 | let mut buf = [0u8; 16]; 79 | let mut v = SmallVec::new(&mut buf); 80 | for i in 0..16 { 81 | v.push(i as u8); 82 | } 83 | assert_eq!(v.cap(), 16); 84 | assert_eq!(v.len(), 16); 85 | assert_eq!(v.rem(), 0); 86 | 87 | for i in 0..16 { 88 | assert_eq!(v[i], i as u8); 89 | v[i] = v[i] * 2; 90 | assert_eq!(v[i], (i * 2) as u8); 91 | } 92 | 93 | for i in 0..16 { 94 | assert_eq!(v.pop(), Some((16 - i - 1) * 2)); 95 | } 96 | 97 | assert_eq!(v.len(), 0); 98 | 99 | } 100 | } -------------------------------------------------------------------------------- /src/parser/validator.rs: -------------------------------------------------------------------------------- 1 | use parser::error::Error; 2 | use parser::reader::FallibleIterator; 3 | use parser::module::{Module, Id, FuncItem}; 4 | 5 | pub fn validate(m: &Module) -> Result<(), Error> { 6 | if m.magic != 0x6d73_6100 { 7 | return Err(Error::InvalidMagic) 8 | } 9 | 10 | if m.version != 0x0000_0001 { 11 | return Err(Error::InvalidVersion) 12 | } 13 | 14 | let mut sections = m.sections(); 15 | while let Some(section) = sections.next()? { 16 | match section.id { 17 | Id::Type => { 18 | let mut function_types = section.function_types(); 19 | while let Some(t) = function_types.next()? { 20 | for _ in t.parameters { 21 | } 22 | for _ in t.results { 23 | } 24 | } 25 | }, 26 | Id::Function => { 27 | let mut functions = section.functions(); 28 | while let Some(_) = functions.next()? { 29 | } 30 | }, 31 | Id::Table => { 32 | let mut tables = section.tables(); 33 | while let Some(_) = tables.next()? { 34 | } 35 | }, 36 | Id::Memory => { 37 | let mut memory = section.memory(); 38 | while let Some(_) = memory.next()? { 39 | } 40 | }, 41 | Id::Export => { 42 | let mut exports = section.exports(); 43 | while let Some(_) = exports.next()? { 44 | } 45 | }, 46 | Id::Element => { 47 | let mut elements = section.elements(); 48 | while let Some(element) = elements.next()? { 49 | let mut items = element.iter(); 50 | while let Some(_) = items.next()? { 51 | } 52 | } 53 | }, 54 | Id::Code => { 55 | let mut code_section = section.code(); 56 | while let Some(code) = code_section.next()? { 57 | let mut item_iter = code.func.iter(); 58 | while let Some(item) = item_iter.next()? { 59 | match item { 60 | FuncItem::Local(_local) => { 61 | }, 62 | FuncItem::Instr(_instr) => { 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | Id::Data => { 69 | let mut data = section.data(); 70 | while let Some(_d) = data.next()? { 71 | } 72 | }, 73 | Id::Custom => { 74 | let _custom = section.custom()?; 75 | }, 76 | _ => {}, 77 | } 78 | } 79 | 80 | 81 | Ok(()) 82 | } -------------------------------------------------------------------------------- /gen_opcode.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | 3 | OP_TR = 0 4 | OP_T1 = 1 5 | OP_T2 = 2 6 | OP_T3 = 3 7 | OP_M = 4 8 | OP_PREFIX = 5 9 | OP_CODE = 6 10 | OP_NAME = 7 11 | OP_TEXT = 8 12 | 13 | def gen_prologue(out): 14 | out.write("""use types::ValueType; 15 | 16 | #[derive(Debug)] 17 | pub struct Op { 18 | pub tr: Option, 19 | pub t1: Option, 20 | pub t2: Option, 21 | pub m: u8, 22 | pub code: u8, 23 | pub text: &'static str, 24 | } 25 | 26 | pub const ___: Option = None; 27 | pub const I32: Option = Some(ValueType::I32); 28 | pub const I64: Option = Some(ValueType::I64); 29 | pub const F32: Option = Some(ValueType::F32); 30 | pub const F64: Option = Some(ValueType::F64); 31 | 32 | """) 33 | 34 | def to_name(row): 35 | return row[OP_TEXT].upper().replace('.','_').replace('/','_').replace('"','') 36 | 37 | def to_op_type(t): 38 | return t 39 | # if t == '___': 40 | # return 'None' 41 | # else: 42 | # return 'Some(ValueType::%s)' % t 43 | 44 | def gen_opcodes(out, rows): 45 | for row in rows: 46 | out.write("pub const {:24}: u8 = {};\n".format(to_name(row), row[OP_CODE])) 47 | out.write("\n\n") 48 | 49 | def gen_ops(out, rows): 50 | for row in rows: 51 | out.write("pub const {:24}: Op = Op {{".format(to_name(row)+'_OP')) 52 | out.write(" tr: %s, " % to_op_type(row[OP_TR])) 53 | out.write(" t1: %s, " % to_op_type(row[OP_T1])) 54 | out.write(" t2: %s, " % to_op_type(row[OP_T2])) 55 | # out.write(" t3: %s, " % to_op_type(row[OP_T3])) 56 | out.write(" m: %s, " % row[OP_M]) 57 | # out.write(" prefix: %s, " % row[OP_PREFIX]) 58 | out.write(" code: %s, " % row[OP_CODE]) 59 | out.write(" text: %s, " % row[OP_TEXT]) 60 | out.write("};\n") 61 | 62 | def gen_op_from(out, rows): 63 | out.write(""" 64 | impl Op { 65 | pub fn from_opcode(opc: u8) -> Option { 66 | Some(match opc { 67 | """) 68 | for row in rows: 69 | opc = to_name(row) 70 | op = to_name(row)+'_OP' 71 | out.write(" {:20} => {},\n".format(opc, op)) 72 | out.write(" {:20} => {},\n".format('_', 'return None')) 73 | 74 | out.write(""" }) 75 | } 76 | } 77 | """) 78 | 79 | def gen_code(out, rows): 80 | gen_prologue(out) 81 | gen_opcodes(out, rows) 82 | gen_ops(out, rows) 83 | gen_op_from(out, rows) 84 | 85 | def read_opcodes(f): 86 | rows = [] 87 | for line in f: 88 | if line.find('WABT_OPCODE(') != 0: 89 | continue 90 | row = line.replace('WABT_OPCODE(','').replace(')','').split(',') 91 | row = [c.strip() for c in row] 92 | 93 | if row[OP_TR] == 'V128': 94 | continue 95 | if row[OP_PREFIX] != '0': 96 | continue 97 | rows.append(row) 98 | return rows 99 | 100 | def main(): 101 | with open('wabt_opcode.def') as f: 102 | with open('src/parser/opcode.rs','w') as out: 103 | gen_code(out, read_opcodes(f)) 104 | 105 | if __name__ == '__main__': 106 | main() -------------------------------------------------------------------------------- /src/environ.rs: -------------------------------------------------------------------------------- 1 | use error::Error; 2 | use writer::Writer; 3 | use small_vec::SmallVec; 4 | use parser::module::Module; 5 | use memory_inst::MemoryInst; 6 | use module_inst::{ModuleInst, FuncInst, Value}; 7 | use types::{ImportDesc}; 8 | use interp::Interp; 9 | 10 | pub struct Config { 11 | memory_size: usize 12 | } 13 | 14 | impl Default for Config { 15 | fn default() -> Config { 16 | Config { 17 | memory_size: 8192, 18 | } 19 | } 20 | } 21 | 22 | pub trait HostHandler { 23 | fn import(&self, module: &str, export: &str, import_desc: &ImportDesc) -> Result; 24 | fn dispatch(&self, interp: &mut Interp, mem: &MemoryInst, type_index: usize, index: usize) -> Result<(), Error>; 25 | } 26 | 27 | pub struct Environment<'env, H: HostHandler> { 28 | cfg: Config, 29 | mem: MemoryInst<'env>, 30 | modules: SmallVec<'env, (&'env str, &'env ModuleInst<'env>)>, 31 | host_handler: H, 32 | } 33 | 34 | impl<'env, H: HostHandler> Environment<'env, H> { 35 | pub fn new(buf: &'env mut [u8], host_handler: H) -> (&'env mut [u8], Self) { 36 | Environment::new_with_config(buf, host_handler, Config::default()) 37 | } 38 | 39 | pub fn new_with_config(buf: &'env mut [u8], host_handler: H, cfg: Config) -> (&'env mut [u8], Self) { 40 | let (mem_buf, buf) = buf.split_at_mut(cfg.memory_size); 41 | let mem = MemoryInst::new(mem_buf, 1, None); 42 | let mut w = Writer::new(buf); 43 | let modules = w.alloc_smallvec(4); 44 | let buf = w.into_slice(); 45 | (buf, Environment { cfg, mem, modules, host_handler }) 46 | } 47 | 48 | pub fn cfg(&self) -> &Config { 49 | &self.cfg 50 | } 51 | 52 | pub fn mem(&self) -> &MemoryInst<'env> { 53 | &self.mem 54 | } 55 | 56 | pub fn load_module(&mut self, name: &'env str, buf: &'env mut [u8], module_data: &[u8]) -> Result<(&'env mut [u8], &'env ModuleInst<'env>), Error> { 57 | let m = Module::new(module_data)?; 58 | let (buf, mi) = ModuleInst::new(buf, &self, &self.mem, m)?; 59 | let mut w = Writer::new(buf); 60 | let mi = w.copy(mi)?; 61 | self.modules.push((name, mi)); 62 | let buf = w.into_slice(); 63 | Ok((buf, mi)) 64 | } 65 | 66 | pub fn import_host_function(&self, module: &str, export: &str, import_desc: &ImportDesc) -> Result { 67 | self.host_handler.import(module, export, import_desc) 68 | } 69 | 70 | pub fn call_host_function(&self, interp: &mut Interp, type_index: usize, index: usize) -> Result<(), Error> { 71 | self.host_handler.dispatch(interp, &self.mem, type_index, index) 72 | } 73 | 74 | pub fn call_module_function(&self, interp: &mut Interp, module_index: usize, function_index: usize) -> Result<(), Error> { 75 | let &(name, mi) = &self.modules[module_index]; 76 | let id = function_index; 77 | info!("calling {}:{}", name, function_index); 78 | 79 | match &mi.functions()[id] { 80 | &FuncInst::Host { type_index, module: _, name: _, host_index } => { 81 | self.call_host_function(interp, type_index, host_index) 82 | }, 83 | &FuncInst::Import { type_index, ref module, ref name, module_index, import_index } => { 84 | info!("CALL IMPORT: type_index: {} module: {}, name: {}, module_index: {}, import_index: {}", type_index, module, name, module_index, import_index); 85 | self.call_module_function(interp, module_index, import_index) 86 | }, 87 | &FuncInst::Local { type_index: _, function_index } => { 88 | if let Some(Value(v)) = interp.call(self, mi, function_index)? { 89 | Ok(interp.push(v)?) 90 | } else { 91 | Ok(()) 92 | } 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bobbin-wasm 2 | 3 | **bobbin-wasm** is a [WebAssembly](http://webassembly.org) library and interpreter written 4 | using the [Rust](https://www.rust-lang.org) programming language, designed to run in 5 | resource constrained embedded systems such as [ARM Cortex-M](https://www.arm.com/products/processors/cortex-m) 6 | SOCs with limited flash, RAM, and processing power, but also also to be useful as a general 7 | purpose interpreter to be embedded in applications for scripting and sandboxing purposes. 8 | 9 | To achieve those goals, **bobbin-wasm** does not depend on the Rust standard library and does 10 | not require an allocator. It is #[no_std] by default, though future versions could add opt-in 11 | features that make use of the standard library. It is also planned to make the entire parser, 12 | validator, and compiler / interpreter panic-free so that it is straightforward to use the 13 | system as a C library after wrappers are written. 14 | 15 | To read more about these goals, see [MOTIVATION](./MOTIVATION.md). 16 | 17 | The current version is based heavily on [WABT](https://github.com/WebAssembly/wabt), particularly 18 | the validation and typechecking components which are straight ports of their C++ counterparts. 19 | 20 | In fact, for testing and validation purposes, **bobbin-wasm** implements clones of `wasm-objdump` and 21 | `wasm-interp` that produce output that is byte-for-byte identical to the originals, allowing the 22 | use of the extensive `WABT` test suite. See [TESTING](./TESTING.md) for more details. 23 | 24 | ## Building 25 | 26 | To build the library and binaries, use `cargo build`. 27 | 28 | To run the object dumper directly without installing, use `cargo run --bin wasm-objdump`. To run 29 | the interpreter directly without installing, use `cargo run --bin wasm-interp`. 30 | 31 | To install the binaries, use `cargo install` or `cargo install --force` to overwrite your existing 32 | binaries. 33 | 34 | ## Hello Wasm 35 | 36 | ``` 37 | $ cargo install --force 38 | $ cd wasm-hello 39 | $ make run 40 | Compiling wasm-hello v0.1.0 (file:///Users/jcsoo/bobbin-dev/bobbin-wasm/wasm-hello) 41 | Finished release [optimized] target(s) in 1.11s 42 | wasm-gc target/wasm32-unknown-unknown/release/wasm_hello.wasm -o wasm_hello.gc.wasm 43 | wasm-opt -Os wasm_hello.gc.wasm -o wasm_hello.wasm 44 | wasm-interp --run-all-exports wasm_hello.wasm 45 | Hello, World 46 | run_hello() => 47 | ``` 48 | 49 | ## Current Status 50 | 51 | **bobbin-wasm** should be considered *extremely unstable*. 52 | 53 | ### Memory and Resource Limits 54 | 55 | The biggest limitation is that there are many memory and resource limits that are hard coded and set 56 | high enough to run the test suite, but not nearly high enough to run typical WASM binaries produced by the 57 | current Rust and C toolchains, even after optimization. These limits will be gradually changed to be 58 | configurable. 59 | 60 | ### Instruction Subset 61 | 62 | Currently, only 32-bit integer instructions are fully implemented. The parser and validator should 63 | recognize 32-bit floating point instructions but they will not execute in the interpreter. 64 | 65 | Eventually the goal is to provide support for 32-bit integer and floating point with a compile-time 66 | option for 32-bit integer only. 67 | 68 | ### Host API 69 | 70 | The host API is extremely crude and should be considered proof of concept. Eventually there should be 71 | a low-level API as well as higher-level APIs and macros and codegen tools to support type-safe 72 | Rust API implementation. 73 | 74 | 75 | ### Documentation and Examples 76 | 77 | Documentation is currently very sparse. [wasm-interp](src/bin/wasm-interp.rs) is the best starting point for anyone 78 | that wants build a simple application and or to use the host API. 79 | 80 | Cross-platform examples for running an interpreter on embedded devices will be released as soon as the underlying 81 | hardware crates are released. 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/parser/types.rs: -------------------------------------------------------------------------------- 1 | use parser::error::Error; 2 | use parser::reader::{Reader, Read}; 3 | use core::fmt; 4 | 5 | pub type Index = u32; 6 | pub type Depth = u32; 7 | 8 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 9 | pub struct Reserved(); 10 | 11 | impl<'a> Read for Reader<'a> { 12 | fn read(&mut self) -> Result { 13 | Ok({ 14 | self.read_var_u0()?; 15 | Reserved() 16 | }) 17 | } 18 | } 19 | 20 | impl fmt::Display for Reserved { 21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 | write!(f, "0") 23 | } 24 | } 25 | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 27 | #[repr(u8)] 28 | pub enum ValueType { 29 | Any = 0x00, 30 | Void = 0x40, 31 | Func = 0x60, 32 | AnyFunc = 0x70, 33 | I32 = 0x7f, 34 | I64 = 0x7e, 35 | F32 = 0x7d, 36 | F64 = 0x7c, 37 | } 38 | 39 | impl<'a> Read for Reader<'a> { 40 | fn read(&mut self) -> Result { 41 | Ok(match self.read_u8()? { 42 | 0x00 => ValueType::Any, 43 | 0x40 => ValueType::Void, 44 | 0x60 => ValueType::Func, 45 | 0x70 => ValueType::AnyFunc, 46 | 0x7f => ValueType::I32, 47 | 0x7e => ValueType::I64, 48 | 0x7d => ValueType::F32, 49 | 0x7c => ValueType::F64, 50 | _ => return Err(Error::InvalidValueType) 51 | }) 52 | } 53 | } 54 | 55 | impl From for ValueType { 56 | fn from(other: u8) -> Self { 57 | match other { 58 | 0x00 => ValueType::Any, 59 | 0x40 => ValueType::Void, 60 | 0x60 => ValueType::Func, 61 | 0x70 => ValueType::AnyFunc, 62 | 0x7f => ValueType::I32, 63 | 0x7e => ValueType::I64, 64 | 0x7d => ValueType::F32, 65 | 0x7c => ValueType::F64, 66 | _ => panic!("Unrecognized ValueType: 0x{:02x}", other) 67 | } 68 | } 69 | } 70 | 71 | impl fmt::Display for ValueType { 72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 73 | use self::ValueType::*; 74 | write!(f, "{}", match *self { 75 | Any => "any", 76 | I32 => "i32", 77 | I64 => "i64", 78 | F32 => "f32", 79 | F64 => "f64", 80 | AnyFunc => "anyfunc", 81 | Func => "func", 82 | Void => "void", 83 | }) 84 | } 85 | } 86 | #[derive(Debug)] 87 | pub struct FunctionType<'a> { 88 | pub functype: u8, 89 | pub parameters: &'a [ValueType], 90 | pub results: &'a [ValueType], 91 | } 92 | 93 | impl<'a> Read> for Reader<'a> { 94 | fn read(&mut self) -> Result, Error> { 95 | Ok({ 96 | let functype = self.read()?; 97 | if functype != 0x60 { 98 | return Err(Error::InvalidFunctionType); 99 | } 100 | let parameters = self.read()?; 101 | let results = self.read()?; 102 | FunctionType { functype, parameters, results } 103 | }) 104 | } 105 | } 106 | 107 | #[derive(Debug)] 108 | pub struct Limits { 109 | pub flag: bool, 110 | pub min: u32, 111 | pub max: Option, 112 | } 113 | 114 | impl<'a> Read for Reader<'a> { 115 | fn read(&mut self) -> Result { 116 | Ok({ 117 | let flag = self.read()?; 118 | let min = self.read()?; 119 | let max = if flag { 120 | self.read().map(Some)? 121 | } else { 122 | None 123 | }; 124 | Limits { flag, min, max } 125 | }) 126 | } 127 | } 128 | 129 | #[derive(Debug)] 130 | pub struct MemoryType { 131 | pub limits: Limits, 132 | } 133 | 134 | impl<'a> Read for Reader<'a> { 135 | fn read(&mut self) -> Result { 136 | Ok({ 137 | let limits = self.read()?; 138 | MemoryType { limits } 139 | }) 140 | } 141 | } 142 | 143 | #[derive(Debug)] 144 | pub struct TableType { 145 | pub elemtype: ValueType, 146 | pub limits: Limits, 147 | } 148 | 149 | impl<'a> Read for Reader<'a> { 150 | fn read(&mut self) -> Result { 151 | Ok({ 152 | let elemtype = self.read()?; 153 | if elemtype != ValueType::AnyFunc { 154 | return Err(Error::InvalidTableType) 155 | } 156 | let limits = self.read()?; 157 | TableType { elemtype, limits } 158 | }) 159 | } 160 | } 161 | 162 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 163 | pub struct GlobalType { 164 | pub valtype: ValueType, 165 | pub mutable: bool, 166 | } 167 | 168 | impl<'a> Read for Reader<'a> { 169 | fn read(&mut self) -> Result { 170 | Ok({ 171 | let valtype = self.read()?; 172 | let mutable = self.read()?; 173 | GlobalType { valtype, mutable } 174 | }) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/bin/wasm-board.rs: -------------------------------------------------------------------------------- 1 | extern crate bobbin_wasm as wasm; 2 | extern crate clap; 3 | extern crate log; 4 | extern crate env_logger; 5 | 6 | use std::process; 7 | use std::io::{self, stdout, Read, Write}; 8 | use std::fs::File; 9 | use std::path::Path; 10 | use std::thread; 11 | use std::time::Duration; 12 | 13 | // use log::Level; 14 | use clap::{App, Arg, ArgMatches}; 15 | 16 | use wasm::{ExportDesc, ImportDesc}; 17 | use wasm::interp::Interp; 18 | use wasm::environ::{Environment, HostHandler}; 19 | use wasm::module_inst::*; 20 | use wasm::memory_inst::MemoryInst; 21 | 22 | #[derive(Debug)] 23 | pub enum Error { 24 | IoError(io::Error), 25 | WasmError(wasm::Error), 26 | } 27 | 28 | impl From for Error { 29 | fn from(other: io::Error) -> Self { 30 | Error::IoError(other) 31 | } 32 | } 33 | 34 | impl From for Error { 35 | fn from(other: wasm::Error) -> Self { 36 | Error::WasmError(other) 37 | } 38 | } 39 | 40 | pub fn main() { 41 | env_logger::init(); 42 | let matches = App::new("interp") 43 | .arg(Arg::with_name("path") 44 | .required(true)) 45 | .get_matches(); 46 | 47 | if let Err(e) = run(matches) { 48 | eprintln!("Error: {:?}", e); 49 | process::exit(1); 50 | } 51 | } 52 | 53 | pub struct BoardHandler {} 54 | 55 | pub const WRITE_FN: usize = 0x0; 56 | pub const LED_FN: usize = 0x1; 57 | pub const DELAY_FN: usize = 0x2; 58 | 59 | impl HostHandler for BoardHandler { 60 | fn import(&self, _module: &str, export: &str, _import_desc: &ImportDesc) -> Result { 61 | Ok({ 62 | match export { 63 | "write" => WRITE_FN, 64 | "led" => LED_FN, 65 | "delay" => DELAY_FN, 66 | _ => return Err(wasm::Error::InvalidImport) 67 | } 68 | }) 69 | } 70 | 71 | fn dispatch(&self, interp: &mut Interp, mem: &MemoryInst, _type_index: usize, index: usize) -> Result<(), wasm::Error> { 72 | Ok({ 73 | match index { 74 | WRITE_FN => { 75 | let len = interp.pop()? as usize; 76 | let ptr = interp.pop()? as usize; 77 | let ptr = mem.map_addr(ptr)?; 78 | let buf = &mem.as_ref()[ptr..ptr+len]; 79 | stdout().write_all(buf).unwrap(); 80 | 81 | }, 82 | LED_FN => { 83 | let arg = interp.pop()?; 84 | if arg == 0 { 85 | println!("[LED OFF]"); 86 | } else { 87 | println!("[LED ON]"); 88 | } 89 | }, 90 | DELAY_FN => { 91 | let arg = interp.pop()?; 92 | if arg > 0 { 93 | thread::sleep(Duration::from_millis(arg as u64)); 94 | } 95 | } 96 | _ => return Err(wasm::Error::InvalidFunction { id: index as u32 }) 97 | } 98 | }) 99 | } 100 | 101 | } 102 | 103 | #[allow(dead_code)] 104 | fn load_file(file_name: &str) -> Result, Error> { 105 | let path = Path::new(file_name); 106 | let mut file = File::open(&path)?; 107 | let mut data: Vec = Vec::new(); 108 | file.read_to_end(&mut data)?; 109 | Ok(data) 110 | } 111 | 112 | pub fn run(matches: ArgMatches) -> Result<(), Error> { 113 | let path = Path::new(matches.value_of("path").unwrap()); 114 | let mut file = File::open(&path)?; 115 | let mut data: Vec = Vec::new(); 116 | file.read_to_end(&mut data)?; 117 | 118 | let path = path.file_stem().unwrap().to_str().unwrap(); 119 | 120 | let _out = String::new(); 121 | 122 | let h = BoardHandler {}; 123 | 124 | let buf = &mut [0u8; 65536 * 2]; 125 | let (buf, mut env) = Environment::new(buf, h); 126 | 127 | let (buf, mi) = env.load_module(path, buf, data.as_ref())?; 128 | 129 | // Interpreter 130 | 131 | let mut interp = Interp::new(buf); 132 | 133 | for e in mi.exports() { 134 | // println!("export: {:?}", e); 135 | if let ExportDesc::Func(index) = e.export_desc { 136 | let id = e.name; 137 | if id != "main" { 138 | continue 139 | } 140 | match &mi.functions()[index as usize] { 141 | &FuncInst::Local { type_index: _, function_index } => { 142 | // println!("Calling Local Function {}", function_index); 143 | match interp.call(&env, &mi, function_index as usize) { 144 | Ok(Some(value)) => { 145 | println!("{}() => {:?}", id, value); 146 | }, 147 | Ok(None) => { 148 | println!("{}() =>", id); 149 | }, 150 | Err(wasm::Error::Unreachable) => { 151 | println!("{}() => error: unreachable executed", id); 152 | }, 153 | Err(wasm::Error::UndefinedTableIndex { id: _ }) => { 154 | println!("{}() => error: undefined table index", id); 155 | }, 156 | Err(wasm::Error::SignatureMismatch) => { 157 | println!("{}() => error: indirect call signature mismatch", id); 158 | }, 159 | Err(e) => { 160 | println!("Error: {:?}", e); 161 | println!("---- Stack Dump ----"); 162 | 163 | let mut i = 0; 164 | while let Ok(value) = interp.pop() { 165 | println!("{}: {:?}", i, value); 166 | i += 1; 167 | } 168 | println!("---- END ----"); 169 | } 170 | } 171 | }, 172 | f @ _ => { 173 | println!("Unable to call {:?}", f); 174 | } 175 | } 176 | break; 177 | } 178 | } 179 | 180 | Ok(()) 181 | } 182 | 183 | -------------------------------------------------------------------------------- /src/bin/wasm-interp.rs: -------------------------------------------------------------------------------- 1 | extern crate bobbin_wasm as wasm; 2 | extern crate clap; 3 | extern crate log; 4 | extern crate env_logger; 5 | 6 | use std::process; 7 | use std::io::{self, Read}; 8 | use std::fs::File; 9 | use std::path::Path; 10 | 11 | // use log::Level; 12 | use clap::{App, Arg, ArgMatches}; 13 | 14 | use wasm::{ExportDesc, ImportDesc}; 15 | use wasm::interp::Interp; 16 | use wasm::environ::{Environment, HostHandler}; 17 | use wasm::module_inst::*; 18 | use wasm::memory_inst::MemoryInst; 19 | 20 | #[derive(Debug)] 21 | pub enum Error { 22 | IoError(io::Error), 23 | WasmError(wasm::Error), 24 | } 25 | 26 | impl From for Error { 27 | fn from(other: io::Error) -> Self { 28 | Error::IoError(other) 29 | } 30 | } 31 | 32 | impl From for Error { 33 | fn from(other: wasm::Error) -> Self { 34 | Error::WasmError(other) 35 | } 36 | } 37 | 38 | pub fn main() { 39 | env_logger::init(); 40 | let matches = App::new("interp") 41 | .arg(Arg::with_name("path") 42 | .required(true)) 43 | .arg(Arg::with_name("dump").long("dump")) 44 | .arg(Arg::with_name("no-compile").long("no-compile")) 45 | .arg(Arg::with_name("run-all-exports").long("run-all-exports")) 46 | .arg(Arg::with_name("host-print").long("host-print")) 47 | .get_matches(); 48 | 49 | if let Err(e) = run(matches) { 50 | eprintln!("Error: {:?}", e); 51 | process::exit(1); 52 | } 53 | } 54 | 55 | pub struct Handler {} 56 | 57 | pub const HELLO_FN: usize = 0x0; 58 | pub const PRINT_FN: usize = 0x1; 59 | pub const ADD_FN: usize = 0x2; 60 | 61 | impl HostHandler for Handler { 62 | fn import(&self, _module: &str, export: &str, _import_desc: &ImportDesc) -> Result { 63 | Ok({ 64 | match export { 65 | "hello" => HELLO_FN, 66 | "print" => PRINT_FN, 67 | "add" => ADD_FN, 68 | _ => return Err(wasm::Error::InvalidImport) 69 | } 70 | }) 71 | } 72 | 73 | fn dispatch(&self, interp: &mut Interp, _mem: &MemoryInst, _type_index: usize, index: usize) -> Result<(), wasm::Error> { 74 | Ok({ 75 | match index { 76 | HELLO_FN => println!("Hello, World"), 77 | PRINT_FN => { 78 | let arg = interp.pop()?; 79 | println!("called host host.print() => i32.{}", arg); 80 | }, 81 | ADD_FN => { 82 | let arg1 = interp.pop()?; 83 | let arg2 = interp.pop()?; 84 | let ret = arg1 + arg2; 85 | println!("{:?} + {:?} -> {:?}", arg1, arg2, ret); 86 | interp.push(ret)?; 87 | } 88 | _ => return Err(wasm::Error::InvalidFunction { id: index as u32 }) 89 | } 90 | }) 91 | } 92 | 93 | } 94 | 95 | #[allow(dead_code)] 96 | fn load_file(file_name: &str) -> Result, Error> { 97 | let path = Path::new(file_name); 98 | let mut file = File::open(&path)?; 99 | let mut data: Vec = Vec::new(); 100 | file.read_to_end(&mut data)?; 101 | Ok(data) 102 | } 103 | 104 | pub fn run(matches: ArgMatches) -> Result<(), Error> { 105 | let path = Path::new(matches.value_of("path").unwrap()); 106 | let mut file = File::open(&path)?; 107 | let mut data: Vec = Vec::new(); 108 | file.read_to_end(&mut data)?; 109 | 110 | let path = path.file_stem().unwrap().to_str().unwrap(); 111 | 112 | let _out = String::new(); 113 | 114 | let h = Handler {}; 115 | 116 | let buf = &mut [0u8; 65536 * 2]; 117 | let (buf, mut env) = Environment::new(buf, h); 118 | 119 | 120 | // let math = load_file("local_test/math.wasm")?; 121 | 122 | // println!("loading {:?}", "math"); 123 | 124 | // let (buf, _) = env.load_module("math", buf, math.as_ref())?; 125 | 126 | // println!("loading {:?}", path); 127 | 128 | 129 | 130 | let (buf, mi) = env.load_module(path, buf, data.as_ref())?; 131 | 132 | // Interpreter 133 | 134 | let mut interp = Interp::new(buf); 135 | 136 | if matches.is_present("run-all-exports") { 137 | 138 | for e in mi.exports() { 139 | // println!("export: {:?}", e); 140 | if let ExportDesc::Func(index) = e.export_desc { 141 | let id = &e.name; 142 | match &mi.functions()[index as usize] { 143 | &FuncInst::Local { type_index: _, function_index } => { 144 | // println!("Calling Local Function {}", function_index); 145 | match interp.call(&env, &mi, function_index as usize) { 146 | Ok(Some(value)) => { 147 | println!("{}() => {:?}", id, value); 148 | }, 149 | Ok(None) => { 150 | println!("{}() =>", id); 151 | }, 152 | Err(wasm::Error::Unreachable) => { 153 | println!("{}() => error: unreachable executed", id); 154 | }, 155 | Err(wasm::Error::UndefinedTableIndex { id: _ }) => { 156 | println!("{}() => error: undefined table index", id); 157 | }, 158 | Err(wasm::Error::SignatureMismatch) => { 159 | println!("{}() => error: indirect call signature mismatch", id); 160 | }, 161 | Err(e) => { 162 | println!("Error: {:?}", e); 163 | println!("---- Stack Dump ----"); 164 | 165 | let mut i = 0; 166 | while let Ok(value) = interp.pop() { 167 | println!("{}: {:?}", i, value); 168 | i += 1; 169 | } 170 | println!("---- END ----"); 171 | } 172 | } 173 | }, 174 | f @ _ => { 175 | println!("Unable to call {:?}", f); 176 | } 177 | } 178 | 179 | } 180 | } 181 | } 182 | 183 | Ok(()) 184 | } 185 | 186 | -------------------------------------------------------------------------------- /src/stack.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | pub type StackResult = Result; 4 | 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 6 | pub enum Error { 7 | Overflow, 8 | Underflow, 9 | OutOfBounds, 10 | } 11 | 12 | pub struct Stack<'a, T: 'a + Copy> { 13 | buf: &'a mut [T], 14 | pos: usize, 15 | } 16 | 17 | impl<'a, T: 'a + Copy> Stack<'a, T> { 18 | pub fn new(buf: &'a mut [T]) -> Self { 19 | Stack { buf: buf, pos: 0 } 20 | } 21 | 22 | #[inline] 23 | pub fn cap(&self) -> usize { 24 | self.buf.len() 25 | } 26 | 27 | #[inline] 28 | pub fn len(&self) -> usize { 29 | self.pos 30 | } 31 | 32 | #[inline] 33 | pub fn empty(&self) -> bool { 34 | self.pos == 0 35 | } 36 | 37 | #[inline] 38 | pub fn full(&self) -> bool { 39 | self.pos == self.buf.len() 40 | } 41 | 42 | #[inline] 43 | pub fn reset(&mut self) -> StackResult<()> { 44 | Ok(self.pos = 0) 45 | } 46 | 47 | #[inline] 48 | pub fn set_pos(&mut self, pos: usize) -> StackResult<()> { 49 | Ok(self.pos = pos) 50 | } 51 | 52 | #[inline] 53 | fn pre_incr(&mut self) -> StackResult { 54 | let pos = self.pos; 55 | if self.full() { 56 | Err(Error::Overflow) 57 | } else { 58 | self.pos += 1; 59 | Ok(pos) 60 | } 61 | } 62 | 63 | #[inline] 64 | fn post_decr(&mut self) -> StackResult { 65 | let mut pos = self.pos; 66 | if self.empty() { 67 | Err(Error::Underflow) 68 | } else { 69 | pos -= 1; 70 | self.pos = pos; 71 | Ok(pos) 72 | } 73 | } 74 | 75 | #[inline] 76 | pub fn push(&mut self, value: T) -> StackResult<()> { 77 | let pos = self.pre_incr()?; 78 | Ok(self.buf[pos] = value) 79 | } 80 | 81 | #[inline] 82 | pub fn pop(&mut self) -> StackResult { 83 | let pos = self.post_decr()?; 84 | Ok(self.buf[pos]) 85 | } 86 | 87 | /// Returns a copy of the item at the top of the stack. 88 | #[inline] 89 | pub fn top(&self) -> StackResult { 90 | if self.empty() { 91 | Err(Error::Underflow) 92 | } else { 93 | Ok(self.buf[self.pos - 1]) 94 | } 95 | } 96 | 97 | /// Returns a copy of the item `depth` items from the top. 98 | #[inline] 99 | pub fn peek(&self, depth: usize) -> StackResult { 100 | if depth >= self.pos { 101 | Err(Error::Underflow) 102 | } else { 103 | Ok(self.buf[self.pos - depth - 1]) 104 | } 105 | } 106 | 107 | /// Returns a mutable reference to the item `depth` items from the top. 108 | #[inline] 109 | pub fn pick(&mut self, depth: usize) -> StackResult<&mut T> { 110 | if depth >= self.pos { 111 | Err(Error::Underflow) 112 | } else { 113 | Ok(&mut self.buf[self.pos - depth - 1]) 114 | } 115 | } 116 | 117 | /// Drops `drop_count` items. If `keep_count` is 0, then items [0..`drop_count`) are 118 | /// deleted. If `keep_count` is 1, then items [1..`drop_count`+1) are deleted. 119 | #[inline] 120 | pub fn drop_keep(&mut self, drop_count: usize, keep_count: usize) -> StackResult<()> { 121 | assert!(keep_count <= 1, "keep_count must be 0 or 1"); 122 | if keep_count == 1 { 123 | *self.pick(drop_count)? = self.top()?; 124 | } 125 | Ok(self.pos -= drop_count) 126 | } 127 | 128 | #[inline] 129 | pub fn get(&self, index: usize) -> StackResult { 130 | if index < self.pos { 131 | Ok(self.buf[index]) 132 | } else { 133 | Err(Error::OutOfBounds) 134 | } 135 | } 136 | 137 | #[inline] 138 | pub fn set(&mut self, index: usize, value: T) -> StackResult<()> { 139 | if index < self.pos { 140 | Ok(self.buf[index] = value) 141 | } else { 142 | Err(Error::OutOfBounds) 143 | } 144 | } 145 | } 146 | 147 | impl<'a, T: 'a + Copy + fmt::Debug> Stack<'a, T> { 148 | pub fn dump(&self) { 149 | for i in 0..self.len() { 150 | let ptr = unsafe { 151 | self.buf.as_ptr().offset((self.pos - i - 1) as isize) 152 | }; 153 | info!("0x{:04}: {:p} {:?}", i, ptr, self.buf[self.pos - i - 1]); 154 | } 155 | } 156 | } 157 | 158 | 159 | impl<'a, T: 'a + Copy + fmt::Debug> fmt::Debug for Stack<'a, T> { 160 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 161 | write!(f, "Stack {{ len: {} }}", self.len()) 162 | } 163 | } 164 | 165 | 166 | #[cfg(test)] 167 | mod tests { 168 | use super::*; 169 | 170 | #[test] 171 | fn test_push_pop() { 172 | let mut buf = [0u8; 8]; 173 | let mut s = Stack::new(&mut buf); 174 | assert!(s.empty()); 175 | assert!(!s.full()); 176 | assert_eq!(s.cap(), 8); 177 | assert_eq!(s.len(), 0); 178 | 179 | for i in 0..8 { 180 | s.push(i as u8).unwrap(); 181 | } 182 | 183 | assert_eq!(s.push(8), Err(Error::Overflow)); 184 | assert_eq!(s.len(), 8); 185 | 186 | assert!(!s.empty()); 187 | assert!(s.full()); 188 | 189 | assert_eq!(s.top().unwrap(), 7); 190 | for i in 0..8 { 191 | assert_eq!(s.peek(i).unwrap(), (7-i) as u8); 192 | } 193 | assert_eq!(s.peek(8), Err(Error::Underflow)); 194 | 195 | for i in 0..8 { 196 | *s.pick(i).unwrap() = i as u8; 197 | } 198 | 199 | for i in 0..8 { 200 | assert_eq!(s.pop().unwrap(), i as u8); 201 | } 202 | } 203 | 204 | #[test] 205 | fn test_pick() { 206 | let mut buf = [0u8; 8]; 207 | let mut s = Stack::new(&mut buf); 208 | s.push(1).unwrap(); 209 | assert_eq!(*s.pick(0).unwrap(), 1); 210 | } 211 | 212 | #[test] 213 | fn test_drop_keep() { 214 | let mut buf = [0u8; 8]; 215 | let mut s = Stack::new(&mut buf); 216 | for i in 0..8 { 217 | s.push(i as u8).unwrap(); 218 | } 219 | s.drop_keep(8, 0).unwrap(); 220 | assert_eq!(s.len(), 0); 221 | 222 | for i in 0..8 { 223 | s.push(i as u8).unwrap(); 224 | } 225 | assert_eq!(s.len(), 8); 226 | s.drop_keep(7, 1).unwrap(); 227 | assert_eq!(s.len(), 1); 228 | assert_eq!(s.top().unwrap(), 7); 229 | } 230 | } -------------------------------------------------------------------------------- /src/floathex.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | // #[allow(unused_imports)] use core::num::Float; 3 | 4 | pub fn f32_parts(v: f32) -> (u8, u8, u32) { 5 | let v = v.to_bits(); 6 | let s = (v >> 31) as u8; 7 | let e = (v >> 23) as u8; 8 | let f = v << 9; 9 | (s, e, f) 10 | } 11 | 12 | pub fn f32_hex(out: &mut W, v: f32) -> fmt::Result { 13 | let (s, e, f) = f32_parts(v); 14 | 15 | match e { 16 | 0x00 => { 17 | if s == 1 { 18 | write!(out, "-")?; 19 | } 20 | write!(out, "0x0")?; 21 | if f > 0 { 22 | write!(out, ".{:x}", f)?; 23 | } 24 | write!(out, "p+0")?; 25 | }, 26 | 0xff => { 27 | if f & 1 << 31 != 0 { 28 | if s != 0 { 29 | write!(out, "-nan")?; 30 | } else { 31 | write!(out, "nan")?; 32 | } 33 | } else if f == 0 { 34 | if s != 0 { 35 | write!(out, "-inf")?; 36 | } else { 37 | write!(out, "inf")?; 38 | } 39 | } else { 40 | if s != 0 { 41 | write!(out, "-nan:0x{:x}", f >> 9)?; 42 | } else { 43 | write!(out, "nan:0x{:x}", f >> 9)?; 44 | } 45 | } 46 | }, 47 | _ => { 48 | if s == 1 { 49 | write!(out, "-")?; 50 | } 51 | write!(out, "0x1")?; 52 | if f > 0 { 53 | write!(out, ".")?; 54 | let mut f = f; 55 | while f != 0 { 56 | let v = f >> 28; 57 | write!(out, "{:x}", v)?; 58 | f = f << 4; 59 | } 60 | } 61 | let e = e as u32 as i32; 62 | let e = e - 127; 63 | write!(out, "p{:+}", e )?; 64 | } 65 | } 66 | Ok(()) 67 | } 68 | 69 | pub fn f64_parts(v: f64) -> (u8, u16, u64) { 70 | let v = v.to_bits(); 71 | let s = (v >> 63) as u8; 72 | let e = (((v >> 52) & (1 << 11) - 1)) as u16; 73 | let f = v << 12; 74 | (s, e, f) 75 | } 76 | 77 | pub fn f64_hex(out: &mut W, v: f64) -> fmt::Result { 78 | let (s, e, f) = f64_parts(v); 79 | 80 | match e { 81 | 0x000 => { 82 | if s == 1 { 83 | write!(out, "-")?; 84 | } 85 | write!(out, "0x0")?; 86 | if f > 0 { 87 | write!(out, ".{:x}", f)?; 88 | } 89 | write!(out, "p+0")?; 90 | }, 91 | 0x07ff => { 92 | if f & 1 << 63 != 0 { 93 | if s != 0 { 94 | write!(out, "-nan")?; 95 | } else { 96 | write!(out, "nan")?; 97 | } 98 | } else if f == 0 { 99 | if s != 0 { 100 | write!(out, "-inf")?; 101 | } else { 102 | write!(out, "inf")?; 103 | } 104 | } else { 105 | if s != 0 { 106 | write!(out, "-nan:0x{:x}", f >> 12)?; 107 | } else { 108 | write!(out, "nan:0x{:x}", f >> 12)?; 109 | } 110 | } 111 | }, 112 | _ => { 113 | if s == 1 { 114 | write!(out, "-")?; 115 | } 116 | write!(out, "0x1")?; 117 | if f > 0 { 118 | write!(out, ".")?; 119 | let mut f = f; 120 | while f != 0 { 121 | let v = f >> 60; 122 | write!(out, "{:x}", v)?; 123 | f = f << 4; 124 | } 125 | } 126 | let e = e as u32 as i32; 127 | let e = e - 1023; 128 | write!(out, "p{:+}", e )?; 129 | } 130 | } 131 | Ok(()) 132 | } 133 | 134 | #[cfg(test)] 135 | mod tests { 136 | use super::*; 137 | #[test] 138 | fn test_f32() { 139 | assert_eq!(f32_parts(1.0), (0, 127, 0)); 140 | assert_eq!(f32_parts(0.5), (0, 126, 0)); 141 | assert_eq!(f32_parts(2.0), (0, 128, 0)); 142 | assert_eq!(f32_parts(3.0), (0, 128, 0x8000_0000)); 143 | assert_eq!(f32_parts(4.0), (0, 129, 0)); 144 | assert_eq!(f32_hex(0.0),"0x0p+0"); 145 | assert_eq!(f32_hex(1.0),"0x1p+0"); 146 | assert_eq!(f32_hex(0.5),"0x1p-1"); 147 | assert_eq!(f32_hex(2.0),"0x1p+1"); 148 | assert_eq!(f32_hex(3.0),"0x1.8p+1"); 149 | assert_eq!(f32_hex(4.0),"0x1p+2"); 150 | assert_eq!(f32_hex(2.0f32.powi(8)),"0x1p+8"); 151 | assert_eq!(f32_hex(0.857421875f32), "0x1.b7p-1"); 152 | assert_eq!(f32_hex(f32::from_bits(0x65a9_6816)), "0x1.52d02cp+76"); 153 | assert_eq!(f32_hex(f32::from_bits(0x374f_2040)), "0x1.9e408p-17"); 154 | assert_eq!(f32_hex(f32::from_bits(0x7fc0_0000)), "nan"); 155 | assert_eq!(f32_hex(f32::from_bits(0x7f80_0abc)), "nan:0xabc"); 156 | assert_eq!(f32_hex(f32::from_bits(0xff80_0abc)), "-nan:0xabc"); 157 | assert_eq!(f32_hex(f32::from_bits(0x7f80_0000)), "inf"); 158 | } 159 | #[test] 160 | fn test_f64() { 161 | assert_eq!(f64_hex(0.0),"0x0p+0"); 162 | assert_eq!(f64_hex(1.0),"0x1p+0"); 163 | assert_eq!(f64_hex(0.5),"0x1p-1"); 164 | assert_eq!(f64_hex(2.0),"0x1p+1"); 165 | assert_eq!(f64_hex(3.0),"0x1.8p+1"); 166 | assert_eq!(f64_hex(4.0),"0x1p+2"); 167 | assert_eq!(f64_hex(2.0f64.powi(8)),"0x1p+8"); 168 | assert_eq!(f64_hex(0.857421875f64), "0x1.b7p-1"); 169 | // assert_eq!(f64_parts(f64::from_bits(0xbfef_9add_3c0e_56b8)), (0,0,0)); 170 | assert_eq!(f64_hex(f64::from_bits(0xbfef_9add_3c0e_56b8)), "-0x1.f9add3c0e56b8p-1"); 171 | assert_eq!(f64_hex(f64::from_bits(0x4019_21fb_5444_2d18)), "0x1.921fb54442d18p+2"); 172 | // assert_eq!(f64_parts(f64::from_bits(0x7ff8_0000_0000_0000)), (0,0,0)); 173 | assert_eq!(f64_hex(f64::from_bits(0x7ff8_0000_0000_0000)), "nan"); 174 | // assert_eq!(f64_parts(f64::from_bits(0xfff8_0000_0000_0000)), (0,0,0)); 175 | assert_eq!(f64_hex(f64::from_bits(0xfff8_0000_0000_0000)), "-nan"); 176 | assert_eq!(f64_hex(f64::from_bits(0x7ff0_0000_0000_0abc)), "nan:0xabc"); 177 | assert_eq!(f64_hex(f64::from_bits(0xfff0_0000_0000_0abc)), "-nan:0xabc"); 178 | assert_eq!(f64_hex(f64::from_bits(0x7ff0_0000_0000_0000)), "inf"); 179 | // assert_eq!(f64_parts(f64::from_bits(0xbfe0_0000_0000_0000)), (0,0,0)); 180 | assert_eq!(f64_hex(f64::from_bits(0xbfe0_0000_0000_0000)), "-0x1p-1"); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/page_table.rs: -------------------------------------------------------------------------------- 1 | // WASM pages are 64KB 2 | // Compilers often reserve a number of pages (16?) at the beginning of the address space. 3 | // and use additional pages which are fairly sparse 4 | // 5 | // This is a simple proof of concept mapping memory addresses to 4096 byte pages. 6 | // Other page sizes may be useful depending on the application and should eventually be supported. 7 | // 8 | // The page table maps from virtual page to physical page. In the map, 0xff indicates that the page 9 | // is not assigned. Values from 0x00 to 0xfe are valid, allowing 255 x 4096 (983,040) bytes of memory. 10 | 11 | use byteorder::{ByteOrder, LittleEndian}; 12 | 13 | pub const PAGE_SIZE: usize = 4096; 14 | 15 | #[derive(Debug, PartialEq, Eq)] 16 | pub enum Error { 17 | OutOfMemory, 18 | InvalidAlignment, 19 | } 20 | 21 | pub struct PageTable { 22 | map: [u8; 256], 23 | pages: u8, 24 | mapped: u8, 25 | } 26 | 27 | impl PageTable { 28 | pub fn new(pages: u8) -> Self { 29 | PageTable { map: [0xff; 256], pages, mapped: 0 } 30 | } 31 | 32 | pub fn pages(&self) -> u8 { 33 | self.pages 34 | } 35 | 36 | pub fn mapped(&self) -> u8 { 37 | self.mapped 38 | } 39 | 40 | pub fn get(&mut self, virt: u8) -> Option { 41 | match self.map[virt as usize] { 42 | 0xff => if self.mapped() < self.pages() { 43 | let p = self.mapped; 44 | self.map[virt as usize] = p; 45 | self.mapped += 1; 46 | Some(p) 47 | } else { 48 | None 49 | }, 50 | p @ _ => Some(p) 51 | } 52 | } 53 | 54 | pub fn allocate(&mut self, virt: u8) -> Option { 55 | if self.mapped < self.pages { 56 | let page = self.mapped; 57 | self.mapped += 1; 58 | self.map[virt as usize] = page; 59 | Some(page) 60 | } else { 61 | None 62 | } 63 | } 64 | 65 | } 66 | 67 | pub struct Memory<'a> { 68 | page_table: PageTable, 69 | buf: &'a mut [u8], 70 | } 71 | 72 | impl<'a> Memory<'a> { 73 | pub fn new(buf: &'a mut [u8]) -> Self { 74 | Memory { 75 | page_table: PageTable::new((buf.len() / PAGE_SIZE) as u8), 76 | buf, 77 | } 78 | } 79 | 80 | fn page(&self, addr: usize) -> u8 { 81 | (addr / PAGE_SIZE) as u8 82 | } 83 | 84 | fn offset(&self, addr: usize) -> usize { 85 | (addr % PAGE_SIZE) 86 | } 87 | 88 | pub fn pages(&self) -> usize { 89 | self.page_table.pages() as usize 90 | } 91 | 92 | pub fn mapped(&self) -> usize { 93 | self.page_table.mapped() as usize 94 | } 95 | 96 | pub fn map_addr(&mut self, v_addr: usize) -> Result { 97 | let v_page = self.page(v_addr); 98 | if let Some(p_page) = self.page_table.get(v_page) { 99 | Ok(((p_page as usize) * PAGE_SIZE) + self.offset(v_addr)) 100 | } else { 101 | Err(Error::OutOfMemory) 102 | } 103 | } 104 | 105 | pub fn get_u8(&mut self, v_addr: usize) -> Result { 106 | Ok(self.buf[self.map_addr(v_addr)?]) 107 | } 108 | 109 | pub fn set_u8(&mut self, v_addr: usize, value: u8) -> Result<(), Error> { 110 | Ok(self.buf[self.map_addr(v_addr)?] = value) 111 | } 112 | 113 | pub fn get_u16(&mut self, v_addr: usize) -> Result { 114 | if v_addr & 0b1 != 0 { return Err(Error::InvalidAlignment) } 115 | let p_addr = self.map_addr(v_addr)?; 116 | Ok(LittleEndian::read_u16(&self.buf[p_addr..])) 117 | } 118 | 119 | pub fn set_u16(&mut self, v_addr: usize, value: u16) -> Result<(), Error> { 120 | if v_addr & 0b1 != 0 { return Err(Error::InvalidAlignment) } 121 | let p_addr = self.map_addr(v_addr)?; 122 | Ok(LittleEndian::write_u16(&mut self.buf[p_addr..], value)) 123 | } 124 | 125 | pub fn get_u32(&mut self, v_addr: usize) -> Result { 126 | if v_addr & 0b11 != 0 { return Err(Error::InvalidAlignment) } 127 | let p_addr = self.map_addr(v_addr)?; 128 | Ok(LittleEndian::read_u32(&self.buf[p_addr..])) 129 | } 130 | 131 | pub fn set_u32(&mut self, v_addr: usize, value: u32) -> Result<(), Error> { 132 | if v_addr & 0b11 != 0 { return Err(Error::InvalidAlignment) } 133 | let p_addr = self.map_addr(v_addr)?; 134 | Ok(LittleEndian::write_u32(&mut self.buf[p_addr..], value)) 135 | } 136 | 137 | } 138 | 139 | 140 | #[cfg(test)] 141 | mod tests { 142 | use super::*; 143 | 144 | #[test] 145 | fn test_page_table() { 146 | for i in 0..255 { 147 | let mut pt = PageTable::new(i); 148 | assert_eq!(pt.pages(), i); 149 | assert_eq!(pt.mapped(), 0); 150 | 151 | for j in 0..i { 152 | assert_eq!(pt.mapped(), j); 153 | let p = pt.allocate(j); 154 | assert_eq!(p, Some(j)); 155 | assert_eq!(pt.get(j), p); 156 | 157 | } 158 | 159 | assert_eq!(pt.allocate(i), None); 160 | assert_eq!(pt.mapped(), i); 161 | } 162 | } 163 | 164 | #[test] 165 | fn test_memory() { 166 | let mut buf = [0u8; PAGE_SIZE * 2]; 167 | let mut mem = Memory::new(&mut buf); 168 | assert_eq!(mem.pages(), 2); 169 | assert_eq!(mem.mapped(), 0); 170 | 171 | for i in PAGE_SIZE..(PAGE_SIZE * 3) { 172 | mem.set_u8(i, i as u8).unwrap(); 173 | assert_eq!(mem.get_u8(i).unwrap(), i as u8); 174 | } 175 | 176 | assert!(mem.set_u8(PAGE_SIZE * 3, 0xff).is_err()); 177 | 178 | assert_eq!(mem.get_u16(PAGE_SIZE).unwrap(), 0x0100); 179 | assert_eq!(mem.get_u32(PAGE_SIZE).unwrap(), 0x03020100); 180 | 181 | mem.set_u16(PAGE_SIZE, 0xabcd).unwrap(); 182 | assert_eq!(mem.get_u16(PAGE_SIZE).unwrap(), 0xabcd); 183 | mem.set_u32(PAGE_SIZE, 0xabcd0123).unwrap(); 184 | assert_eq!(mem.get_u32(PAGE_SIZE).unwrap(), 0xabcd0123); 185 | 186 | assert_eq!(mem.get_u8(PAGE_SIZE + 0).unwrap(), 0x23); 187 | assert_eq!(mem.get_u8(PAGE_SIZE + 1).unwrap(), 0x01); 188 | assert_eq!(mem.get_u8(PAGE_SIZE + 2).unwrap(), 0xcd); 189 | assert_eq!(mem.get_u8(PAGE_SIZE + 3).unwrap(), 0xab); 190 | 191 | assert_eq!(mem.get_u16(PAGE_SIZE + 1), Err(Error::InvalidAlignment)); 192 | assert_eq!(mem.set_u16(PAGE_SIZE + 1, 0x0), Err(Error::InvalidAlignment)); 193 | 194 | assert_eq!(mem.get_u32(PAGE_SIZE + 1), Err(Error::InvalidAlignment)); 195 | assert_eq!(mem.get_u32(PAGE_SIZE + 2), Err(Error::InvalidAlignment)); 196 | assert_eq!(mem.get_u32(PAGE_SIZE + 3), Err(Error::InvalidAlignment)); 197 | assert_eq!(mem.set_u32(PAGE_SIZE + 1, 0), Err(Error::InvalidAlignment)); 198 | assert_eq!(mem.set_u32(PAGE_SIZE + 2, 0), Err(Error::InvalidAlignment)); 199 | assert_eq!(mem.set_u32(PAGE_SIZE + 3, 0), Err(Error::InvalidAlignment)); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/memory_inst.rs: -------------------------------------------------------------------------------- 1 | use Error; 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | 5 | use core::marker::PhantomData; 6 | use core::cell::{Cell, UnsafeCell}; 7 | use core::slice; 8 | use core::fmt; 9 | 10 | use page_table::PageTable; 11 | 12 | pub const PAGE_SIZE: usize = 65536; 13 | pub const MINI_SIZE: usize = 4096; 14 | 15 | pub struct MemoryInst<'a> { 16 | buf: *mut u8, 17 | buf_len: usize, 18 | page_table: UnsafeCell, 19 | num_pages: Cell, 20 | min_pages: usize, 21 | max_pages: usize, 22 | _phantom: PhantomData<&'a [u8]>, 23 | } 24 | 25 | impl<'a> MemoryInst<'a> { 26 | pub fn new(buf: &'a mut [u8], min_pages: usize, _max_pages: Option) -> MemoryInst { 27 | let buf_len = buf.len(); 28 | // let buf_pages = buf.len() / PAGE_SIZE; 29 | let mini_pages = buf.len() / MINI_SIZE; 30 | assert!(mini_pages <= 255); 31 | let buf = buf.as_mut_ptr(); 32 | let page_table = UnsafeCell::new(PageTable::new(mini_pages as u8)); 33 | let num_pages = Cell::new(min_pages); 34 | // let max_pages = if let Some(max_pages) = max_pages { 35 | // if max_pages < buf_pages { max_pages } else { buf_pages } 36 | // } else { 37 | // buf_pages 38 | // }; 39 | // Allow 40 | let max_pages = 64; 41 | MemoryInst { buf: buf as *mut u8, buf_len, page_table, num_pages, min_pages, max_pages, _phantom: PhantomData } 42 | } 43 | 44 | pub fn len(&self) -> usize { 45 | self.num_pages.get() * PAGE_SIZE 46 | } 47 | 48 | pub fn cap(&self) -> usize { 49 | self.max_pages * PAGE_SIZE 50 | } 51 | 52 | pub fn num_pages(&self) -> usize { 53 | self.num_pages.get() 54 | } 55 | 56 | pub fn reset(&self) { 57 | self.num_pages.set(self.min_pages); 58 | } 59 | 60 | pub fn current_memory(&self) -> i32 { 61 | self.num_pages.get() as i32 62 | } 63 | 64 | pub fn grow_memory(&self, pages: i32) -> i32 { 65 | info!("grow_memory({})", pages); 66 | let prev = self.current_memory(); 67 | let next = prev + pages; 68 | if next <= self.max_pages as i32 { 69 | self.num_pages.set(next as usize); 70 | info!(" num_pages: {}", self.num_pages()); 71 | info!(" len: {}", self.len()); 72 | prev 73 | } else { 74 | info!(" max_pages: {}", self.max_pages); 75 | -1 76 | } 77 | } 78 | 79 | pub fn as_ref(&self) -> &[u8] { 80 | unsafe { slice::from_raw_parts(self.buf, self.buf_len) } 81 | } 82 | 83 | pub fn as_mut(&self) -> &mut [u8] { 84 | unsafe { slice::from_raw_parts_mut(self.buf, self.buf_len) } 85 | } 86 | 87 | fn check_access(&self, index: usize, len: usize) -> Result<(), Error> { 88 | info!("check_access({}, {}) < {}", index, len, self.len()); 89 | if index + len <= self.len() { Ok(()) } else { Err(Error::OutOfBounds) } 90 | } 91 | 92 | fn mini_page(&self, addr: usize) -> u8 { 93 | (addr / MINI_SIZE) as u8 94 | } 95 | 96 | fn offset(&self, addr: usize) -> usize { 97 | (addr % MINI_SIZE) 98 | } 99 | 100 | fn page_table(&self) -> &mut PageTable { 101 | unsafe { &mut *self.page_table.get() } 102 | } 103 | 104 | pub fn map_addr(&self, v_addr: usize) -> Result { 105 | info!("map_addr({:08x})", v_addr); 106 | 107 | // NOTE: This is here because LLVM allocates static memory starting at 0x100_000, which 108 | // is too large for the page table to handle. 109 | 110 | let a_addr = match v_addr { 111 | addr @ _ if addr < 0x00_1000 => addr, 112 | addr @ _ if addr >= 0x10_0000 => (addr - 0x10_0000) + 0x1000, 113 | _ => return Err(Error::ReservedMemoryArea), 114 | }; 115 | 116 | info!(" a_addr: {:08x}", a_addr); 117 | 118 | let v_page = self.mini_page(a_addr); 119 | info!(" v_page: {:04x}", v_page); 120 | if let Some(p_page) = self.page_table().get(v_page) { 121 | info!(" p_page: {:04x}", p_page); 122 | info!(" p_offset: {:04x}", self.offset(a_addr)); 123 | let p_addr = ((p_page as usize) * MINI_SIZE) + self.offset(a_addr); 124 | info!(" p_addr: {:08x}", p_addr); 125 | Ok(p_addr) 126 | } else { 127 | Err(Error::OutOfMemory) 128 | } 129 | } 130 | 131 | pub fn get(&self, index: usize) -> u8 { 132 | self.as_ref()[self.map_addr(index).unwrap()] 133 | } 134 | 135 | pub fn set(&self, index: usize, value: u8) { 136 | self.as_mut()[self.map_addr(index).unwrap()] = value 137 | } 138 | 139 | pub fn load(&self, index: usize) -> Result { 140 | Ok({ 141 | self.check_access(index, 4)?; 142 | let index = self.map_addr(index)?; 143 | LittleEndian::read_i32(&self.as_ref()[index..]) 144 | }) 145 | } 146 | 147 | pub fn load16_s(&self, index: usize) -> Result { 148 | Ok({ 149 | self.check_access(index, 2)?; 150 | let index = self.map_addr(index)?; 151 | LittleEndian::read_i16(&self.as_ref()[index..]) as i16 as i32 152 | }) 153 | } 154 | 155 | pub fn load16_u(&self, index: usize) -> Result { 156 | Ok({ 157 | self.check_access(index, 2)?; 158 | let index = self.map_addr(index)?; 159 | LittleEndian::read_i16(&self.as_ref()[index..]) as u16 as i32 160 | }) 161 | } 162 | 163 | 164 | pub fn load8_u(&self, index: usize) -> Result { 165 | Ok({ 166 | self.check_access(index, 1)?; 167 | let index = self.map_addr(index)?; 168 | self.as_mut()[index] as u8 as i32 169 | }) 170 | } 171 | 172 | pub fn load8_s(&self, index: usize) -> Result { 173 | Ok({ 174 | self.check_access(index, 1)?; 175 | let index = self.map_addr(index)?; 176 | self.as_mut()[index] as i8 as i32 177 | }) 178 | } 179 | 180 | pub fn store(&self, index: usize, value: i32) -> Result<(), Error> { 181 | Ok({ 182 | self.check_access(index, 4)?; 183 | let index = self.map_addr(index)?; 184 | LittleEndian::write_i32(&mut self.as_mut()[index..], value) 185 | }) 186 | } 187 | 188 | pub fn store16(&self, index: usize, value: i32) -> Result<(), Error> { 189 | Ok({ 190 | self.check_access(index, 2)?; 191 | let index = self.map_addr(index)?; 192 | LittleEndian::write_i16(&mut self.as_mut()[index..], value as i16) 193 | }) 194 | } 195 | pub fn store8(&self, index: usize, value: i32) -> Result<(), Error> { 196 | Ok({ 197 | self.check_access(index, 1)?; 198 | let index = self.map_addr(index)?; 199 | self.as_mut()[index] = value as u8; 200 | }) 201 | } 202 | } 203 | 204 | impl<'a> fmt::Debug for MemoryInst<'a> { 205 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 206 | write!(f, "MemoryInst {{ len: {} / {} pages: {} / {} }}", 207 | self.len(), self.cap(), self.num_pages(), self.max_pages 208 | ) 209 | } 210 | } 211 | 212 | 213 | #[cfg(test)] 214 | mod tests { 215 | use super::*; 216 | 217 | #[test] 218 | fn test_memory() { 219 | let mut buf = [0u8; 256]; 220 | let mem = MemoryInst::new(&mut buf, 1, Some(4)); 221 | 222 | for i in 0..4 { 223 | mem.store(i * 4, i as i32).unwrap(); 224 | } 225 | 226 | for i in 0..4 { 227 | assert_eq!(mem.load(i * 4).unwrap(), i as i32); 228 | } 229 | 230 | } 231 | } -------------------------------------------------------------------------------- /opcodes.csv: -------------------------------------------------------------------------------- 1 | Mnemonic,Opcode,Immediates,Signature,Families 2 | unreachable,0x00,,() : (),Q 3 | block,0x02,$signature: block signature type,() : (), 4 | loop,0x03,$signature: block signature type,() : (), 5 | if,0x04,$signature: block signature type,($condition: i32) : (),B 6 | else,0x05,,($T[$any]) : ($T[$any]),B 7 | end,0x0b,,($T[$any]) : ($T[$any]), 8 | br,0x0c,$depth: varuint32,($T[$block_arity]) : ($T[$block_arity]),B Q 9 | br_if,0x0d,$depth: varuint32,"($T[$block_arity], $condition: i32) : ($T[$block_arity])",B 10 | br_table,0x0e,"$table: array of varuint32, $default: varuint32","($T[$block_arity], $index: i32) : ($T[$block_arity])",B Q 11 | return,0x0f,,($T[$block_arity]) : ($T[$block_arity]),B Q 12 | nop,0x01,,() : (), 13 | drop,0x1a,,($T[1]) : (), 14 | i32.const,0x41,$value: varsint32,() : (i32), 15 | i64.const,0x42,$value: varsint64,() : (i64), 16 | f32.const,0x43,$value: float32,() : (f32), 17 | f64.const,0x44,$value: float64,() : (f64), 18 | get_local,0x20,$id: varuint32,() : ($T[1]), 19 | set_local,0x21,$id: varuint32,($T[1]) : (), 20 | tee_local,0x22,$id: varuint32,($T[1]) : ($T[1]), 21 | get_global,0x23,$id: varuint32,() : ($T[1]), 22 | set_global,0x24,$id: varuint32,($T[1]) : (), 23 | select,0x1b,,"($T[1], $T[1], $condition: i32) : ($T[1])", 24 | call,0x10,$callee: varuint32,($T[$args]) : ($T[$returns]),L 25 | call_indirect,0x11,"$signature: varuint32, $reserved: varuint1","($T[$args], $callee: i32) : ($T[$returns])",L 26 | i32.add,0x6a,,"(i32, i32) : (i32)",G 27 | i64.add,0x7c,,"(i64, i64) : (i64)",G 28 | i32.sub,0x6b,,"(i32, i32) : (i32)",G 29 | i64.sub,0x7d,,"(i64, i64) : (i64)",G 30 | i32.mul,0x6c,,"(i32, i32) : (i32)",G 31 | i64.mul,0x7e,,"(i64, i64) : (i64)",G 32 | i32.div_s,0x6d,,"(i32, i32) : (i32)",S 33 | i64.div_s,0x7f,,"(i64, i64) : (i64)",S 34 | i32.div_u,0x6e,,"(i32, i32) : (i32)",U 35 | i64.div_u,0x80,,"(i64, i64) : (i64)",U 36 | i32.rem_s,0x6f,,"(i32, i32) : (i32)",S R 37 | i64.rem_s,0x81,,"(i64, i64) : (i64)",S R 38 | i32.rem_u,0x70,,"(i32, i32) : (i32)",U R 39 | i64.rem_u,0x82,,"(i64, i64) : (i64)",U R 40 | i32.and,0x71,,"(i32, i32) : (i32)",G 41 | i64.and,0x83,,"(i64, i64) : (i64)",G 42 | i32.or,0x72,,"(i32, i32) : (i32)",G 43 | i64.or,0x84,,"(i64, i64) : (i64)",G 44 | i32.xor,0x73,,"(i32, i32) : (i32)",G 45 | i64.xor,0x85,,"(i64, i64) : (i64)",G 46 | i32.shl,0x74,,"(i32, i32) : (i32)","T, G" 47 | i64.shl,0x86,,"(i64, i64) : (i64)","T, G" 48 | i32.shr_s,0x75,,"(i32, i32) : (i32)","T, S" 49 | i64.shr_s,0x87,,"(i64, i64) : (i64)","T, S" 50 | i32.shr_u,0x76,,"(i32, i32) : (i32)","T, U" 51 | i64.shr_u,0x88,,"(i64, i64) : (i64)","T, U" 52 | i32.rotl,0x77,,"(i32, i32) : (i32)","T, G" 53 | i64.rotl,0x89,,"(i64, i64) : (i64)","T, G" 54 | i32.rotr,0x78,,"(i32, i32) : (i32)","T, G" 55 | i64.rotr,0x8a,,"(i64, i64) : (i64)","T, G" 56 | i32.clz,0x67,,(i32) : (i32),G 57 | i64.clz,0x79,,(i64) : (i64),G 58 | i32.ctz,0x68,,(i32) : (i32),G 59 | i64.ctz,0x7a,,(i64) : (i64),G 60 | i32.popcnt,0x69,,(i32) : (i32),G 61 | i64.popcnt,0x7b,,(i64) : (i64),G 62 | i32.eqz,0x45,,(i32) : (i32),G 63 | i64.eqz,0x50,,(i64) : (i32),G 64 | f32.add,0x92,,"(f32, f32) : (f32)",F 65 | f64.add,0xa0,,"(f64, f64) : (f64)",F 66 | f32.sub,0x93,,"(f32, f32) : (f32)",F 67 | f64.sub,0xa1,,"(f64, f64) : (f64)",F 68 | f32.mul,0x94,,"(f32, f32) : (f32)",F 69 | f64.mul,0xa2,,"(f64, f64) : (f64)",F 70 | f32.div,0x95,,"(f32, f32) : (f32)",F 71 | f64.div,0xa3,,"(f64, f64) : (f64)",F 72 | f32.sqrt,0x91,,(f32) : (f32),F 73 | f64.sqrt,0x9f,,(f64) : (f64),F 74 | f32.min,0x96,,"(f32, f32) : (f32)",F 75 | f64.min,0xa4,,"(f64, f64) : (f64)",F 76 | f32.max,0x97,,"(f32, f32) : (f32)",F 77 | f64.max,0xa5,,"(f64, f64) : (f64)",F 78 | f32.ceil,0x8d,,(f32) : (f32),F 79 | f64.ceil,0x9b,,(f64) : (f64),F 80 | f32.floor,0x8e,,(f32) : (f32),F 81 | f64.floor,0x9c,,(f64) : (f64),F 82 | f32.trunc,0x8f,,(f32) : (f32),F 83 | f64.trunc,0x9d,,(f64) : (f64),F 84 | f32.nearest,0x90,,(f32) : (f32),F 85 | f64.nearest,0x9e,,(f64) : (f64),F 86 | f32.abs,0x8b,,(f32) : (f32),E 87 | f64.abs,0x99,,(f64) : (f64),E 88 | f32.neg,0x8c,,(f32) : (f32),E 89 | f64.neg,0x9a,,(f64) : (f64),E 90 | f32.copysign,0x98,,"(f32, f32) : (f32)",E 91 | f64.copysign,0xa6,,"(f64, f64) : (f64)",E 92 | i32.eq,0x46,,"(i32, i32) : (i32)","C, G" 93 | i64.eq,0x51,,"(i64, i64) : (i32)","C, G" 94 | i32.ne,0x47,,"(i32, i32) : (i32)","C, G" 95 | i64.ne,0x52,,"(i64, i64) : (i32)","C, G" 96 | i32.lt_s,0x48,,"(i32, i32) : (i32)","C, S" 97 | i64.lt_s,0x53,,"(i64, i64) : (i32)","C, S" 98 | i32.lt_u,0x49,,"(i32, i32) : (i32)","C, U" 99 | i64.lt_u,0x54,,"(i64, i64) : (i32)","C, U" 100 | i32.le_s,0x4c,,"(i32, i32) : (i32)","C, S" 101 | i64.le_s,0x57,,"(i64, i64) : (i32)","C, S" 102 | i32.le_u,0x4d,,"(i32, i32) : (i32)","C, U" 103 | i64.le_u,0x58,,"(i64, i64) : (i32)","C, U" 104 | i32.gt_s,0x4a,,"(i32, i32) : (i32)","C, S" 105 | i64.gt_s,0x55,,"(i64, i64) : (i32)","C, S" 106 | i32.gt_u,0x4b,,"(i32, i32) : (i32)","C, U" 107 | i64.gt_u,0x56,,"(i64, i64) : (i32)","C, U" 108 | i32.ge_s,0x4e,,"(i32, i32) : (i32)","C, S" 109 | i64.ge_s,0x59,,"(i64, i64) : (i32)","C, S" 110 | i32.ge_u,0x4f,,"(i32, i32) : (i32)","C, U" 111 | i64.ge_u,0x5a,,"(i64, i64) : (i32)","C, U" 112 | f32.eq,0x5b,,"(f32, f32) : (i32)","C, F" 113 | f64.eq,0x61,,"(f64, f64) : (i32)","C, F" 114 | f32.ne,0x5c,,"(f32, f32) : (i32)","C, F" 115 | f64.ne,0x62,,"(f64, f64) : (i32)","C, F" 116 | f32.lt,0x5d,,"(f32, f32) : (i32)","C, F" 117 | f64.lt,0x63,,"(f64, f64) : (i32)","C, F" 118 | f32.le,0x5f,,"(f32, f32) : (i32)","C, F" 119 | f64.le,0x65,,"(f64, f64) : (i32)","C, F" 120 | f32.gt,0x5e,,"(f32, f32) : (i32)","C, F" 121 | f64.gt,0x64,,"(f64, f64) : (i32)","C, F" 122 | f32.ge,0x60,,"(f32, f32) : (i32)","C, F" 123 | f64.ge,0x66,,"(f64, f64) : (i32)","C, F" 124 | i32.wrap/i64,0xa7,,(i64) : (i32),G 125 | i64.extend_s/i32,0xac,,(i32) : (i64),S 126 | i64.extend_u/i32,0xad,,(i32) : (i64),U 127 | i32.trunc_s/f32,0xa8,,(f32) : (i32),"F, S" 128 | i32.trunc_s/f64,0xaa,,(f64) : (i32),"F, S" 129 | i64.trunc_s/f32,0xae,,(f32) : (i64),"F, S" 130 | i64.trunc_s/f64,0xb0,,(f64) : (i64),"F, S" 131 | i32.trunc_u/f32,0xa9,,(f32) : (i32),"F, U" 132 | i32.trunc_u/f64,0xab,,(f64) : (i32),"F, U" 133 | i64.trunc_u/f32,0xaf,,(f32) : (i64),"F, U" 134 | i64.trunc_u/f64,0xb1,,(f64) : (i64),"F, U" 135 | f32.demote/f64,0xb6,,(f64) : (f32),F 136 | f64.promote/f32,0xbb,,(f32) : (f64),F 137 | f32.convert_s/i32,0xb2,,(i32) : (f32),"F, S" 138 | f32.convert_s/i64,0xb4,,(i64) : (f32),"F, S" 139 | f64.convert_s/i32,0xb7,,(i32) : (f64),"F, S" 140 | f64.convert_s/i64,0xb9,,(i64) : (f64),"F, S" 141 | f32.convert_u/i32,0xb3,,(i32) : (f32),"F, U" 142 | f32.convert_u/i64,0xb5,,(i64) : (f32),"F, U" 143 | f64.convert_u/i32,0xb8,,(i32) : (f64),"F, U" 144 | f64.convert_u/i64,0xba,,(i64) : (f64),"F, U" 145 | i32.reinterpret/f32,0xbc,,(f32) : (i32), 146 | i64.reinterpret/f64,0xbd,,(f64) : (i64), 147 | f32.reinterpret/i32,0xbe,,(i32) : (f32), 148 | f64.reinterpret/i64,0xbf,,(i64) : (f64), 149 | i32.load,0x28,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i32),"M, G" 150 | i64.load,0x29,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, G" 151 | f32.load,0x2a,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (f32),"M, E" 152 | f64.load,0x2b,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (f64),"M, E" 153 | i32.store,0x36,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i32) : ()","M, G" 154 | i64.store,0x37,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i64) : ()","M, G" 155 | f32.store,0x38,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: f32) : ()","M, F" 156 | f64.store,0x39,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: f64) : ()","M, F" 157 | i32.load8_s,0x2c,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i32),"M, S" 158 | i32.load16_s,0x2e,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i32),"M, S" 159 | i64.load8_s,0x30,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, S" 160 | i64.load16_s,0x32,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, S" 161 | i64.load32_s,0x34,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, S" 162 | i32.load8_u,0x2d,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i32),"M, U" 163 | i32.load16_u,0x2f,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i32),"M, U" 164 | i64.load8_u,0x31,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, U" 165 | i64.load16_u,0x33,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, U" 166 | i64.load32_u,0x35,"$flags: memflags, $offset: varuPTR",($base: iPTR) : (i64),"M, U" 167 | i32.store8,0x3a,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i32) : ()","M, G" 168 | i32.store16,0x3b,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i32) : ()","M, G" 169 | i64.store8,0x3c,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i64) : ()","M, G" 170 | i64.store16,0x3d,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i64) : ()","M, G" 171 | i64.store32,0x3e,"$flags: memflags, $offset: varuPTR","($base: iPTR, $value: i64) : ()","M, G" 172 | mem.grow,0x40,$reserved: varuint1,($delta: iPTR) : (iPTR),Z 173 | mem.size,0x3f,$reserved: varuint1,() : (iPTR),Z -------------------------------------------------------------------------------- /src/parser/reader.rs: -------------------------------------------------------------------------------- 1 | use parser::Error; 2 | use parser::util::*; 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | pub use fallible_iterator::FallibleIterator; 5 | 6 | use core::str; 7 | use core::mem; 8 | use core::marker::PhantomData; 9 | 10 | pub struct Reader<'a> { 11 | buf: &'a [u8] 12 | } 13 | 14 | impl<'a> Reader<'a> { 15 | pub fn new(buf: &'a [u8]) -> Self { 16 | Reader { buf } 17 | } 18 | 19 | pub fn len(&self) -> usize { 20 | self.buf.len() 21 | } 22 | 23 | pub fn into_slice(self) -> &'a [u8] { 24 | self.buf 25 | } 26 | 27 | pub fn rest(&self) -> &'a [u8] { 28 | self.buf 29 | } 30 | 31 | pub fn offset_from(&self, base: &Reader<'a>) -> usize { 32 | let base_ptr = base.buf.as_ptr() as *const u8 as usize; 33 | let ptr = self.buf.as_ptr() as *const u8 as usize; 34 | debug_assert!(base_ptr <= ptr); 35 | debug_assert!(ptr + self.buf.len() <= base_ptr + base.buf.len()); 36 | ptr - base_ptr 37 | } 38 | 39 | pub fn read_slice(&mut self, len: usize) -> Result<&'a [u8], Error> { 40 | if self.len() < len { 41 | Err(Error::UnexpectedEof) 42 | } else { 43 | let val = &self.buf[..len]; 44 | self.buf = &self.buf[len..]; 45 | Ok(val) 46 | } 47 | } 48 | 49 | pub fn read_u8(&mut self) -> Result { 50 | let slice = self.read_slice(1)?; 51 | Ok(slice[0]) 52 | } 53 | 54 | pub fn read_u32(&mut self) -> Result { 55 | let slice = self.read_slice(4)?; 56 | Ok( 57 | (slice[0] as u32) << 0 | 58 | (slice[1] as u32) << 8 | 59 | (slice[2] as u32) << 16 | 60 | (slice[3] as u32) << 24 61 | ) 62 | } 63 | 64 | pub fn read_var_u0(&mut self) -> Result<(), Error> { 65 | match self.read_u8()? { 66 | 0 => Ok(()), 67 | _ => Err(Error::InvalidU0), 68 | } 69 | } 70 | 71 | pub fn read_var_u1(&mut self) -> Result { 72 | match self.read_u8()? { 73 | 0 => Ok(false), 74 | 1 => Ok(true), 75 | _ => Err(Error::InvalidU1), 76 | } 77 | } 78 | 79 | pub fn read_var_u7(&mut self) -> Result { 80 | let byte = self.read_u8()?; 81 | if byte & 0x80 == 0 { 82 | Ok(byte) 83 | } else { 84 | Err(Error::InvalidU7) 85 | } 86 | } 87 | 88 | pub fn read_var_u32(&mut self) -> Result { 89 | let mut byte = self.read_u8()?; 90 | if byte & 0x80 == 0 { 91 | Ok(byte.into()) 92 | } else { 93 | let mut value = (byte & 0x7f) as u32; 94 | let mut shift = 7; 95 | loop { 96 | byte = self.read_u8()?; 97 | value |= ((byte & 0x7f) as u32) << shift; 98 | if byte & 0x80 == 0 { break } 99 | shift += 7; 100 | if shift > 31 { 101 | return Err(Error::InvalidU32) 102 | } 103 | } 104 | Ok(value) 105 | } 106 | } 107 | pub fn read_var_u64(&mut self) -> Result { 108 | let mut byte = self.read_u8()?; 109 | if byte & 0x80 == 0 { 110 | Ok(byte as u64) 111 | } else { 112 | let mut value = (byte & 0x7f) as u64; 113 | let mut shift = 7; 114 | loop { 115 | byte = self.read_u8()?; 116 | value |= ((byte & 0x7f) as u64) << shift; 117 | if byte & 0x80 == 0 { break } 118 | shift += 7; 119 | if shift > 63 { return Err(Error::InvalidU64) } 120 | } 121 | Ok(value) 122 | } 123 | } 124 | 125 | #[inline] 126 | pub fn read_var_i32(&mut self) -> Result { 127 | let mut byte = self.read_u8()?; 128 | if byte & 0x80 == 0 { 129 | if byte & 0x40 != 0 { 130 | byte |= 0x80; 131 | } 132 | Ok(byte as i8 as i32) 133 | } else { 134 | let mut value = (byte & 0x7f) as u32; 135 | let mut shift = 7; 136 | loop { 137 | byte = self.read_u8()?; 138 | value |= ((byte & 0x7f) as u32) << shift; 139 | if byte & 0x80 == 0 { 140 | if byte & 0x40 != 0 { 141 | value |= 0xffff_ff80 << shift; 142 | } 143 | break 144 | } 145 | shift += 7; 146 | if shift > 31 { return Err(Error::InvalidI32) } 147 | } 148 | Ok(value as i32) 149 | } 150 | } 151 | 152 | #[inline] 153 | pub fn read_var_i64(&mut self) -> Result { 154 | let mut byte = self.read_u8()?; 155 | if byte & 0x80 == 0 { 156 | if byte & 0x40 != 0 { 157 | byte |= 0x80; 158 | } 159 | Ok(byte as i8 as i64) 160 | } else { 161 | let mut value = (byte & 0x7f) as u64; 162 | let mut shift = 7; 163 | loop { 164 | byte = self.read_u8()?; 165 | value |= ((byte & 0x7f) as u64) << shift; 166 | if byte & 0x80 == 0 { 167 | if byte & 0x40 != 0 { 168 | value |= 0xffff_ffff_ffff_ff80 << shift; 169 | } 170 | break 171 | } 172 | shift += 7; 173 | if shift > 63 { return Err(Error::InvalidI64) } 174 | } 175 | Ok(value as i64) 176 | } 177 | } 178 | 179 | #[inline] 180 | pub fn read_f32(&mut self) -> Result { 181 | self.read_slice(4).map(LittleEndian::read_f32) 182 | } 183 | 184 | #[inline] 185 | pub fn read_f64(&mut self) -> Result { 186 | self.read_slice(8).map(LittleEndian::read_f64) 187 | } 188 | } 189 | 190 | impl<'a> Clone for Reader<'a> { 191 | fn clone(&self) -> Self { 192 | Reader::new(self.buf) 193 | } 194 | } 195 | 196 | pub trait Read { 197 | fn read(&mut self) -> Result; 198 | } 199 | 200 | impl<'a> Read for Reader<'a> { 201 | fn read(&mut self) -> Result { 202 | self.read_u8().map(|v| v != 0) 203 | } 204 | } 205 | 206 | impl<'a> Read for Reader<'a> { 207 | fn read(&mut self) -> Result { 208 | self.read_u8() 209 | } 210 | } 211 | 212 | impl<'a> Read for Reader<'a> { 213 | fn read(&mut self) -> Result { 214 | self.read_var_u32() 215 | } 216 | } 217 | 218 | impl<'a> Read for Reader<'a> { 219 | fn read(&mut self) -> Result { 220 | self.read_var_i32() 221 | } 222 | } 223 | 224 | impl<'a> Read for Reader<'a> { 225 | fn read(&mut self) -> Result { 226 | self.read_var_i64() 227 | } 228 | } 229 | 230 | impl<'a> Read for Reader<'a> { 231 | fn read(&mut self) -> Result { 232 | self.read_f32() 233 | } 234 | } 235 | 236 | impl<'a> Read for Reader<'a> { 237 | fn read(&mut self) -> Result { 238 | self.read_f64() 239 | } 240 | } 241 | 242 | impl<'a, T> Read<&'a [T]> for Reader<'a> { 243 | fn read(&mut self) -> Result<&'a [T], Error> { 244 | let len = self.read_var_u32()? as usize; 245 | let t_len = len * mem::size_of::(); 246 | self.read_slice(t_len).map(from_byte_slice) 247 | } 248 | } 249 | 250 | impl<'a> Read<&'a str> for Reader<'a> { 251 | fn read(&mut self) -> Result<&'a str, Error> { 252 | match str::from_utf8(self.read()?) { 253 | Ok(s) => Ok(s), 254 | Err(_) => Err(Error::InvalidUtf8) 255 | } 256 | } 257 | } 258 | 259 | pub struct ReadIterator<'a, T> { 260 | r: Reader<'a>, 261 | _phantom: PhantomData, 262 | } 263 | 264 | impl<'a, T> ReadIterator<'a, T> { 265 | pub fn new(r: Reader<'a>) -> Self { 266 | ReadIterator { r, _phantom: PhantomData } 267 | } 268 | } 269 | 270 | impl<'a, T> FallibleIterator for ReadIterator<'a, T> 271 | where Reader<'a>: Read { 272 | type Item = T; 273 | type Error = Error; 274 | fn next(&mut self) -> Result, Self::Error> { 275 | if self.r.len() > 0 { 276 | Ok(Some(self.r.read()?)) 277 | } else { 278 | Ok(None) 279 | } 280 | } 281 | } 282 | 283 | 284 | pub struct SectionReadIterator<'a, T> { 285 | r: Reader<'a>, 286 | count: u32, 287 | _phantom: PhantomData, 288 | } 289 | 290 | impl<'a, T> SectionReadIterator<'a, T> { 291 | pub fn new(r: Reader<'a>) -> Self { 292 | SectionReadIterator { r, count: 0, _phantom: PhantomData } 293 | } 294 | } 295 | 296 | impl<'a, T> FallibleIterator for SectionReadIterator<'a, T> 297 | where Reader<'a>: Read { 298 | type Item = T; 299 | type Error = Error; 300 | fn next(&mut self) -> Result, Self::Error> { 301 | if self.count == 0 { 302 | self.r.read_var_u32()?; 303 | } 304 | if self.r.len() > 0 { 305 | self.count += 1; 306 | Ok(Some(self.r.read()?)) 307 | } else { 308 | Ok(None) 309 | } 310 | } 311 | } 312 | 313 | 314 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Jonathan Soo 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/cursor.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{ByteOrder, LittleEndian}; 2 | 3 | use core::fmt; 4 | 5 | #[derive(Clone)] 6 | pub struct Cursor<'a> { 7 | buf: &'a [u8], 8 | pos: usize, 9 | } 10 | 11 | impl<'a> Cursor<'a> { 12 | pub fn new(buf: &'a [u8]) -> Self { 13 | Cursor { buf, pos: 0 } 14 | } 15 | 16 | pub fn new_at_pos(buf: &'a [u8], pos: usize ) -> Self { 17 | Cursor { buf, pos } 18 | } 19 | 20 | pub fn pos(&self) -> usize { 21 | self.pos 22 | } 23 | 24 | pub fn len(&self) -> usize { 25 | self.buf.len() 26 | } 27 | 28 | pub fn done(&self) -> bool { 29 | self.buf.len() == 0 30 | } 31 | 32 | pub fn advance(&mut self, count: usize) -> &mut Self { 33 | self.pos += count; 34 | self.buf = &self.buf[count..]; 35 | self 36 | } 37 | 38 | pub fn advanced(&self, count: usize) -> Self { 39 | let pos = self.pos + count; 40 | let buf = &self.buf[count..]; 41 | Cursor { buf, pos } 42 | } 43 | 44 | pub fn rest(&self) -> Cursor<'a> { 45 | Cursor { buf: self.buf, pos: self.pos } 46 | } 47 | 48 | pub fn split(&mut self, len: usize) -> Cursor<'a> { 49 | let buf = &self.buf[..len]; 50 | let pos = self.pos; 51 | self.advance(len); 52 | Cursor { buf, pos } 53 | } 54 | 55 | pub fn slice(&mut self, len: usize) -> &'a [u8] { 56 | let v = &self.buf[..len]; 57 | self.advance(len); 58 | v 59 | } 60 | 61 | // Read Unsigned 62 | 63 | pub fn read_u8(&mut self) -> u8 { 64 | let v = self.buf[0]; 65 | self.advance(1); 66 | v 67 | } 68 | 69 | pub fn read_u16(&mut self) -> u16 { 70 | let v = LittleEndian::read_u16(self.buf); 71 | self.advance(2); 72 | v 73 | } 74 | 75 | pub fn read_u32(&mut self) -> u32 { 76 | let v = LittleEndian::read_u32(self.buf); 77 | self.advance(4); 78 | v 79 | } 80 | 81 | pub fn read_u64(&mut self) -> u64 { 82 | let v = LittleEndian::read_u64(self.buf); 83 | self.advance(8); 84 | v 85 | } 86 | 87 | // Read Signed 88 | pub fn read_i8(&mut self) -> i8 { 89 | let v = self.read_u8() as i8; 90 | self.advance(1); 91 | v 92 | } 93 | 94 | pub fn read_i16(&mut self) -> i16 { 95 | let v = LittleEndian::read_i16(self.buf); 96 | self.advance(2); 97 | v 98 | } 99 | 100 | pub fn read_i32(&mut self) -> i32 { 101 | let v = LittleEndian::read_i32(self.buf); 102 | self.advance(4); 103 | v 104 | } 105 | 106 | pub fn read_i64(&mut self) -> i64 { 107 | let v = LittleEndian::read_i64(self.buf); 108 | self.advance(8); 109 | v 110 | } 111 | 112 | // Read Floating Point 113 | 114 | pub fn read_f32(&mut self) -> f32 { 115 | let v = LittleEndian::read_f32(self.buf); 116 | self.advance(4); 117 | v 118 | } 119 | 120 | pub fn read_f64(&mut self) -> f64 { 121 | let v = LittleEndian::read_f64(self.buf); 122 | self.advance(8); 123 | v 124 | } 125 | 126 | // Read LEB128 127 | 128 | pub fn read_var_u1(&mut self) -> u8 { 129 | let byte = self.read_u8(); 130 | if byte & 0x80 == 0 { 131 | if byte > 1 { panic!("Overflow") } 132 | byte 133 | } else { 134 | panic!("Overflow"); 135 | } 136 | } 137 | 138 | pub fn read_var_u7(&mut self) -> u8 { 139 | let byte = self.read_u8(); 140 | if byte & 0x80 == 0 { 141 | byte 142 | } else { 143 | panic!("Overflow"); 144 | } 145 | } 146 | 147 | pub fn read_var_u32(&mut self) -> u32 { 148 | let mut byte = self.read_u8(); 149 | if byte & 0x80 == 0 { 150 | byte as u32 151 | } else { 152 | let mut value = (byte & 0x7f) as u32; 153 | let mut shift = 7; 154 | loop { 155 | byte = self.read_u8(); 156 | value |= ((byte & 0x7f) as u32) << shift; 157 | if byte & 0x80 == 0 { break } 158 | shift += 7; 159 | if shift > 31 { panic!("Overflow") } 160 | } 161 | value 162 | } 163 | } 164 | 165 | 166 | pub fn read_var_u64(&mut self) -> u64 { 167 | let mut byte = self.read_u8(); 168 | if byte & 0x80 == 0 { 169 | byte as u64 170 | } else { 171 | let mut value = (byte & 0x7f) as u64; 172 | let mut shift = 7; 173 | loop { 174 | byte = self.read_u8(); 175 | value |= ((byte & 0x7f) as u64) << shift; 176 | if byte & 0x80 == 0 { break } 177 | shift += 7; 178 | if shift > 63 { panic!("Overflow") } 179 | } 180 | value 181 | } 182 | } 183 | 184 | pub fn read_var_i7(&mut self) -> i8 { 185 | let mut byte = self.read_u8(); 186 | if byte & 0x80 == 0 { 187 | if byte & 0x40 != 0 { 188 | byte |= 0x80; 189 | } 190 | byte as i8 191 | } else { 192 | panic!("Overflow") 193 | } 194 | } 195 | 196 | pub fn read_var_i32(&mut self) -> i32 { 197 | let mut byte = self.read_u8(); 198 | if byte & 0x80 == 0 { 199 | if byte & 0x40 != 0 { 200 | byte |= 0x80; 201 | } 202 | byte as i8 as i32 203 | } else { 204 | let mut value = (byte & 0x7f) as u32; 205 | let mut shift = 7; 206 | loop { 207 | byte = self.read_u8(); 208 | value |= ((byte & 0x7f) as u32) << shift; 209 | if byte & 0x80 == 0 { 210 | if byte & 0x40 != 0 { 211 | value |= 0xffff_ff80 << shift; 212 | } 213 | break 214 | } 215 | shift += 7; 216 | if shift > 31 { panic!("Overflow") } 217 | } 218 | value as i32 219 | } 220 | } 221 | 222 | pub fn read_var_i64(&mut self) -> i64 { 223 | let mut byte = self.read_u8(); 224 | if byte & 0x80 == 0 { 225 | if byte & 0x40 != 0 { 226 | byte |= 0x80; 227 | } 228 | byte as i8 as i64 229 | } else { 230 | let mut value = (byte & 0x7f) as u64; 231 | let mut shift = 7; 232 | loop { 233 | byte = self.read_u8(); 234 | value |= ((byte & 0x7f) as u64) << shift; 235 | if byte & 0x80 == 0 { 236 | if byte & 0x40 != 0 { 237 | value |= 0xffff_ffff_ffff_ff80 << shift; 238 | } 239 | break 240 | } 241 | shift += 7; 242 | if shift > 63 { panic!("Overflow") } 243 | } 244 | value as i64 245 | } 246 | } 247 | } 248 | 249 | impl<'a> AsRef<[u8]> for Cursor<'a> { 250 | fn as_ref(&self) -> &[u8] { 251 | self.buf 252 | } 253 | } 254 | 255 | impl<'a> fmt::Debug for Cursor<'a> { 256 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 257 | write!(f, "Cursor {{ pos: {} len: {} buf: [", self.pos, self.buf.len())?; 258 | for (i, b) in self.buf.iter().enumerate() { 259 | if i != 0 { write!(f, " ")?; } 260 | write!(f, "{:02x}", b)?; 261 | } 262 | write!(f, "] }}")?; 263 | Ok(()) 264 | } 265 | 266 | } 267 | 268 | #[cfg(test)] 269 | mod tests { 270 | use super::Cursor; 271 | 272 | #[test] 273 | fn test_read_u7() { 274 | fn read_u7(buf: &[u8]) -> u8 { 275 | let mut c = Cursor::new(buf); 276 | let v = c.read_var_u7(); 277 | assert!(c.done()); 278 | v 279 | } 280 | for i in 0u8..128 { 281 | assert_eq!(read_u7(&[i]), i); 282 | } 283 | } 284 | 285 | #[test] 286 | fn test_read_i7() { 287 | fn read_i7(buf: &[u8]) -> i8 { 288 | let mut c = Cursor::new(buf); 289 | let v = c.read_var_i7(); 290 | assert!(c.done()); 291 | v 292 | } 293 | for i in 0u8..64 { 294 | assert_eq!(read_i7(&[i]), i as i8); 295 | } 296 | assert_eq!(read_i7(&[0b0111_1111]), -1); 297 | assert_eq!(read_i7(&[0b0111_1110]), -2); 298 | assert_eq!(read_i7(&[0b0111_1100]), -4); 299 | assert_eq!(read_i7(&[0b0111_1000]), -8); 300 | assert_eq!(read_i7(&[0b0111_0000]), -16); 301 | assert_eq!(read_i7(&[0b0110_0000]), -32); 302 | assert_eq!(read_i7(&[0b0100_0000]), -64); 303 | } 304 | 305 | #[test] 306 | fn test_read_u32() { 307 | fn read_u32(buf: &[u8]) -> u32 { 308 | let mut c = Cursor::new(buf); 309 | let v = c.read_var_u32(); 310 | assert!(c.done()); 311 | v 312 | } 313 | 314 | // 0-7 bits 315 | assert_eq!(read_u32(&[0b0000000]), 0b0000000); 316 | assert_eq!(read_u32(&[0b1111111]), 0b1111111); 317 | 318 | // 8-14 bits 319 | assert_eq!(read_u32(&[0b10000000, 0b0000001]), 0b1_0000000); 320 | assert_eq!(read_u32(&[0b10000001, 0b0000001]), 0b1_0000001); 321 | assert_eq!(read_u32(&[0b11111111, 0b0000001]), 0b1_1111111); 322 | 323 | // 15-21 bits 324 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000); 325 | assert_eq!(read_u32(&[0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001); 326 | assert_eq!(read_u32(&[0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000); 327 | assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111); 328 | 329 | // 22-28 bits 330 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000000); 331 | assert_eq!(read_u32(&[0b10000001, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000001); 332 | assert_eq!(read_u32(&[0b10000000, 0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001_0000000); 333 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000_0000000); 334 | assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111_1111111); 335 | 336 | // 29-32 bits 337 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b00001000]), 0b1000_0000000_0000000_0000000_0000000); 338 | assert_eq!(read_u32(&[0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b00001000]), 0b1000_0001000_0000100_0000010_0000001); 339 | assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00001111]), 0b1111_1111111_1111111_1111111_1111111); 340 | //assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00010000]), 0b1111_1111111_1111111_1111111_1111111); 341 | } 342 | 343 | #[test] 344 | fn test_read_u64() { 345 | fn read_u64(buf: &[u8]) -> u64 { 346 | let mut c = Cursor::new(buf); 347 | let v = c.read_var_u64(); 348 | assert!(c.done()); 349 | v 350 | } 351 | 352 | // 0-7 bits 353 | assert_eq!(read_u64(&[0b0000000]), 0b0000000); 354 | assert_eq!(read_u64(&[0b1111111]), 0b1111111); 355 | 356 | // 8-14 bits 357 | assert_eq!(read_u64(&[0b10000000, 0b0000001]), 0b1_0000000); 358 | assert_eq!(read_u64(&[0b10000001, 0b0000001]), 0b1_0000001); 359 | assert_eq!(read_u64(&[0b11111111, 0b0000001]), 0b1_1111111); 360 | 361 | // 15-21 bits 362 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000); 363 | assert_eq!(read_u64(&[0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001); 364 | assert_eq!(read_u64(&[0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000); 365 | assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111); 366 | 367 | // 22-28 bits 368 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000000); 369 | assert_eq!(read_u64(&[0b10000001, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000001); 370 | assert_eq!(read_u64(&[0b10000000, 0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001_0000000); 371 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000_0000000); 372 | assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111_1111111); 373 | 374 | // 29-32 bits 375 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b00001000]), 0b1000_0000000_0000000_0000000_0000000); 376 | assert_eq!(read_u64(&[0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b00001000]), 0b1000_0001000_0000100_0000010_0000001); 377 | assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00001111]), 0b1111_1111111_1111111_1111111_1111111); 378 | //assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00010000]), 0b1111_1111111_1111111_1111111_1111111); 379 | } 380 | 381 | 382 | #[test] 383 | fn test_read_i32() { 384 | fn read_i32(buf: &[u8]) -> i32 { 385 | let mut c = Cursor::new(buf); 386 | let v = c.read_var_i32(); 387 | assert!(c.done()); 388 | v 389 | } 390 | 391 | // 0-7 bits 392 | assert_eq!(read_i32(&[0x0]), 0); 393 | assert_eq!(read_i32(&[0x1]), 1); 394 | assert_eq!(read_i32(&[0x2]), 2); 395 | assert_eq!(read_i32(&[0x7f]), -1); 396 | assert_eq!(read_i32(&[0x7e]), -2); 397 | 398 | assert_eq!(read_i32(&[0xff, 0]), 127); 399 | assert_eq!(read_i32(&[0x80, 1]), 128); 400 | assert_eq!(read_i32(&[0x81, 1]), 129); 401 | 402 | assert_eq!(read_i32(&[0x81, 0x7f]), -127); 403 | assert_eq!(read_i32(&[0x80, 0x7f]), -128); 404 | assert_eq!(read_i32(&[0xff, 0x7e]), -129); 405 | } 406 | 407 | #[test] 408 | fn test_read_i64() { 409 | fn read_i64(buf: &[u8]) -> i64 { 410 | let mut c = Cursor::new(buf); 411 | let v = c.read_var_i64(); 412 | assert!(c.done()); 413 | v 414 | } 415 | 416 | // 0-7 bits 417 | assert_eq!(read_i64(&[0x0]), 0); 418 | assert_eq!(read_i64(&[0x1]), 1); 419 | assert_eq!(read_i64(&[0x2]), 2); 420 | assert_eq!(read_i64(&[0x7f]), -1); 421 | assert_eq!(read_i64(&[0x7e]), -2); 422 | 423 | assert_eq!(read_i64(&[0xff, 0]), 127); 424 | assert_eq!(read_i64(&[0x80, 1]), 128); 425 | assert_eq!(read_i64(&[0x81, 1]), 129); 426 | 427 | assert_eq!(read_i64(&[0x81, 0x7f]), -127); 428 | assert_eq!(read_i64(&[0x80, 0x7f]), -128); 429 | assert_eq!(read_i64(&[0xff, 0x7e]), -129); 430 | } 431 | } -------------------------------------------------------------------------------- /src/reader.rs: -------------------------------------------------------------------------------- 1 | use Error; 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | use core::ops::{Index, Range}; 5 | 6 | pub type ReaderResult = Result; 7 | 8 | pub struct Reader<'a> { 9 | buf: &'a [u8], 10 | pos: usize, 11 | } 12 | 13 | impl<'a> Reader<'a> { 14 | pub fn new(buf: &'a [u8]) -> Self { 15 | Reader { buf: buf, pos: 0 } 16 | } 17 | 18 | #[inline] 19 | pub fn len(&self) -> usize { 20 | self.buf.len() 21 | } 22 | 23 | #[inline] 24 | pub fn pos(&self) -> usize { 25 | self.pos 26 | } 27 | 28 | #[inline] 29 | pub fn set_pos(&mut self, pos: usize) { 30 | self.pos = pos 31 | } 32 | 33 | #[inline] 34 | pub fn incr(&mut self) { 35 | self.pos += 1 36 | } 37 | 38 | #[inline] 39 | pub fn advance(&mut self, offset: usize) { 40 | self.pos += offset 41 | } 42 | 43 | #[inline] 44 | pub fn done(&self) -> bool { 45 | self.pos >= self.buf.len() 46 | } 47 | 48 | #[inline] 49 | pub fn remaining(&self) -> usize { 50 | self.buf.len() - self.pos 51 | } 52 | 53 | #[inline] 54 | fn readT>(&mut self, size: usize, f: F) -> ReaderResult { 55 | if self.pos() + size > self.buf.len() { return Err(Error::End) } 56 | let v = f(&self.buf[self.pos..self.pos+size]); 57 | self.pos += size; 58 | Ok(v) 59 | } 60 | 61 | #[inline] 62 | pub fn read_u8(&mut self) -> ReaderResult { 63 | self.read(1, |buf| buf[0]) 64 | } 65 | 66 | #[inline] 67 | pub fn read_u16(&mut self) -> ReaderResult { 68 | self.read(2, LittleEndian::read_u16) 69 | } 70 | 71 | #[inline] 72 | pub fn read_u32(&mut self) -> ReaderResult { 73 | self.read(4, LittleEndian::read_u32) 74 | } 75 | 76 | #[inline] 77 | pub fn read_u64(&mut self) -> ReaderResult { 78 | self.read(8, LittleEndian::read_u64) 79 | } 80 | 81 | #[inline] 82 | pub fn read_i8(&mut self) -> ReaderResult { 83 | self.read(1, |buf| buf[0] as i8) 84 | } 85 | 86 | #[inline] 87 | pub fn read_i16(&mut self) -> ReaderResult { 88 | self.read(2, LittleEndian::read_i16) 89 | } 90 | 91 | #[inline] 92 | pub fn read_i32(&mut self) -> ReaderResult { 93 | self.read(4, LittleEndian::read_i32) 94 | } 95 | 96 | #[inline] 97 | pub fn read_i64(&mut self) -> ReaderResult { 98 | self.read(8, LittleEndian::read_i64) 99 | } 100 | 101 | #[inline] 102 | pub fn read_f32(&mut self) -> ReaderResult { 103 | self.read(4, LittleEndian::read_f32) 104 | } 105 | 106 | #[inline] 107 | pub fn read_f64(&mut self) -> ReaderResult { 108 | self.read(8, LittleEndian::read_f64) 109 | } 110 | 111 | #[inline] 112 | pub fn read_var_u1(&mut self) -> ReaderResult { 113 | let value = self.read_var_u7()?; 114 | if value <= 1 { 115 | Ok(value) 116 | } else { 117 | return Err(Error::Leb128Overflow) 118 | } 119 | } 120 | 121 | #[inline] 122 | pub fn read_var_u7(&mut self) -> ReaderResult { 123 | let byte = self.read_u8()?; 124 | if byte & 0x80 == 0 { 125 | Ok(byte) 126 | } else { 127 | return Err(Error::Leb128Overflow) 128 | } 129 | } 130 | 131 | #[inline] 132 | pub fn read_var_u32(&mut self) -> ReaderResult { 133 | let mut byte = self.read_u8()?; 134 | if byte & 0x80 == 0 { 135 | Ok(byte as u32) 136 | } else { 137 | let mut value = (byte & 0x7f) as u32; 138 | let mut shift = 7; 139 | loop { 140 | byte = self.read_u8()?; 141 | value |= ((byte & 0x7f) as u32) << shift; 142 | if byte & 0x80 == 0 { break } 143 | shift += 7; 144 | if shift > 31 { return Err(Error::Leb128Overflow) } 145 | } 146 | Ok(value) 147 | } 148 | } 149 | 150 | pub fn read_var_u64(&mut self) -> ReaderResult { 151 | let mut byte = self.read_u8()?; 152 | if byte & 0x80 == 0 { 153 | Ok(byte as u64) 154 | } else { 155 | let mut value = (byte & 0x7f) as u64; 156 | let mut shift = 7; 157 | loop { 158 | byte = self.read_u8()?; 159 | value |= ((byte & 0x7f) as u64) << shift; 160 | if byte & 0x80 == 0 { break } 161 | shift += 7; 162 | if shift > 63 { return Err(Error::Leb128Overflow) } 163 | } 164 | Ok(value) 165 | } 166 | } 167 | 168 | #[inline] 169 | pub fn read_var_i7(&mut self) -> ReaderResult { 170 | let mut byte = self.read_u8()?; 171 | if byte & 0x80 == 0 { 172 | if byte & 0x40 != 0 { 173 | byte |= 0x80; 174 | } 175 | Ok(byte as i8) 176 | } else { 177 | return Err(Error::Leb128Overflow) 178 | } 179 | } 180 | 181 | #[inline] 182 | pub fn read_var_i32(&mut self) -> ReaderResult { 183 | let mut byte = self.read_u8()?; 184 | if byte & 0x80 == 0 { 185 | if byte & 0x40 != 0 { 186 | byte |= 0x80; 187 | } 188 | Ok(byte as i8 as i32) 189 | } else { 190 | let mut value = (byte & 0x7f) as u32; 191 | let mut shift = 7; 192 | loop { 193 | byte = self.read_u8()?; 194 | value |= ((byte & 0x7f) as u32) << shift; 195 | if byte & 0x80 == 0 { 196 | if byte & 0x40 != 0 { 197 | value |= 0xffff_ff80 << shift; 198 | } 199 | break 200 | } 201 | shift += 7; 202 | if shift > 31 { return Err(Error::Leb128Overflow) } 203 | } 204 | Ok(value as i32) 205 | } 206 | } 207 | 208 | #[inline] 209 | pub fn read_var_i64(&mut self) -> ReaderResult { 210 | let mut byte = self.read_u8()?; 211 | if byte & 0x80 == 0 { 212 | if byte & 0x40 != 0 { 213 | byte |= 0x80; 214 | } 215 | Ok(byte as i8 as i64) 216 | } else { 217 | let mut value = (byte & 0x7f) as u64; 218 | let mut shift = 7; 219 | loop { 220 | byte = self.read_u8()?; 221 | value |= ((byte & 0x7f) as u64) << shift; 222 | if byte & 0x80 == 0 { 223 | if byte & 0x40 != 0 { 224 | value |= 0xffff_ffff_ffff_ff80 << shift; 225 | } 226 | break 227 | } 228 | shift += 7; 229 | if shift > 63 { return Err(Error::Leb128Overflow) } 230 | } 231 | Ok(value as i64) 232 | } 233 | } 234 | 235 | #[inline] 236 | pub fn read_range(&mut self, len: usize) -> ReaderResult> { 237 | let v = self.pos..self.pos+len; 238 | self.pos += len; 239 | Ok(v) 240 | } 241 | #[inline] 242 | pub fn slice(&self, range: Range) -> &[u8] { 243 | &self.buf[range] 244 | } 245 | } 246 | 247 | impl<'a> Index for Reader<'a> { 248 | type Output = u8; 249 | 250 | fn index(&self, index: usize) -> &u8 { 251 | &self.buf[index] 252 | } 253 | } 254 | 255 | impl<'a> AsRef<[u8]> for Reader<'a> { 256 | fn as_ref(&self) -> &[u8] { 257 | self.buf 258 | } 259 | } 260 | 261 | #[cfg(test)] 262 | mod tests { 263 | use super::Reader; 264 | 265 | #[test] 266 | fn test_read_u7() { 267 | fn read_u7(buf: &[u8]) -> u8 { 268 | let mut r = Reader::new(buf); 269 | let v = r.read_var_u7().unwrap(); 270 | assert!(r.done()); 271 | v 272 | } 273 | for i in 0u8..128 { 274 | assert_eq!(read_u7(&[i]), i); 275 | } 276 | } 277 | 278 | #[test] 279 | fn test_read_i7() { 280 | fn read_i7(buf: &[u8]) -> i8 { 281 | let mut r = Reader::new(buf); 282 | let v = r.read_var_i7().unwrap(); 283 | assert!(r.done()); 284 | v 285 | } 286 | for i in 0u8..64 { 287 | assert_eq!(read_i7(&[i]), i as i8); 288 | } 289 | assert_eq!(read_i7(&[0b0111_1111]), -1); 290 | assert_eq!(read_i7(&[0b0111_1110]), -2); 291 | assert_eq!(read_i7(&[0b0111_1100]), -4); 292 | assert_eq!(read_i7(&[0b0111_1000]), -8); 293 | assert_eq!(read_i7(&[0b0111_0000]), -16); 294 | assert_eq!(read_i7(&[0b0110_0000]), -32); 295 | assert_eq!(read_i7(&[0b0100_0000]), -64); 296 | } 297 | 298 | #[test] 299 | fn test_read_u32() { 300 | fn read_u32(buf: &[u8]) -> u32 { 301 | let mut r = Reader::new(buf); 302 | let v = r.read_var_u32().unwrap(); 303 | assert!(r.done()); 304 | v 305 | } 306 | 307 | // 0-7 bits 308 | assert_eq!(read_u32(&[0b0000000]), 0b0000000); 309 | assert_eq!(read_u32(&[0b1111111]), 0b1111111); 310 | 311 | // 8-14 bits 312 | assert_eq!(read_u32(&[0b10000000, 0b0000001]), 0b1_0000000); 313 | assert_eq!(read_u32(&[0b10000001, 0b0000001]), 0b1_0000001); 314 | assert_eq!(read_u32(&[0b11111111, 0b0000001]), 0b1_1111111); 315 | 316 | // 15-21 bits 317 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000); 318 | assert_eq!(read_u32(&[0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001); 319 | assert_eq!(read_u32(&[0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000); 320 | assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111); 321 | 322 | // 22-28 bits 323 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000000); 324 | assert_eq!(read_u32(&[0b10000001, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000001); 325 | assert_eq!(read_u32(&[0b10000000, 0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001_0000000); 326 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000_0000000); 327 | assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111_1111111); 328 | 329 | // 29-32 bits 330 | assert_eq!(read_u32(&[0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b00001000]), 0b1000_0000000_0000000_0000000_0000000); 331 | assert_eq!(read_u32(&[0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b00001000]), 0b1000_0001000_0000100_0000010_0000001); 332 | assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00001111]), 0b1111_1111111_1111111_1111111_1111111); 333 | //assert_eq!(read_u32(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00010000]), 0b1111_1111111_1111111_1111111_1111111); 334 | } 335 | 336 | #[test] 337 | fn test_read_u64() { 338 | fn read_u64(buf: &[u8]) -> u64 { 339 | let mut r = Reader::new(buf); 340 | let v = r.read_var_u64().unwrap(); 341 | assert!(r.done()); 342 | v 343 | } 344 | 345 | // 0-7 bits 346 | assert_eq!(read_u64(&[0b0000000]), 0b0000000); 347 | assert_eq!(read_u64(&[0b1111111]), 0b1111111); 348 | 349 | // 8-14 bits 350 | assert_eq!(read_u64(&[0b10000000, 0b0000001]), 0b1_0000000); 351 | assert_eq!(read_u64(&[0b10000001, 0b0000001]), 0b1_0000001); 352 | assert_eq!(read_u64(&[0b11111111, 0b0000001]), 0b1_1111111); 353 | 354 | // 15-21 bits 355 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000); 356 | assert_eq!(read_u64(&[0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001); 357 | assert_eq!(read_u64(&[0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000); 358 | assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111); 359 | 360 | // 22-28 bits 361 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000000); 362 | assert_eq!(read_u64(&[0b10000001, 0b10000000, 0b10000000, 0b0000001]), 0b1_0000000_0000000_0000001); 363 | assert_eq!(read_u64(&[0b10000000, 0b10000001, 0b10000000, 0b0000001]), 0b1_0000000_0000001_0000000); 364 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b10000001, 0b0000001]), 0b1_0000001_0000000_0000000); 365 | assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b11111111, 0b0000001]), 0b1_1111111_1111111_1111111); 366 | 367 | // 29-32 bits 368 | assert_eq!(read_u64(&[0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b00001000]), 0b1000_0000000_0000000_0000000_0000000); 369 | assert_eq!(read_u64(&[0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b00001000]), 0b1000_0001000_0000100_0000010_0000001); 370 | assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00001111]), 0b1111_1111111_1111111_1111111_1111111); 371 | //assert_eq!(read_u64(&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b00010000]), 0b1111_1111111_1111111_1111111_1111111); 372 | } 373 | 374 | 375 | #[test] 376 | fn test_read_i32() { 377 | fn read_i32(buf: &[u8]) -> i32 { 378 | let mut r = Reader::new(buf); 379 | let v = r.read_var_i32().unwrap(); 380 | assert!(r.done()); 381 | v 382 | } 383 | 384 | // 0-7 bits 385 | assert_eq!(read_i32(&[0x0]), 0); 386 | assert_eq!(read_i32(&[0x1]), 1); 387 | assert_eq!(read_i32(&[0x2]), 2); 388 | assert_eq!(read_i32(&[0x7f]), -1); 389 | assert_eq!(read_i32(&[0x7e]), -2); 390 | 391 | assert_eq!(read_i32(&[0xff, 0]), 127); 392 | assert_eq!(read_i32(&[0x80, 1]), 128); 393 | assert_eq!(read_i32(&[0x81, 1]), 129); 394 | 395 | assert_eq!(read_i32(&[0x81, 0x7f]), -127); 396 | assert_eq!(read_i32(&[0x80, 0x7f]), -128); 397 | assert_eq!(read_i32(&[0xff, 0x7e]), -129); 398 | } 399 | 400 | #[test] 401 | fn test_read_i64() { 402 | fn read_i64(buf: &[u8]) -> i64 { 403 | let mut r = Reader::new(buf); 404 | let v = r.read_var_i64().unwrap(); 405 | assert!(r.done()); 406 | v 407 | } 408 | 409 | // 0-7 bits 410 | assert_eq!(read_i64(&[0x0]), 0); 411 | assert_eq!(read_i64(&[0x1]), 1); 412 | assert_eq!(read_i64(&[0x2]), 2); 413 | assert_eq!(read_i64(&[0x7f]), -1); 414 | assert_eq!(read_i64(&[0x7e]), -2); 415 | 416 | assert_eq!(read_i64(&[0xff, 0]), 127); 417 | assert_eq!(read_i64(&[0x80, 1]), 128); 418 | assert_eq!(read_i64(&[0x81, 1]), 129); 419 | 420 | assert_eq!(read_i64(&[0x81, 0x7f]), -127); 421 | assert_eq!(read_i64(&[0x80, 0x7f]), -128); 422 | assert_eq!(read_i64(&[0xff, 0x7e]), -129); 423 | } 424 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.6.4" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.10.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "atty" 18 | version = "0.2.6" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | dependencies = [ 21 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "1.0.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | 31 | [[package]] 32 | name = "bobbin-wasm" 33 | version = "0.1.0" 34 | dependencies = [ 35 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 38 | "fallible-iterator 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 40 | ] 41 | 42 | [[package]] 43 | name = "byteorder" 44 | version = "1.2.1" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | 47 | [[package]] 48 | name = "cfg-if" 49 | version = "0.1.2" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "chrono" 54 | version = "0.4.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | dependencies = [ 57 | "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 59 | ] 60 | 61 | [[package]] 62 | name = "clap" 63 | version = "2.30.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | dependencies = [ 66 | "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 67 | "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 73 | ] 74 | 75 | [[package]] 76 | name = "env_logger" 77 | version = "0.5.3" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | dependencies = [ 80 | "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "termcolor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "fallible-iterator" 89 | version = "0.1.4" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | 92 | [[package]] 93 | name = "lazy_static" 94 | version = "1.0.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | 97 | [[package]] 98 | name = "libc" 99 | version = "0.2.36" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | 102 | [[package]] 103 | name = "log" 104 | version = "0.4.1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | dependencies = [ 107 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 108 | ] 109 | 110 | [[package]] 111 | name = "memchr" 112 | version = "2.0.1" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | dependencies = [ 115 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "num" 120 | version = "0.1.42" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 125 | "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "num-integer" 130 | version = "0.1.36" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 134 | ] 135 | 136 | [[package]] 137 | name = "num-iter" 138 | version = "0.1.35" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | dependencies = [ 141 | "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 142 | "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 143 | ] 144 | 145 | [[package]] 146 | name = "num-traits" 147 | version = "0.2.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | 150 | [[package]] 151 | name = "redox_syscall" 152 | version = "0.1.37" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | 155 | [[package]] 156 | name = "redox_termios" 157 | version = "0.1.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 161 | ] 162 | 163 | [[package]] 164 | name = "regex" 165 | version = "0.2.6" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | dependencies = [ 168 | "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 170 | "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 173 | ] 174 | 175 | [[package]] 176 | name = "regex-syntax" 177 | version = "0.4.2" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | 180 | [[package]] 181 | name = "strsim" 182 | version = "0.7.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | 185 | [[package]] 186 | name = "termcolor" 187 | version = "0.3.4" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | dependencies = [ 190 | "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 191 | ] 192 | 193 | [[package]] 194 | name = "termion" 195 | version = "1.5.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | dependencies = [ 198 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 201 | ] 202 | 203 | [[package]] 204 | name = "textwrap" 205 | version = "0.9.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | dependencies = [ 208 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 209 | ] 210 | 211 | [[package]] 212 | name = "thread_local" 213 | version = "0.3.5" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | dependencies = [ 216 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 218 | ] 219 | 220 | [[package]] 221 | name = "time" 222 | version = "0.1.39" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | dependencies = [ 225 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 226 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 227 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 228 | ] 229 | 230 | [[package]] 231 | name = "unicode-width" 232 | version = "0.1.4" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | 235 | [[package]] 236 | name = "unreachable" 237 | version = "1.0.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | dependencies = [ 240 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 241 | ] 242 | 243 | [[package]] 244 | name = "utf8-ranges" 245 | version = "1.0.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | 248 | [[package]] 249 | name = "vec_map" 250 | version = "0.8.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | 253 | [[package]] 254 | name = "void" 255 | version = "1.0.2" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | 258 | [[package]] 259 | name = "winapi" 260 | version = "0.3.4" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | dependencies = [ 263 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 265 | ] 266 | 267 | [[package]] 268 | name = "winapi-i686-pc-windows-gnu" 269 | version = "0.4.0" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | 272 | [[package]] 273 | name = "winapi-x86_64-pc-windows-gnu" 274 | version = "0.4.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | 277 | [[package]] 278 | name = "wincolor" 279 | version = "0.1.6" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | dependencies = [ 282 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 283 | ] 284 | 285 | [metadata] 286 | "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" 287 | "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" 288 | "checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" 289 | "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" 290 | "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" 291 | "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" 292 | "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" 293 | "checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b" 294 | "checksum env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f15f0b172cb4f52ed5dbf47f774a387cd2315d1bf7894ab5af9b083ae27efa5a" 295 | "checksum fallible-iterator 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6034a9c9dfce417c7710128d202eef406878cd2fe294e76e2ee05259c9b042d" 296 | "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" 297 | "checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" 298 | "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" 299 | "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" 300 | "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" 301 | "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" 302 | "checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" 303 | "checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" 304 | "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" 305 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 306 | "checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b" 307 | "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" 308 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 309 | "checksum termcolor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "73e83896da740a4541a6f21606b35f2aa4bada5b65d89dc61114bf9d6ff2dc7e" 310 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 311 | "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" 312 | "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" 313 | "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" 314 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 315 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 316 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 317 | "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" 318 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 319 | "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" 320 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 321 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 322 | "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" 323 | -------------------------------------------------------------------------------- /src/module_inst.rs: -------------------------------------------------------------------------------- 1 | use error::Error; 2 | 3 | 4 | use types::*; 5 | use parser::{FallibleIterator}; 6 | use parser::module::*; 7 | use compiler::*; 8 | use core::cell::Cell; 9 | use environ::{Environment, HostHandler}; 10 | use memory_inst::MemoryInst; 11 | use small_vec::SmallVec; 12 | use writer::Writer; 13 | 14 | use parser::module::ExportDesc; 15 | use parser::types::{FunctionType, GlobalType}; 16 | 17 | use core::fmt; 18 | 19 | pub struct ModuleInst<'buf> { 20 | function_types: SmallVec<'buf, FunctionType<'buf>>, 21 | functions: SmallVec<'buf, FuncInst<'buf>>, 22 | globals: SmallVec<'buf, GlobalInst>, 23 | exports: SmallVec<'buf, ExportInst<'buf>>, 24 | tables: SmallVec<'buf, SmallVec<'buf, u32>>, 25 | code: CompiledCode<'buf>, 26 | } 27 | 28 | impl<'buf, 'env> ModuleInst<'buf> { 29 | pub fn new(buf: &'buf mut [u8], env: &Environment, mem: &MemoryInst, m: Module) -> Result<(&'buf mut [u8], ModuleInst<'buf>), Error> { 30 | let mut w = Writer::new(buf); 31 | 32 | let mut function_types = w.alloc_smallvec(16); 33 | let mut functions = w.alloc_smallvec(32); 34 | let mut globals = w.alloc_smallvec(16); 35 | let mut tables = w.alloc_smallvec(16); 36 | let mut exports = w.alloc_smallvec(32); 37 | 38 | info!("function_types: {:p}", &function_types); 39 | info!("functions: {:p}", &functions); 40 | info!("globals: {:p}", &globals); 41 | 42 | let mut sections = m.sections(); 43 | 44 | while let Some(section) = sections.next()? { 45 | match section.id() { 46 | Id::Type => { 47 | let mut types = section.types(); 48 | while let Some(t) = types.next()? { 49 | let functype = 0x60; 50 | let parameters: &[ValueType] = w.copy_slice(t.parameters)?; 51 | let results: &[ValueType] = w.copy_slice(t.results)?; 52 | function_types.push(FunctionType { functype, parameters, results }); 53 | } 54 | }, 55 | Id::Import => { 56 | let mut imports = section.imports(); 57 | let mut import_index = 0; 58 | while let Some(i) = imports.next()? { 59 | info!("Import: {:?}", i); 60 | match i.import_desc { 61 | ImportDesc::Func(type_index) => { 62 | let type_index = type_index as usize; 63 | let module_bytes = w.copy_slice(i.module.as_bytes())?; 64 | let module = ::core::str::from_utf8(module_bytes)?; 65 | let name_bytes = w.copy_slice(i.name.as_bytes())?; 66 | let name = ::core::str::from_utf8(name_bytes)?; 67 | let module_index = 0; 68 | if module_bytes == b"host" || module_bytes == b"env" { 69 | let host_index = env.import_host_function(module, name, &i.import_desc)?; 70 | info!("Host Function @ {}: {} {} {} {}", functions.len(), type_index, module, name, host_index); 71 | functions.push(FuncInst::Host { type_index, module, name, host_index }); 72 | } else { 73 | info!("Import Function @ {}: {} {} {} {}", functions.len(), type_index, module, name, import_index); 74 | functions.push(FuncInst::Import { type_index, module, name, module_index, import_index }); 75 | } 76 | }, 77 | ImportDesc::Table(_) => { 78 | // info!("Import Table"); 79 | }, 80 | ImportDesc::Memory(_) => { 81 | // info!("Import Memory"); 82 | }, 83 | ImportDesc::Global(global_type) => { 84 | globals.push(GlobalInst::Import { global_type, import_index}); 85 | } 86 | } 87 | import_index += 1; 88 | } 89 | }, 90 | Id::Function => { 91 | let mut funcs = section.functions(); 92 | let mut function_index = 0; 93 | while let Some(function) = funcs.next()? { 94 | let type_index = function as usize; 95 | info!("Local Func @ {}: {} {}", functions.len(), type_index, function_index); 96 | functions.push(FuncInst::Local { type_index, function_index }); 97 | function_index += 1; 98 | } 99 | }, 100 | Id::Memory => { 101 | let mut mems = section.memory(); 102 | while let Some(m) = mems.next()? { 103 | info!("MEMORY: {:?}", m.limits); 104 | info!("growing to {}", m.limits.min); 105 | mem.grow_memory(m.limits.min as i32); 106 | info!("num_pages: {}", mem.num_pages()); 107 | } 108 | 109 | } 110 | Id::Global => { 111 | let mut globs = section.globals(); 112 | let mut global_index = 0; 113 | while let Some(global) = globs.next()? { 114 | let global_type = global.global_type; 115 | let init = global.init; 116 | let value = if let Some(value) = init.i32_value() { 117 | Cell::new(Value::from(value)) 118 | } else { 119 | panic!("Invalid global initializer value"); 120 | }; 121 | globals.push(GlobalInst::Local { global_type, global_index, value }); 122 | global_index += 1; 123 | } 124 | }, 125 | Id::Table => { 126 | let mut tabs = section.tables(); 127 | while let Some(table) = tabs.next()? { 128 | let TableType { elemtype, limits } = table; 129 | info!("Adding table: {} {:?}", elemtype, limits); 130 | let size = if let Some(max) = limits.max { 131 | max 132 | } else { 133 | limits.min 134 | }; 135 | let mut t: SmallVec = w.alloc_smallvec(size as usize); 136 | for _ in 0..size { 137 | t.push(0); 138 | } 139 | tables.push(t); 140 | } 141 | }, 142 | Id::Export => { 143 | let mut expts = section.exports(); 144 | while let Some(export) = expts.next()? { 145 | let Export { name, export_desc } = export; 146 | let bytes = w.copy_slice(name.as_bytes())?; 147 | let name = ::core::str::from_utf8(bytes)?; 148 | exports.push(ExportInst { name, export_desc }); 149 | } 150 | } 151 | Id::Element => { 152 | let mut elements = section.elements(); 153 | while let Some(element) = elements.next()? { 154 | let Element { table_index, offset, init } = element; 155 | // use byteorder::{ByteOrder, LittleEndian}; 156 | 157 | info!("Initializing table {}", table_index); 158 | let table = &mut tables[table_index as usize]; 159 | let offset = offset.i32_value().unwrap(); 160 | let mut i = 0; 161 | let mut o = offset as usize; 162 | while i < init.len() { 163 | // let d = LittleEndian::read_u32(&data[i..]); 164 | let d = init[i] as u32; 165 | info!("{:08x}: {:08x}", o, d); 166 | table[o] = d; 167 | o += 1; 168 | i += 1; 169 | } 170 | } 171 | }, 172 | Id::Data => { 173 | let mut data = section.data(); 174 | while let Some(data) = data.next()? { 175 | let Data { mem_index: _, offset, init } = data; 176 | let offset = offset.i32_value().unwrap(); 177 | for i in 0..init.len() { 178 | let d = init[i]; 179 | let o = offset as usize + i; 180 | // info!("{:08x}: {:02x}", o, d); 181 | mem.set(o, d); 182 | } 183 | } 184 | }, 185 | _ => {}, 186 | } 187 | } 188 | 189 | let buf = w.into_slice(); 190 | 191 | // Change compiler to use ModuleInst 192 | 193 | let (buf, code) = Compiler::new(&mut [0u8; 4096]).compile(buf, 194 | function_types.as_ref(), 195 | functions.as_ref(), 196 | globals.as_ref(), 197 | &m)?; 198 | 199 | Ok((buf, ModuleInst { function_types, functions, globals, exports, tables, code })) 200 | } 201 | 202 | pub fn function_types(&self) -> &[FunctionType] { 203 | self.function_types.as_ref() 204 | } 205 | 206 | pub fn functions(&self) -> &[FuncInst] { 207 | self.functions.as_ref() 208 | } 209 | 210 | pub fn globals(&self) -> &[GlobalInst] { 211 | self.globals.as_ref() 212 | } 213 | 214 | pub fn table(&self, index: usize) -> &SmallVec { 215 | &self.tables[index] 216 | } 217 | 218 | pub fn exports(&self) -> &[ExportInst] { 219 | self.exports.as_ref() 220 | } 221 | 222 | pub fn indirect_functions_len(&self) -> usize { 223 | self.tables[0].len() 224 | } 225 | 226 | pub fn indirect_function_id(&self, index: usize) -> u32 { 227 | self.tables[0][index] 228 | } 229 | 230 | pub fn type_signature(&self, index: usize) -> &FunctionType { 231 | &self.function_types[index] 232 | } 233 | 234 | pub fn global_type(&self, index: u32) -> Result { 235 | Ok({ 236 | info!("global_type({})", index); 237 | if index as usize > self.globals.len() { 238 | return Err(Error::OutOfBounds); 239 | } 240 | match self.globals[index as usize] { 241 | GlobalInst::Local { global_type, global_index: _, value: _} => { 242 | global_type 243 | }, 244 | GlobalInst::Import { global_type, import_index: _ } => { 245 | global_type 246 | } 247 | } 248 | }) 249 | } 250 | 251 | pub fn get_global(&self, index: u32) -> Result { 252 | Ok({ 253 | info!("get_global({})", index); 254 | if index as usize > self.globals.len() { 255 | return Err(Error::OutOfBounds); 256 | } 257 | match self.globals[index as usize] { 258 | GlobalInst::Local { global_type: _, global_index: _, ref value } => { 259 | let v = value.get().0; 260 | info!(" => {}", v); 261 | v 262 | }, 263 | GlobalInst::Import { global_type: _, import_index: _ } => { 264 | unimplemented!() 265 | } 266 | } 267 | }) 268 | } 269 | pub fn set_global(&self, index: u32, new_value: i32) -> Result<(), Error> { 270 | Ok({ 271 | info!("set_global({}, {})", index, new_value); 272 | if index as usize > self.globals.len() { 273 | return Err(Error::OutOfBounds); 274 | } 275 | match self.globals[index as usize] { 276 | GlobalInst::Local { global_type: _, global_index: _, ref value } => { 277 | info!("set_value @ {:p} = {}", value, new_value); 278 | value.set(Value(new_value)) 279 | }, 280 | GlobalInst::Import { global_type: _, import_index: _ } => { 281 | unimplemented!() 282 | } 283 | } 284 | }) 285 | } 286 | pub fn code(&self) -> &CompiledCode { 287 | &self.code 288 | } 289 | 290 | // pub fn body(&self, index: usize) -> Option { 291 | // self.m.body(index as u32) 292 | // } 293 | } 294 | 295 | #[derive(Debug)] 296 | pub enum FuncInst<'a> { 297 | Host { type_index: usize, module: &'a str, name: &'a str, host_index: usize }, 298 | Import { type_index: usize, module: &'a str, name: &'a str, module_index: usize, import_index: usize }, 299 | Local { type_index: usize, function_index: usize }, 300 | } 301 | 302 | impl<'a> FuncInst<'a> { 303 | pub fn type_index(&self) -> usize { 304 | match self { 305 | &FuncInst::Host { type_index, module: _, name: _, host_index: _ } => type_index, 306 | &FuncInst::Import { type_index, module: _, name: _, module_index: _, import_index: _ } => type_index, 307 | &FuncInst::Local { type_index, function_index: _ } => type_index, 308 | } 309 | } 310 | } 311 | 312 | #[derive(Debug)] 313 | pub enum GlobalInst { 314 | Import { global_type: GlobalType, import_index: usize }, 315 | Local { global_type: GlobalType, global_index: usize, value: Cell }, 316 | } 317 | 318 | impl GlobalInst { 319 | pub fn global_type(&self) -> GlobalType { 320 | match self { 321 | &GlobalInst::Import { global_type, import_index: _ } => global_type, 322 | &GlobalInst::Local { global_type, global_index: _, value: _ } => global_type, 323 | } 324 | } 325 | } 326 | 327 | #[derive(Debug)] 328 | pub struct ExportInst<'buf> { 329 | pub name: &'buf str, 330 | pub export_desc: ExportDesc, 331 | } 332 | 333 | #[derive(Default, Clone, Copy, PartialEq, Eq)] 334 | pub struct Value(pub i32); 335 | 336 | impl fmt::Debug for Value { 337 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 338 | write!(f, "i32:{}", self.0 as u32) 339 | } 340 | } 341 | 342 | impl fmt::Display for Value { 343 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 344 | self.0.fmt(f) 345 | } 346 | } 347 | 348 | 349 | 350 | impl From for Value { 351 | fn from(other: i32) -> Value { 352 | Value(other) 353 | } 354 | } 355 | 356 | impl From for Value { 357 | fn from(other: u32) -> Value { 358 | Value(other as i32) 359 | } 360 | } 361 | 362 | impl From for i32 { 363 | fn from(other: Value) -> i32 { 364 | other.0 365 | } 366 | } 367 | 368 | impl From for u32 { 369 | fn from(other: Value) -> u32 { 370 | other.0 as u32 371 | } 372 | } 373 | 374 | 375 | #[cfg(test)] 376 | mod tests { 377 | use super::*; 378 | 379 | // #[test] 380 | // fn test_module_inst() { 381 | // let mut buf = [0u8; 2048]; 382 | 383 | // let mut m = Module::new(); 384 | // m.set_name("hello.wasm"); 385 | 386 | // let (mi, _buf) = ModuleInst::new(&m, &mut buf).unwrap(); 387 | // assert_eq!(mi.name(), "hello.wasm"); 388 | // } 389 | 390 | #[test] 391 | fn test_copy_types() { 392 | use opcode::I32; 393 | 394 | let mut buf = [0u8; 1024]; 395 | let mut w = Writer::new(&mut buf); 396 | 397 | let t_new = { 398 | let parameters = &[I32 as u8, I32 as u8][..]; 399 | let returns = &[I32 as u8][..]; 400 | let t = Type { parameters, returns }; 401 | Type { 402 | parameters: w.copy_slice(t.parameters).unwrap(), 403 | returns: w.copy_slice(t.returns).unwrap(), 404 | } 405 | }; 406 | assert_eq!(t_new.parameters.len(), 2); 407 | assert_eq!(t_new.returns.len(), 1); 408 | } 409 | 410 | 411 | #[test] 412 | fn test_build_type_list() { 413 | use opcode::{I32, I64}; 414 | use {Error, ValueType}; 415 | 416 | trait WriteTo { 417 | fn write_to(&self, w: &mut W) -> Result<(), E>; 418 | } 419 | 420 | impl<'buf> WriteTo, Error> for ValueType { 421 | fn write_to(&self, w: &mut Writer<'buf>) -> Result<(), Error> { 422 | w.write_i8(*self as i8) 423 | } 424 | } 425 | 426 | impl<'buf, W, T, E> WriteTo for &'buf [T] where T: WriteTo { 427 | fn write_to(&self, w: &mut W) -> Result<(), E> { 428 | for item in self.iter() { 429 | item.write_to(w)?; 430 | } 431 | Ok(()) 432 | } 433 | } 434 | 435 | let src = &[I32, I64][..]; 436 | 437 | let mut buf = [0u8; 64]; 438 | let mut w = Writer::new(&mut buf); 439 | 440 | src.write_to(&mut w).unwrap(); 441 | 442 | // for t in src { 443 | // (*t).write_to(&mut w).unwrap(); 444 | // } 445 | let _dst: &[i8] = w.split(); 446 | 447 | } 448 | } -------------------------------------------------------------------------------- /src/bin/wasm-objdump.rs: -------------------------------------------------------------------------------- 1 | extern crate bobbin_wasm as wasm; 2 | extern crate clap; 3 | // #[macro_use] extern crate log; 4 | extern crate env_logger; 5 | 6 | use std::process; 7 | use std::io::{self, Read}; 8 | use std::fs::File; 9 | use std::path::Path; 10 | 11 | use clap::{App, Arg, ArgMatches}; 12 | 13 | // use wasm::{Reader, BinaryReader}; 14 | use wasm::types::Index; 15 | use wasm::parser::{self, Id, Module, FallibleIterator, ExportDesc, ImportDesc, Immediate, FuncItem, Instr, Local}; 16 | // use wasm::visitor; 17 | 18 | 19 | use std::fmt::{self, Write}; 20 | 21 | #[derive(Debug)] 22 | pub enum Error { 23 | IoError(io::Error), 24 | FmtError(fmt::Error), 25 | ParserError(parser::Error), 26 | WasmError(wasm::Error), 27 | } 28 | 29 | impl From for Error { 30 | fn from(other: io::Error) -> Self { 31 | Error::IoError(other) 32 | } 33 | } 34 | 35 | impl From for Error { 36 | fn from(other: fmt::Error) -> Self { 37 | Error::FmtError(other) 38 | } 39 | } 40 | 41 | impl From for Error { 42 | fn from(other: parser::Error) -> Self { 43 | Error::ParserError(other) 44 | } 45 | } 46 | 47 | impl From for Error { 48 | fn from(other: wasm::Error) -> Self { 49 | Error::WasmError(other) 50 | } 51 | } 52 | 53 | pub fn main() { 54 | env_logger::init(); 55 | let matches = App::new("dump") 56 | .arg(Arg::with_name("path") 57 | .required(true)) 58 | .arg(Arg::with_name("headers") 59 | .long("headers") 60 | .short("h")) 61 | .arg(Arg::with_name("details") 62 | .long("details") 63 | .short("x")) 64 | .arg(Arg::with_name("disassemble") 65 | .long("disassemble") 66 | .short("d")) 67 | .arg(Arg::with_name("relocations") 68 | .short("r")) 69 | .get_matches(); 70 | 71 | if let Err(e) = run(matches) { 72 | eprintln!("Error: {:?}", e); 73 | process::exit(1); 74 | } 75 | } 76 | 77 | pub fn run(matches: ArgMatches) -> Result<(), Error> { 78 | let path = Path::new(matches.value_of("path").unwrap()); 79 | let mut file = File::open(&path)?; 80 | let mut data: Vec = Vec::new(); 81 | file.read_to_end(&mut data)?; 82 | 83 | // let path = path.file_name().unwrap().to_str().unwrap(); 84 | let name = path.file_name().unwrap().to_str().unwrap(); 85 | let mut out = String::new(); 86 | 87 | 88 | let m = parser::Module::new(data.as_ref())?; 89 | 90 | writeln!(out, "\n{}:\tfile format wasm 0x{:x}\n", name, m.version())?; 91 | 92 | if matches.is_present("headers") { 93 | dump_headers(&mut out, &m)?; 94 | // let mut d = wasm::dumper::HeaderDumper{ w: &mut out }; 95 | // visitor::visit(&m, &mut d)?; 96 | 97 | } 98 | 99 | if matches.is_present("details") { 100 | dump_details(&mut out, &m)?; 101 | // let mut d = wasm::dumper::DetailsDumper{ w: &mut out }; 102 | // visitor::visit(&m, &mut d)?; 103 | } 104 | 105 | if matches.is_present("disassemble") { 106 | dump_code(&mut out, &m)?; 107 | // let mut d = wasm::dumper::Disassembler::new(&mut out ); 108 | // visitor::visit(&m, &mut d)?; 109 | } 110 | print!("{}", out); 111 | 112 | Ok(()) 113 | } 114 | 115 | pub fn dump_headers(out: &mut W, m: &Module) -> Result<(), Error> { 116 | writeln!(out, "Sections:")?; 117 | let mut sections = m.sections(); 118 | while let Some(s) = sections.next()? { 119 | let s_id = s.id(); 120 | let s_count = s.count()?; 121 | let s_beg = m.offset_to(s.buf); 122 | let s_len = s.buf.len(); 123 | let s_end = s_beg + s_len; 124 | match s_id { 125 | Id::Custom => { 126 | // let mut c = Cursor::new(data); 127 | // let s_name = c.read_identifier(); 128 | let s_name = ""; 129 | writeln!(out, "{:>9} start={:#010x} end={:#010x} (size={:#010x}) {:?}", s_id.as_str(), s_beg, s_end, s_len, s_name)?; 130 | }, 131 | Id::Start => { 132 | writeln!(out, "{:>9} start={:#010x} end={:#010x} (size={:#010x}) start: {}", s_id.as_str(), s_beg, s_end, s_len, s_count)?; 133 | }, 134 | _ => { 135 | writeln!(out, "{:>9} start={:#010x} end={:#010x} (size={:#010x}) count: {}", s_id.as_str(), s_beg, s_end, s_len, s_count)?; 136 | } 137 | } 138 | } 139 | Ok(()) 140 | } 141 | 142 | pub fn dump_details(out: &mut W, m: &Module) -> Result<(), Error> { 143 | writeln!(out, "Section Details:")?; 144 | let mut sections = m.sections(); 145 | let mut import_funcs = 0; 146 | let mut import_tables = 0; 147 | let mut import_memory = 0; 148 | let mut import_globals = 0; 149 | while let Some(s) = sections.next()? { 150 | let s_id = s.id(); 151 | if s_id != Id::Code && s_id != Id::Custom { 152 | writeln!(out, "{}:", s_id.as_str())?; 153 | } 154 | match s_id { 155 | Id::Custom => {}, 156 | Id::Type => { 157 | let mut types = s.types(); 158 | let mut n = 0; 159 | while let Some(t) = types.next()? { 160 | write!(out, " - type[{}] (", n)?; 161 | for (n, p) in t.parameters.iter().enumerate() { 162 | if n > 0 { write!(out, ", ")? } 163 | write!(out, "{}", p)?; 164 | } 165 | write!(out, ")")?; 166 | write!(out, " ->")?; 167 | if t.results.len() == 0 { 168 | write!(out, " nil")? 169 | } else { 170 | write!(out, " {}", t.results[0])?; 171 | } 172 | writeln!(out, "")?; 173 | n += 1; 174 | } 175 | }, 176 | Id::Import => { 177 | let mut imports = s.imports(); 178 | let mut n = 0; 179 | while let Some(i) = imports.next()? { 180 | let module = i.module; 181 | let name = i.name; 182 | let desc = i.import_desc; 183 | match desc { 184 | ImportDesc::Func(f) => { 185 | writeln!(out, " - func[{}] sig={} <{}> <- {}.{}", n, f, name, module, name)?; 186 | import_funcs += 1; 187 | }, 188 | ImportDesc::Table(t) => { 189 | writeln!(out, " - table[{}] elem_type=anyfunc init={} max={:?} <- {}.{}", n, t.limits.min, t.limits.max, module, name)?; 190 | import_tables += 1; 191 | }, 192 | ImportDesc::Memory(m) => { 193 | writeln!(out, " - memory[{}] pages: initial={} max={:?} <- {}.{}", n, m.limits.min, m.limits.max, module, name)?; 194 | import_memory += 1; 195 | }, 196 | ImportDesc::Global(g) => { 197 | writeln!(out, " - global[{}] {} mutable={} <- {}.{}", n, g.valtype, if g.mutable { 1 } else { 0 }, module, name)?; 198 | import_globals += 1; 199 | }, 200 | } 201 | n += 1; 202 | } 203 | }, 204 | Id::Function => { 205 | let mut funcs = s.functions(); 206 | let mut n = import_funcs; 207 | while let Some(f) = funcs.next()? { 208 | writeln!(out," - func[{}] sig={}", n, f)?; 209 | n += 1; 210 | } 211 | }, 212 | Id::Table => { 213 | let mut tables = s.tables(); 214 | let mut n = import_tables; 215 | while let Some(t) = tables.next()? { 216 | let element_type = t.elemtype; 217 | let limits = t.limits; 218 | let limits_max = if let Some(limits_max) = limits.max { 219 | limits_max 220 | } else { 221 | limits.min 222 | }; 223 | writeln!(out, " - table[{}] type={} initial={} max={}", n, element_type, limits.min, limits_max)?; 224 | n += 1; 225 | } 226 | }, 227 | Id::Memory => { 228 | let mut memory = s.memory(); 229 | let mut n = import_memory; 230 | while let Some(m) = memory.next()? { 231 | let limits = m.limits; 232 | write!(out, " - memory[{}] pages: initial={}", n, limits.min)?; 233 | if let Some(maximum) = limits.max { 234 | write!(out, " maximum={}", maximum)?; 235 | } 236 | writeln!(out, "")?; 237 | 238 | n += 1; 239 | } 240 | }, 241 | Id::Global => { 242 | let mut globals = s.globals(); 243 | let mut n = import_globals; 244 | while let Some(g) = globals.next()? { 245 | let t = g.global_type; 246 | let init = g.init; 247 | let instr = init.instr; 248 | let opcode = match instr.opcode { 249 | 0x41 => "i32", 250 | 0x42 => "i64", 251 | 0x43 => "f32", 252 | 0x44 => "f64", 253 | 0x23 => "global", 254 | _ => unimplemented!() 255 | }; 256 | writeln!(out, " - global[{}] {} mutable={} - init {}={:?}", n, t.valtype, if t.mutable { 1 } else { 0 }, opcode, instr.immediate)?; 257 | n += 1; 258 | } 259 | }, 260 | Id::Export => { 261 | let mut exports = s.exports(); 262 | let n = 0; 263 | while let Some(e) = exports.next()? { 264 | let kind = match e.export_desc { 265 | ExportDesc::Func(_) => "func", 266 | ExportDesc::Table(_) => "table", 267 | ExportDesc::Memory(_) => "memory", 268 | ExportDesc::Global(_) => "global", 269 | }; 270 | writeln!(out, " - {}[{}] -> {:?}", kind, n, e.name)?; 271 | } 272 | }, 273 | Id::Start => { 274 | let start = s.start()?; 275 | writeln!(out, " - start function: {}", start.func_index)?; 276 | }, 277 | Id::Element => { 278 | let mut elements = s.elements(); 279 | let mut n = 0; 280 | while let Some(e) = elements.next()? { 281 | let imm = if let Immediate::I32Const { value } = e.offset.instr.immediate { 282 | value 283 | } else { 284 | // FIXME 285 | panic!("invalid immediate type"); 286 | }; 287 | 288 | writeln!(out, " - init {}={}", "i32", imm)?; 289 | writeln!(out, " - segment[{}] table={} count={}", n, e.table_index, e.init.len())?; 290 | for i in 0..e.init.len() { 291 | writeln!(out, " - elem[{}] = func[{}]", i, e.init[i])?; 292 | } 293 | n += 1; 294 | } 295 | }, 296 | Id::Code => {}, 297 | Id::Data => { 298 | let mut data = s.data(); 299 | let mut n = 0; 300 | while let Some(d) = data.next()? { 301 | let offset = d.offset; 302 | let imm = if let Immediate::I32Const { value } = offset.instr.immediate { 303 | value 304 | } else { 305 | // FIXME 306 | panic!("invalid immediate type"); 307 | }; 308 | let init = d.init; 309 | writeln!(out, " - segment[{}] size={} - init {}={} ", n, init.len(), "i32", imm)?; 310 | write!(out, " - {:07x}:", imm)?; 311 | for (i, d) in init.iter().enumerate() { 312 | if i % 2 == 0 { 313 | write!(out, " ")?; 314 | } 315 | write!(out, "{:02x}", d)?; 316 | } 317 | writeln!(out, "")?; 318 | n += 1; 319 | } 320 | }, 321 | } 322 | 323 | } 324 | Ok(()) 325 | } 326 | 327 | pub fn dump_code(out: &mut W, m: &Module) -> Result<(), Error> { 328 | use parser::opcode::*; 329 | use std::collections::HashMap; 330 | 331 | 332 | let mut func_names: HashMap = HashMap::new(); 333 | 334 | writeln!(out, "Code Disassembly:")?; 335 | let mut sections = m.sections(); 336 | let mut import_funcs = 0; 337 | 338 | while let Some(s) = sections.next()? { 339 | match s.id() { 340 | Id::Import => { 341 | let mut imports = s.imports(); 342 | while let Some(i) = imports.next()? { 343 | match i.import_desc { 344 | ImportDesc::Func(_) => { 345 | func_names.insert(import_funcs, String::from(i.name)); 346 | import_funcs += 1; 347 | }, 348 | _ => {}, 349 | } 350 | } 351 | }, 352 | Id::Export => { 353 | let mut exports = s.exports(); 354 | while let Some(e) = exports.next()? { 355 | match e.export_desc { 356 | ExportDesc::Func(index) => { 357 | func_names.insert(index, String::from(e.name)); 358 | // writeln!(out, "{} => {}", e.name, import_funcs + index)?; 359 | }, 360 | _ => {}, 361 | } 362 | } 363 | } 364 | _ => {}, 365 | } 366 | } 367 | 368 | let mut sections = m.sections(); 369 | while let Some(s) = sections.next()? { 370 | let s_id = s.id(); 371 | if s_id != Id::Code { continue } 372 | 373 | let mut code_iter = s.code(); 374 | let mut n = import_funcs; 375 | while let Some(code) = code_iter.next()? { 376 | let offset = m.offset_to(code.buf); 377 | if let Some(ref name) = func_names.get(&n) { 378 | writeln!(out, "{:06x} <{}>:", offset, name)?; 379 | } else { 380 | writeln!(out, "{:06x} func[{}]:", offset, n)?; 381 | } 382 | 383 | let mut funcs = code.func.iter(); 384 | let mut depth = 0; 385 | let mut local_count = 0usize; 386 | let mut local_index = 0usize; 387 | while let Some(func_item) = funcs.next()? { 388 | match func_item { 389 | FuncItem::Local(Local { n, t }) => { 390 | let n = n as usize; 391 | let offset = offset + 2 + local_count * 2; 392 | write!(out, " {:06x}:", offset)?; 393 | let mut w = 0; 394 | write!(out, " {:02x} {:02x}", n, t as u8)?; 395 | w += 6; 396 | while w < 28 { 397 | write!(out, " ")?; 398 | w += 1; 399 | } 400 | write!(out, "| ")?; 401 | if n == 1 { 402 | writeln!(out, "local[{}] type={}", local_index, t)?; 403 | } else { 404 | writeln!(out, "local[{}..{}] type={}", 405 | local_index, 406 | local_index + n - 1, 407 | t 408 | )?; 409 | } 410 | local_count += 1; 411 | local_index += n; 412 | }, 413 | FuncItem::Instr(Instr { opcode, immediate: imm, data}) => { 414 | let offset = m.offset_to(data); 415 | 416 | let op = if let Some(op) = Op::from_opcode(opcode) { 417 | op 418 | } else { 419 | panic!("Unrecognized opcode: {}", opcode); 420 | }; 421 | match op.code { 422 | ELSE | END => { 423 | if depth > 0 { 424 | depth -= 1; 425 | } 426 | }, 427 | _ => {}, 428 | } 429 | write!(out, " {:06x}:", offset)?; 430 | let mut w = 0; 431 | if op.code == I64_CONST { 432 | for b in data.iter().take(10) { 433 | write!(out, " {:02x}", b)?; 434 | w += 3; 435 | } 436 | if w > 28 { 437 | write!(out, " ")?; 438 | } 439 | } else { 440 | for b in data.iter() { 441 | write!(out, " {:02x}", b)?; 442 | w += 3; 443 | } 444 | } 445 | while w < 28 { 446 | write!(out, " ")?; 447 | w += 1; 448 | } 449 | write!(out, "| ")?; 450 | for _ in 0..depth { write!(out, " ")?; } 451 | match imm { 452 | Immediate::None | Immediate::BranchTable { table: _ } => writeln!(out, "{}", op.text)?, 453 | Immediate::Block { signature } => if signature != ::parser::ValueType::Void { 454 | writeln!(out, "{} {}", op.text, signature)? 455 | } else { 456 | writeln!(out, "{}", op.text)? 457 | }, 458 | Immediate::Call { index } => if let Some(ref name) = func_names.get(&index) { 459 | writeln!(out, "{} {} <{}>", op.text, index, name)?; 460 | } else { 461 | writeln!(out, "{} {}", op.text, index)?; 462 | } 463 | _ => writeln!(out, "{} {:?}", op.text, imm)?, 464 | } 465 | 466 | match op.code { 467 | BLOCK | LOOP | IF | ELSE => { 468 | depth += 1; 469 | }, 470 | _ => {}, 471 | } 472 | } 473 | } 474 | } 475 | 476 | 477 | 478 | n += 1; 479 | } 480 | } 481 | Ok(()) 482 | } --------------------------------------------------------------------------------