Improved tests

This commit is contained in:
Syrus Akbary
2018-10-12 02:45:09 +02:00
parent 9bca6940b3
commit b4a09a2e94
8 changed files with 873 additions and 38 deletions

View File

@ -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(())
}

View File

@ -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)]

View File

@ -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
View 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
View 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(())
// }

View File

@ -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()];

View File

@ -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)
}

View File

@ -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>,