mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-25 22:51:32 +00:00
Improved tests
This commit is contained in:
@ -56,8 +56,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String>{
|
||||
// if !webassembly::validate(&wasm_binary) {
|
||||
// return Err("Invalid WASM module".to_string())
|
||||
// };
|
||||
webassembly::compile(wasm_binary);
|
||||
// println!("Data, {:?}", wasm_binary);
|
||||
webassembly::instantiate(wasm_binary, None).map_err(|err| String::from(err.description()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Shamelessly copied from greenwasm-spectest:
|
||||
// Code adapted from greenwasm-spectest
|
||||
// https://github.com/Kimundi/greenwasm/blob/master/greenwasm-spectest/src/lib.rs
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
@ -1,10 +1,14 @@
|
||||
use std::path::Path;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use wabt::script::{Value, Action};
|
||||
use super::{InvokationResult, ScriptHandler, run_single_file};
|
||||
use crate::webassembly::{compile, Error, ErrorKind};
|
||||
use crate::webassembly::{compile, instantiate, Error, ErrorKind, Module, Instance};
|
||||
|
||||
struct StoreCtrl {
|
||||
last_module: Option<Rc<Instance>>,
|
||||
modules: HashMap<String, Rc<Instance>>
|
||||
}
|
||||
|
||||
impl StoreCtrl {
|
||||
@ -19,30 +23,35 @@ impl StoreCtrl {
|
||||
StoreCtrl {
|
||||
// tx,
|
||||
// _handle,
|
||||
// modules: HashMap::new(),
|
||||
// last_module: None,
|
||||
modules: HashMap::new(),
|
||||
last_module: None,
|
||||
}
|
||||
}
|
||||
|
||||
// fn add_module(&mut self, name: Option<String>, module: DummyEnvironment) {
|
||||
// // if let Some(name) = name {
|
||||
// // println!("ADD MODULE {:?}", name);
|
||||
// // self.modules
|
||||
// // .insert(name.unwrap_or("__last_module".to_string()), module);
|
||||
// // }
|
||||
// // self.modules.insert("__last_module".to_string(), module);
|
||||
// // self.last_module = Some(module);
|
||||
fn add_module(&mut self, name: Option<String>, module: Rc<Instance>) {
|
||||
if let Some(name) = name {
|
||||
// self.modules[&name] = module;
|
||||
self.modules.insert(name, Rc::clone(&module));
|
||||
}
|
||||
self.last_module = Some(Rc::clone(&module));
|
||||
// println!("ADD MODULE {:?}", name);
|
||||
// self.modules
|
||||
// .insert(name.unwrap_or("__last_module".to_string()), module);
|
||||
// }
|
||||
// self.modules.insert("__last_module".to_string(), module);
|
||||
// self.last_module = Some(module);
|
||||
}
|
||||
|
||||
// fn get_module(&self, name: String) -> Option<&DummyEnvironment> {
|
||||
// // return self
|
||||
// // .modules
|
||||
// // .get(&name)
|
||||
// // .or(self.modules.get("__last_module"));
|
||||
fn get_module(self, name: Option<String>) -> Rc<Instance> {
|
||||
self.last_module.unwrap()
|
||||
// return self
|
||||
// .modules
|
||||
// .get(&name)
|
||||
// .or(self.modules.get("__last_module"));
|
||||
// return None;
|
||||
// // return self.modules[&name];
|
||||
// // name.map(|name| self.modules[&name]).or(self.last_module).unwrap()
|
||||
// }
|
||||
// return self.modules[&name];
|
||||
// name.map(|name| self.modules[&name]).or(self.last_module).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptHandler for StoreCtrl {
|
||||
@ -53,14 +62,33 @@ impl ScriptHandler for StoreCtrl {
|
||||
field: String,
|
||||
args: Vec<Value>,
|
||||
) -> InvokationResult {
|
||||
if let Some(module) = &self.last_module {
|
||||
// let function = module.exports.get(field).expect("field not found");
|
||||
println!("HEEY {:?}", module);
|
||||
}
|
||||
// match module {
|
||||
// Some(m) => {
|
||||
// println!("HEEY {:?}", m);
|
||||
// },
|
||||
// _ => unimplemented!()
|
||||
// }
|
||||
// println!("action invoke {}", module.unwrap_or("as".to_string()));
|
||||
// let modul = &self.last_module;
|
||||
// modul.expect("a");
|
||||
//
|
||||
unimplemented!()
|
||||
}
|
||||
fn action_get(&mut self, module: Option<String>, field: String) -> Value {
|
||||
// println!("action get");
|
||||
unimplemented!()
|
||||
}
|
||||
fn module(&mut self, bytes: Vec<u8>, name: Option<String>) {
|
||||
let module_wrapped = compile(bytes);
|
||||
module_wrapped.expect("Module is invalid");
|
||||
let module_wrapped = instantiate(bytes, None);
|
||||
let result = module_wrapped.expect("Module is invalid");
|
||||
// let module: &'module Module = result.module;
|
||||
// self.last_module = Some(result.module);
|
||||
self.add_module(name, Rc::new(result.instance));
|
||||
// println!("ADD MODULE {}", name.unwrap_or("no name".to_string()))
|
||||
}
|
||||
fn assert_malformed(&mut self, bytes: Vec<u8>) {
|
||||
let module_wrapped = compile(bytes);
|
||||
@ -111,4 +139,5 @@ macro_rules! wasm_tests {
|
||||
|
||||
wasm_tests!{
|
||||
_type,
|
||||
br_if,
|
||||
}
|
||||
|
542
src/spec/tests/br_if.wast
Normal file
542
src/spec/tests/br_if.wast
Normal file
@ -0,0 +1,542 @@
|
||||
;; Test `br_if` operator
|
||||
|
||||
(module
|
||||
(func $dummy)
|
||||
|
||||
(func (export "type-i32")
|
||||
(block (drop (i32.ctz (br_if 0 (i32.const 0) (i32.const 1)))))
|
||||
)
|
||||
(func (export "type-i64")
|
||||
(block (drop (i64.ctz (br_if 0 (i64.const 0) (i32.const 1)))))
|
||||
)
|
||||
(func (export "type-f32")
|
||||
(block (drop (f32.neg (br_if 0 (f32.const 0) (i32.const 1)))))
|
||||
)
|
||||
(func (export "type-f64")
|
||||
(block (drop (f64.neg (br_if 0 (f64.const 0) (i32.const 1)))))
|
||||
)
|
||||
|
||||
(func (export "type-i32-value") (result i32)
|
||||
(block (result i32) (i32.ctz (br_if 0 (i32.const 1) (i32.const 1))))
|
||||
)
|
||||
(func (export "type-i64-value") (result i64)
|
||||
(block (result i64) (i64.ctz (br_if 0 (i64.const 2) (i32.const 1))))
|
||||
)
|
||||
(func (export "type-f32-value") (result f32)
|
||||
(block (result f32) (f32.neg (br_if 0 (f32.const 3) (i32.const 1))))
|
||||
)
|
||||
(func (export "type-f64-value") (result f64)
|
||||
(block (result f64) (f64.neg (br_if 0 (f64.const 4) (i32.const 1))))
|
||||
)
|
||||
|
||||
(func (export "as-block-first") (param i32) (result i32)
|
||||
(block (br_if 0 (get_local 0)) (return (i32.const 2))) (i32.const 3)
|
||||
)
|
||||
(func (export "as-block-mid") (param i32) (result i32)
|
||||
(block (call $dummy) (br_if 0 (get_local 0)) (return (i32.const 2)))
|
||||
(i32.const 3)
|
||||
)
|
||||
(func (export "as-block-last") (param i32)
|
||||
(block (call $dummy) (call $dummy) (br_if 0 (get_local 0)))
|
||||
)
|
||||
(func (export "as-block-first-value") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(drop (br_if 0 (i32.const 10) (get_local 0))) (return (i32.const 11))
|
||||
)
|
||||
)
|
||||
(func (export "as-block-mid-value") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(call $dummy)
|
||||
(drop (br_if 0 (i32.const 20) (get_local 0)))
|
||||
(return (i32.const 21))
|
||||
)
|
||||
)
|
||||
(func (export "as-block-last-value") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(call $dummy) (call $dummy) (br_if 0 (i32.const 11) (get_local 0))
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "as-loop-first") (param i32) (result i32)
|
||||
(block (loop (br_if 1 (get_local 0)) (return (i32.const 2)))) (i32.const 3)
|
||||
)
|
||||
(func (export "as-loop-mid") (param i32) (result i32)
|
||||
(block (loop (call $dummy) (br_if 1 (get_local 0)) (return (i32.const 2))))
|
||||
(i32.const 4)
|
||||
)
|
||||
(func (export "as-loop-last") (param i32)
|
||||
(loop (call $dummy) (br_if 1 (get_local 0)))
|
||||
)
|
||||
|
||||
(func (export "as-br-value") (result i32)
|
||||
(block (result i32) (br 0 (br_if 0 (i32.const 1) (i32.const 2))))
|
||||
)
|
||||
|
||||
(func (export "as-br_if-cond")
|
||||
(block (br_if 0 (br_if 0 (i32.const 1) (i32.const 1))))
|
||||
)
|
||||
(func (export "as-br_if-value") (result i32)
|
||||
(block (result i32)
|
||||
(drop (br_if 0 (br_if 0 (i32.const 1) (i32.const 2)) (i32.const 3)))
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(func (export "as-br_if-value-cond") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(drop (br_if 0 (i32.const 2) (br_if 0 (i32.const 1) (get_local 0))))
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "as-br_table-index")
|
||||
(block (br_table 0 0 0 (br_if 0 (i32.const 1) (i32.const 2))))
|
||||
)
|
||||
(func (export "as-br_table-value") (result i32)
|
||||
(block (result i32)
|
||||
(br_table 0 0 0 (br_if 0 (i32.const 1) (i32.const 2)) (i32.const 3)) (i32.const 4)
|
||||
)
|
||||
)
|
||||
(func (export "as-br_table-value-index") (result i32)
|
||||
(block (result i32)
|
||||
(br_table 0 0 (i32.const 2) (br_if 0 (i32.const 1) (i32.const 3))) (i32.const 4)
|
||||
)
|
||||
)
|
||||
(func (export "as-return-value") (result i64)
|
||||
(block (result i64) (return (br_if 0 (i64.const 1) (i32.const 2))))
|
||||
)
|
||||
|
||||
(func (export "as-if-cond") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(if (result i32)
|
||||
(br_if 0 (i32.const 1) (get_local 0))
|
||||
(then (i32.const 2))
|
||||
(else (i32.const 3))
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "as-if-then") (param i32 i32)
|
||||
(block
|
||||
(if (get_local 0) (then (br_if 1 (get_local 1))) (else (call $dummy)))
|
||||
)
|
||||
)
|
||||
(func (export "as-if-else") (param i32 i32)
|
||||
(block
|
||||
(if (get_local 0) (then (call $dummy)) (else (br_if 1 (get_local 1))))
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "as-select-first") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(select (br_if 0 (i32.const 3) (i32.const 10)) (i32.const 2) (get_local 0))
|
||||
)
|
||||
)
|
||||
(func (export "as-select-second") (param i32) (result i32)
|
||||
(block (result i32)
|
||||
(select (i32.const 1) (br_if 0 (i32.const 3) (i32.const 10)) (get_local 0))
|
||||
)
|
||||
)
|
||||
(func (export "as-select-cond") (result i32)
|
||||
(block (result i32)
|
||||
(select (i32.const 1) (i32.const 2) (br_if 0 (i32.const 3) (i32.const 10)))
|
||||
)
|
||||
)
|
||||
|
||||
(func $f (param i32 i32 i32) (result i32) (i32.const -1))
|
||||
(func (export "as-call-first") (result i32)
|
||||
(block (result i32)
|
||||
(call $f
|
||||
(br_if 0 (i32.const 12) (i32.const 1)) (i32.const 2) (i32.const 3)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "as-call-mid") (result i32)
|
||||
(block (result i32)
|
||||
(call $f
|
||||
(i32.const 1) (br_if 0 (i32.const 13) (i32.const 1)) (i32.const 3)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "as-call-last") (result i32)
|
||||
(block (result i32)
|
||||
(call $f
|
||||
(i32.const 1) (i32.const 2) (br_if 0 (i32.const 14) (i32.const 1))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func $func (param i32 i32 i32) (result i32) (get_local 0))
|
||||
(type $check (func (param i32 i32 i32) (result i32)))
|
||||
(table anyfunc (elem $func))
|
||||
(func (export "as-call_indirect-func") (result i32)
|
||||
(block (result i32)
|
||||
(call_indirect (type $check)
|
||||
(br_if 0 (i32.const 4) (i32.const 10))
|
||||
(i32.const 1) (i32.const 2) (i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "as-call_indirect-first") (result i32)
|
||||
(block (result i32)
|
||||
(call_indirect (type $check)
|
||||
(i32.const 1) (br_if 0 (i32.const 4) (i32.const 10)) (i32.const 2) (i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "as-call_indirect-mid") (result i32)
|
||||
(block (result i32)
|
||||
(call_indirect (type $check)
|
||||
(i32.const 1) (i32.const 2) (br_if 0 (i32.const 4) (i32.const 10)) (i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "as-call_indirect-last") (result i32)
|
||||
(block (result i32)
|
||||
(call_indirect (type $check)
|
||||
(i32.const 1) (i32.const 2) (i32.const 3) (br_if 0 (i32.const 4) (i32.const 10))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "as-set_local-value") (param i32) (result i32)
|
||||
(local i32)
|
||||
(block (result i32)
|
||||
(set_local 0 (br_if 0 (i32.const 17) (get_local 0)))
|
||||
(i32.const -1)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "as-unary-operand") (result f64)
|
||||
(block (result f64) (f64.neg (br_if 0 (f64.const 1.0) (i32.const 1))))
|
||||
)
|
||||
(func (export "as-binary-left") (result i32)
|
||||
(block (result i32) (i32.add (br_if 0 (i32.const 1) (i32.const 1)) (i32.const 10)))
|
||||
)
|
||||
(func (export "as-binary-right") (result i32)
|
||||
(block (result i32) (i32.sub (i32.const 10) (br_if 0 (i32.const 1) (i32.const 1))))
|
||||
)
|
||||
|
||||
(memory 0)
|
||||
(func (export "as-memory.grow-size") (result i32)
|
||||
(block (result i32) (memory.grow (br_if 0 (i32.const 1) (i32.const 1))))
|
||||
)
|
||||
|
||||
(func (export "nested-block-value") (param i32) (result i32)
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(block (result i32)
|
||||
(drop (i32.const 2))
|
||||
(i32.add
|
||||
(i32.const 4)
|
||||
(block (result i32)
|
||||
(drop (br_if 1 (i32.const 8) (get_local 0)))
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "nested-br-value") (param i32) (result i32)
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(block (result i32)
|
||||
(drop (i32.const 2))
|
||||
(br 0
|
||||
(block (result i32)
|
||||
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "nested-br_if-value") (param i32) (result i32)
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(block (result i32)
|
||||
(drop (i32.const 2))
|
||||
(drop (br_if 0
|
||||
(block (result i32)
|
||||
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 4)
|
||||
)
|
||||
(i32.const 1)
|
||||
))
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "nested-br_if-value-cond") (param i32) (result i32)
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(block (result i32)
|
||||
(drop (i32.const 2))
|
||||
(drop (br_if 0
|
||||
(i32.const 4)
|
||||
(block (result i32)
|
||||
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 1)
|
||||
)
|
||||
))
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "nested-br_table-value") (param i32) (result i32)
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(block (result i32)
|
||||
(drop (i32.const 2))
|
||||
(br_table 0
|
||||
(block (result i32)
|
||||
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 4)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "nested-br_table-value-index") (param i32) (result i32)
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(block (result i32)
|
||||
(drop (i32.const 2))
|
||||
(br_table 0
|
||||
(i32.const 4)
|
||||
(block (result i32)
|
||||
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 1)
|
||||
)
|
||||
)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
(assert_return (invoke "type-i32"))
|
||||
(assert_return (invoke "type-i64"))
|
||||
(assert_return (invoke "type-f32"))
|
||||
(assert_return (invoke "type-f64"))
|
||||
|
||||
(assert_return (invoke "type-i32-value") (i32.const 1))
|
||||
(assert_return (invoke "type-i64-value") (i64.const 2))
|
||||
(assert_return (invoke "type-f32-value") (f32.const 3))
|
||||
(assert_return (invoke "type-f64-value") (f64.const 4))
|
||||
|
||||
(assert_return (invoke "as-block-first" (i32.const 0)) (i32.const 2))
|
||||
(assert_return (invoke "as-block-first" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke "as-block-mid" (i32.const 0)) (i32.const 2))
|
||||
(assert_return (invoke "as-block-mid" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke "as-block-last" (i32.const 0)))
|
||||
(assert_return (invoke "as-block-last" (i32.const 1)))
|
||||
|
||||
(assert_return (invoke "as-block-first-value" (i32.const 0)) (i32.const 11))
|
||||
(assert_return (invoke "as-block-first-value" (i32.const 1)) (i32.const 10))
|
||||
(assert_return (invoke "as-block-mid-value" (i32.const 0)) (i32.const 21))
|
||||
(assert_return (invoke "as-block-mid-value" (i32.const 1)) (i32.const 20))
|
||||
(assert_return (invoke "as-block-last-value" (i32.const 0)) (i32.const 11))
|
||||
(assert_return (invoke "as-block-last-value" (i32.const 1)) (i32.const 11))
|
||||
|
||||
(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 2))
|
||||
(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 2))
|
||||
(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 4))
|
||||
(assert_return (invoke "as-loop-last" (i32.const 0)))
|
||||
(assert_return (invoke "as-loop-last" (i32.const 1)))
|
||||
|
||||
(assert_return (invoke "as-br-value") (i32.const 1))
|
||||
|
||||
(assert_return (invoke "as-br_if-cond"))
|
||||
(assert_return (invoke "as-br_if-value") (i32.const 1))
|
||||
(assert_return (invoke "as-br_if-value-cond" (i32.const 0)) (i32.const 2))
|
||||
(assert_return (invoke "as-br_if-value-cond" (i32.const 1)) (i32.const 1))
|
||||
|
||||
(assert_return (invoke "as-br_table-index"))
|
||||
(assert_return (invoke "as-br_table-value") (i32.const 1))
|
||||
(assert_return (invoke "as-br_table-value-index") (i32.const 1))
|
||||
|
||||
(assert_return (invoke "as-return-value") (i64.const 1))
|
||||
|
||||
(assert_return (invoke "as-if-cond" (i32.const 0)) (i32.const 2))
|
||||
(assert_return (invoke "as-if-cond" (i32.const 1)) (i32.const 1))
|
||||
(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 0)))
|
||||
(assert_return (invoke "as-if-then" (i32.const 4) (i32.const 0)))
|
||||
(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 1)))
|
||||
(assert_return (invoke "as-if-then" (i32.const 4) (i32.const 1)))
|
||||
(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 0)))
|
||||
(assert_return (invoke "as-if-else" (i32.const 3) (i32.const 0)))
|
||||
(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 1)))
|
||||
(assert_return (invoke "as-if-else" (i32.const 3) (i32.const 1)))
|
||||
|
||||
(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 3))
|
||||
(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke "as-select-second" (i32.const 0)) (i32.const 3))
|
||||
(assert_return (invoke "as-select-second" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke "as-select-cond") (i32.const 3))
|
||||
|
||||
(assert_return (invoke "as-call-first") (i32.const 12))
|
||||
(assert_return (invoke "as-call-mid") (i32.const 13))
|
||||
(assert_return (invoke "as-call-last") (i32.const 14))
|
||||
|
||||
(assert_return (invoke "as-call_indirect-func") (i32.const 4))
|
||||
(assert_return (invoke "as-call_indirect-first") (i32.const 4))
|
||||
(assert_return (invoke "as-call_indirect-mid") (i32.const 4))
|
||||
(assert_return (invoke "as-call_indirect-last") (i32.const 4))
|
||||
|
||||
(assert_return (invoke "as-set_local-value" (i32.const 0)) (i32.const -1))
|
||||
(assert_return (invoke "as-set_local-value" (i32.const 1)) (i32.const 17))
|
||||
|
||||
(assert_return (invoke "as-unary-operand") (f64.const 1.0))
|
||||
(assert_return (invoke "as-binary-left") (i32.const 1))
|
||||
(assert_return (invoke "as-binary-right") (i32.const 1))
|
||||
(assert_return (invoke "as-memory.grow-size") (i32.const 1))
|
||||
|
||||
(assert_return (invoke "nested-block-value" (i32.const 0)) (i32.const 21))
|
||||
(assert_return (invoke "nested-block-value" (i32.const 1)) (i32.const 9))
|
||||
(assert_return (invoke "nested-br-value" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke "nested-br-value" (i32.const 1)) (i32.const 9))
|
||||
(assert_return (invoke "nested-br_if-value" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke "nested-br_if-value" (i32.const 1)) (i32.const 9))
|
||||
(assert_return (invoke "nested-br_if-value-cond" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke "nested-br_if-value-cond" (i32.const 1)) (i32.const 9))
|
||||
(assert_return (invoke "nested-br_table-value" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke "nested-br_table-value" (i32.const 1)) (i32.const 9))
|
||||
(assert_return (invoke "nested-br_table-value-index" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke "nested-br_table-value-index" (i32.const 1)) (i32.const 9))
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-false-i32 (block (i32.ctz (br_if 0 (i32.const 0))))))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-false-i64 (block (i64.ctz (br_if 0 (i32.const 0))))))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-false-f32 (block (f32.neg (br_if 0 (i32.const 0))))))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-false-f64 (block (f64.neg (br_if 0 (i32.const 0))))))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-true-i32 (block (i32.ctz (br_if 0 (i32.const 1))))))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-i64 (block (i64.ctz (br_if 0 (i64.const 1))))))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-f32 (block (f32.neg (br_if 0 (f32.const 1))))))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-f64 (block (f64.neg (br_if 0 (i64.const 1))))))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-false-arg-void-vs-num (result i32)
|
||||
(block (result i32) (br_if 0 (i32.const 0)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-arg-void-vs-num (result i32)
|
||||
(block (result i32) (br_if 0 (i32.const 1)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-false-arg-num-vs-void
|
||||
(block (br_if 0 (i32.const 0) (i32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-arg-num-vs-void
|
||||
(block (br_if 0 (i32.const 0) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-false-arg-void-vs-num (result i32)
|
||||
(block (result i32) (br_if 0 (nop) (i32.const 0)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-arg-void-vs-num (result i32)
|
||||
(block (result i32) (br_if 0 (nop) (i32.const 1)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-false-arg-num-vs-num (result i32)
|
||||
(block (result i32)
|
||||
(drop (br_if 0 (i64.const 1) (i32.const 0))) (i32.const 1)
|
||||
)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-true-arg-num-vs-num (result i32)
|
||||
(block (result i32)
|
||||
(drop (br_if 0 (i64.const 1) (i32.const 0))) (i32.const 1)
|
||||
)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-cond-void-vs-i32
|
||||
(block (br_if 0 (nop)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-cond-num-vs-i32
|
||||
(block (br_if 0 (i64.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-arg-cond-void-vs-i32 (result i32)
|
||||
(block (result i32) (br_if 0 (i32.const 0) (nop)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-arg-void-vs-num-nested (result i32)
|
||||
(block (result i32) (i32.const 0) (block (br_if 1 (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-arg-cond-num-vs-i32 (result i32)
|
||||
(block (result i32) (br_if 0 (i32.const 0) (i64.const 0)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $unbound-label (br_if 1 (i32.const 1))))
|
||||
"unknown label"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $unbound-nested-label (block (block (br_if 5 (i32.const 1))))))
|
||||
"unknown label"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $large-label (br_if 0x10000001 (i32.const 1))))
|
||||
"unknown label"
|
||||
)
|
224
src/webassembly/execute.rs
Normal file
224
src/webassembly/execute.rs
Normal file
@ -0,0 +1,224 @@
|
||||
use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{DefinedFuncIndex, MemoryIndex};
|
||||
use region::protect;
|
||||
use region::Protection;
|
||||
use std::mem::transmute;
|
||||
use std::ptr::{self, write_unaligned};
|
||||
|
||||
use super::compilation::{
|
||||
compile_module, Compilation, Relocation, RelocationTarget,
|
||||
};
|
||||
use super::memory::LinearMemory;
|
||||
use super::module::{Module, Export};
|
||||
use super::instance::Instance;
|
||||
use super::environ::ModuleTranslation;
|
||||
|
||||
/// Executes a module that has been translated with the `wasmtime-environ` environment
|
||||
/// implementation.
|
||||
pub fn compile_and_link_module<'data, 'module>(
|
||||
isa: &TargetIsa,
|
||||
translation: &ModuleTranslation<'data, 'module>,
|
||||
) -> Result<Compilation, String> {
|
||||
let (mut compilation, relocations) = compile_module(&translation, isa)?;
|
||||
|
||||
// Apply relocations, now that we have virtual addresses for everything.
|
||||
relocate(&mut compilation, &relocations, &translation.module);
|
||||
|
||||
Ok(compilation)
|
||||
}
|
||||
|
||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata
|
||||
fn relocate(
|
||||
compilation: &mut Compilation,
|
||||
relocations: &PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||
module: &Module,
|
||||
) {
|
||||
// The relocations are relative to the relocation's address plus four bytes
|
||||
// TODO: Support architectures other than x64, and other reloc kinds.
|
||||
for (i, function_relocs) in relocations.iter() {
|
||||
for r in function_relocs {
|
||||
let target_func_address: isize = match r.reloc_target {
|
||||
RelocationTarget::UserFunc(index) => {
|
||||
compilation.functions[module.defined_func_index(index).expect(
|
||||
"relocation to imported function not supported yet",
|
||||
)].as_ptr() as isize
|
||||
}
|
||||
RelocationTarget::GrowMemory => grow_memory as isize,
|
||||
RelocationTarget::CurrentMemory => current_memory as isize,
|
||||
};
|
||||
|
||||
let body = &mut compilation.functions[i];
|
||||
match r.reloc {
|
||||
Reloc::Abs8 => unsafe {
|
||||
let reloc_address = body.as_mut_ptr().offset(r.offset as isize) as i64;
|
||||
let reloc_addend = r.addend;
|
||||
let reloc_abs = target_func_address as i64 + reloc_addend;
|
||||
write_unaligned(reloc_address as *mut i64, reloc_abs);
|
||||
},
|
||||
Reloc::X86PCRel4 => unsafe {
|
||||
let reloc_address = body.as_mut_ptr().offset(r.offset as isize) as isize;
|
||||
let reloc_addend = r.addend as isize;
|
||||
// TODO: Handle overflow.
|
||||
let reloc_delta_i32 =
|
||||
(target_func_address - reloc_address + reloc_addend) as i32;
|
||||
write_unaligned(reloc_address as *mut i32, reloc_delta_i32);
|
||||
},
|
||||
_ => panic!("unsupported reloc kind"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 {
|
||||
unsafe {
|
||||
let instance = (*vmctx.offset(4)) as *mut Instance;
|
||||
(*instance)
|
||||
.memory_mut(memory_index as MemoryIndex)
|
||||
.grow(size)
|
||||
.unwrap_or(u32::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 {
|
||||
unsafe {
|
||||
let instance = (*vmctx.offset(4)) as *mut Instance;
|
||||
(*instance)
|
||||
.memory_mut(memory_index as MemoryIndex)
|
||||
.current_size()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the VmCtx data structure for the JIT'd code to use. This must
|
||||
/// match the VmCtx layout in the environment.
|
||||
fn make_vmctx(instance: &mut Instance, mem_base_addrs: &mut [*mut u8]) -> Vec<*mut u8> {
|
||||
debug_assert!(
|
||||
instance.tables.len() <= 1,
|
||||
"non-default tables is not supported"
|
||||
);
|
||||
|
||||
let (default_table_ptr, default_table_len) = instance
|
||||
.tables
|
||||
.get_mut(0)
|
||||
.map(|table| (table.as_mut_ptr() as *mut u8, table.len()))
|
||||
.unwrap_or((ptr::null_mut(), 0));
|
||||
|
||||
let mut vmctx = Vec::new();
|
||||
vmctx.push(instance.globals.as_mut_ptr());
|
||||
vmctx.push(mem_base_addrs.as_mut_ptr() as *mut u8);
|
||||
vmctx.push(default_table_ptr);
|
||||
vmctx.push(default_table_len as *mut u8);
|
||||
vmctx.push(instance as *mut Instance as *mut u8);
|
||||
|
||||
vmctx
|
||||
}
|
||||
|
||||
/// Jumps to the code region of memory and execute the start function of the module.
|
||||
pub fn execute(
|
||||
module: &Module,
|
||||
compilation: &Compilation,
|
||||
instance: &mut Instance,
|
||||
) -> Result<(), String> {
|
||||
println!("execute");
|
||||
|
||||
let start_index = module.start_func.or_else(|| {
|
||||
match module.exports.get("main") {
|
||||
Some(&Export::Function(index)) => Some(index),
|
||||
_ => None
|
||||
}
|
||||
}) ;
|
||||
// else {
|
||||
// // TODO: We really need to handle this error nicely
|
||||
// return Err("need to have a start function".to_string());
|
||||
// }
|
||||
// let start_index = module
|
||||
// .start_func
|
||||
// .ok_or_else(|| String::from("No start function defined, aborting execution"))?;
|
||||
|
||||
// We have to relocate here
|
||||
|
||||
|
||||
// TODO: Put all the function bodies into a page-aligned memory region, and
|
||||
// then make them ReadExecute rather than ReadWriteExecute.
|
||||
for code_buf in compilation.functions.values() {
|
||||
match unsafe {
|
||||
protect(
|
||||
code_buf.as_ptr(),
|
||||
code_buf.len(),
|
||||
Protection::ReadWriteExecute,
|
||||
)
|
||||
} {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
return Err(format!(
|
||||
"failed to give executable permission to code: {}",
|
||||
err
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let code_buf = start_index.map(|i| {
|
||||
&compilation.functions[module
|
||||
.defined_func_index(i)
|
||||
.expect("imported start functions not supported yet")]
|
||||
});
|
||||
|
||||
// Collect all memory base addresses and Vec.
|
||||
let mut mem_base_addrs = instance
|
||||
.memories
|
||||
.iter_mut()
|
||||
.map(LinearMemory::base_addr)
|
||||
.collect::<Vec<_>>();
|
||||
let vmctx = make_vmctx(instance, &mut mem_base_addrs);
|
||||
|
||||
code_buf.map(|code_buf_pt|{
|
||||
// Rather than writing inline assembly to jump to the code region, we use the fact that
|
||||
// the Rust ABI for calling a function with no arguments and no return matches the one of
|
||||
// the generated code. Thanks to this, we can transmute the code region into a first-class
|
||||
// Rust function and call it.
|
||||
unsafe {
|
||||
let start_func = transmute::<_, fn(*const *mut u8)>(code_buf_pt.as_ptr());
|
||||
start_func(vmctx.as_ptr());
|
||||
}
|
||||
});
|
||||
println!("{:?}", module.exports);
|
||||
println!("execute end");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// pub fn execute_fn(
|
||||
// instance: &mut Instance,
|
||||
// func_name: String
|
||||
// ) -> Result<(), String> {
|
||||
// println!("execute");
|
||||
|
||||
// let start_index = match instance.module.exports.get(&func_name) {
|
||||
// Some(&Export::Function(index)) => index,
|
||||
// _ => panic!("No func name")
|
||||
// };
|
||||
|
||||
// let code_buf = &instance.compilation.functions[instance.module
|
||||
// .defined_func_index(start_index)
|
||||
// .expect("imported start functions not supported yet")];
|
||||
|
||||
|
||||
// let mut mem_base_addrs = instance
|
||||
// .memories
|
||||
// .iter_mut()
|
||||
// .map(LinearMemory::base_addr)
|
||||
// .collect::<Vec<_>>();
|
||||
|
||||
// let vmctx = make_vmctx(instance, &mut mem_base_addrs);
|
||||
|
||||
// unsafe {
|
||||
// let start_func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr());
|
||||
// start_func(vmctx.as_ptr())
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
@ -10,6 +10,10 @@ use super::compilation::Compilation;
|
||||
/// An Instance of a WebAssemby module.
|
||||
#[derive(Debug)]
|
||||
pub struct Instance {
|
||||
// pub module: Box<Module>,
|
||||
|
||||
// pub compilation: Box<Compilation>,
|
||||
|
||||
/// WebAssembly table data.
|
||||
pub tables: Vec<Vec<usize>>,
|
||||
|
||||
@ -28,12 +32,17 @@ impl Instance {
|
||||
data_initializers: &[DataInitializer],
|
||||
) -> Self {
|
||||
let mut result = Self {
|
||||
// module: Box::new(module),
|
||||
// compilation: Box::new(compilation),
|
||||
tables: Vec::new(),
|
||||
memories: Vec::new(),
|
||||
globals: Vec::new(),
|
||||
};
|
||||
// println!("Instance::instantiate tables");
|
||||
result.instantiate_tables(module, compilation, &module.table_elements);
|
||||
// println!("Instance::instantiate memories");
|
||||
result.instantiate_memories(module, data_initializers);
|
||||
// println!("Instance::instantiate globals");
|
||||
result.instantiate_globals(module);
|
||||
result
|
||||
}
|
||||
@ -70,12 +79,23 @@ impl Instance {
|
||||
fn instantiate_memories(&mut self, module: &Module, data_initializers: &[DataInitializer]) {
|
||||
debug_assert!(self.memories.is_empty());
|
||||
// Allocate the underlying memory and initialize it to all zeros.
|
||||
// println!("instantiate_memories::reserve exact");
|
||||
self.memories.reserve_exact(module.memories.len());
|
||||
// println!("instantiate_memories::loop");
|
||||
for memory in &module.memories {
|
||||
let v = LinearMemory::new(memory.pages_count as u32, memory.maximum.map(|m| m as u32));
|
||||
// println!("instantiate_memories::new linear memory: {}", memory.pages_count);
|
||||
// We do this so at least there is one page
|
||||
let pages_count = if (memory.pages_count as u32) > 0 {
|
||||
memory.pages_count as u32
|
||||
}
|
||||
else {
|
||||
1
|
||||
};
|
||||
let v = LinearMemory::new(pages_count, memory.maximum.map(|m| m as u32));
|
||||
self.memories.push(v);
|
||||
}
|
||||
for init in data_initializers {
|
||||
// println!("instantiate_memories::initialize data");
|
||||
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
|
||||
let mem_mut = self.memories[init.memory_index].as_mut();
|
||||
let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()];
|
||||
|
@ -4,6 +4,7 @@ pub mod memory;
|
||||
pub mod environ;
|
||||
pub mod instance;
|
||||
pub mod errors;
|
||||
pub mod execute;
|
||||
pub mod utils;
|
||||
|
||||
use cranelift_native;
|
||||
@ -16,15 +17,16 @@ pub use self::environ::ModuleEnvironment;
|
||||
pub use self::module::Module;
|
||||
pub use self::instance::Instance;
|
||||
pub use self::errors::{Error, ErrorKind};
|
||||
pub use self::execute::{compile_and_link_module,execute};
|
||||
use wasmparser;
|
||||
|
||||
pub struct ResultObject {
|
||||
/// A WebAssembly.Module object representing the compiled WebAssembly module.
|
||||
/// This Module can be instantiated again
|
||||
module: Module,
|
||||
pub module: Module,
|
||||
/// A WebAssembly.Instance object that contains all the Exported WebAssembly
|
||||
/// functions.
|
||||
instance: Instance
|
||||
pub instance: Instance
|
||||
}
|
||||
|
||||
pub struct ImportObject {
|
||||
@ -47,13 +49,31 @@ pub struct ImportObject {
|
||||
/// If the operation fails, the Result rejects with a
|
||||
/// WebAssembly.CompileError, WebAssembly.LinkError, or
|
||||
/// WebAssembly.RuntimeError, depending on the cause of the failure.
|
||||
pub fn instantiate(buffer_source: Vec<u8>, import_object: ImportObject) -> Result<ResultObject, Error> {
|
||||
let module = compile(buffer_source)?;
|
||||
let instance = Instance {
|
||||
tables: Vec::new(),
|
||||
memories: Vec::new(),
|
||||
globals: Vec::new(),
|
||||
};
|
||||
pub fn instantiate(buffer_source: Vec<u8>, import_object: Option<ImportObject>) -> Result<ResultObject, Error> {
|
||||
let isa = construct_isa();
|
||||
println!("instantiate::init");
|
||||
let mut module = Module::new();
|
||||
let environ = ModuleEnvironment::new(&*isa, &mut module);
|
||||
let translation = environ.translate(&buffer_source).map_err(|e| ErrorKind::CompileError(e.to_string()))?;
|
||||
println!("instantiate::compile and link");
|
||||
let compilation = compile_and_link_module(&*isa, &translation)?;
|
||||
// let (compilation, relocations) = compile_module(&translation, &*isa)?;
|
||||
println!("instantiate::instantiate");
|
||||
|
||||
let mut instance = Instance::new(
|
||||
translation.module,
|
||||
&compilation,
|
||||
&translation.lazy.data_initializers,
|
||||
);
|
||||
println!("instantiate::execute");
|
||||
|
||||
let x = execute(&module, &compilation, &mut instance)?;
|
||||
|
||||
// let instance = Instance {
|
||||
// tables: Vec::new(),
|
||||
// memories: Vec::new(),
|
||||
// globals: Vec::new(),
|
||||
// };
|
||||
|
||||
Ok(ResultObject {
|
||||
module,
|
||||
@ -79,7 +99,8 @@ pub fn compile(buffer_source: Vec<u8>) -> Result<Module, Error> {
|
||||
let mut module = Module::new();
|
||||
let environ = ModuleEnvironment::new(&*isa, &mut module);
|
||||
let translation = environ.translate(&buffer_source).map_err(|e| ErrorKind::CompileError(e.to_string()))?;
|
||||
compile_module(&translation, &*isa)?;
|
||||
// compile_module(&translation, &*isa)?;
|
||||
compile_and_link_module(&*isa, &translation)?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ pub enum Export {
|
||||
|
||||
/// A translated WebAssembly module, excluding the function bodies and
|
||||
/// memory initializers.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Module {
|
||||
/// Unprocessed signatures exactly as provided by `declare_signature()`.
|
||||
pub signatures: Vec<ir::Signature>,
|
||||
|
Reference in New Issue
Block a user