diff --git a/Cargo.lock b/Cargo.lock index df746ea10..f328e2b5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,6 +752,7 @@ dependencies = [ "cranelift-wasm 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "libffi 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 7e6793954..ea098bdf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "wasmer" version = "0.1.0" authors = ["Syrus Akbary "] edition = "2018" -repository = "https://github.com/wapmio/wasmer" +repository = "https://github.com/wafoundation/wasmer" publish = false [dependencies] @@ -28,9 +28,11 @@ memmap = "0.6.2" spin = "0.4.9" log = "0.4.5" target-lexicon = { version = "0.0.3", default-features = false } +libc = "0.2" [dev-dependencies] libffi = "0.6.4" +# maplit = "1.0.1" [features] debug = [] diff --git a/src/integrations/mod.rs b/src/integrations/mod.rs new file mode 100644 index 000000000..5f1e13b6d --- /dev/null +++ b/src/integrations/mod.rs @@ -0,0 +1,27 @@ +use libc::putchar; + +#[cfg(test)] +mod tests { + use crate::webassembly::{ + instantiate, ErrorKind, Export, ImportObject, Instance, Module, ResultObject, + }; + use libc::putchar; + + #[test] + fn test_putchar() { + let wasm_bytes = include_wast2wasm_bytes!("tests/putchar.wast"); + let mut import_object = ImportObject::new(); + import_object.set("env", "putchar", putchar as *const u8); + + let result_object = + instantiate(wasm_bytes, Some(import_object)).expect("Not compiled properly"); + let module = result_object.module; + let instance = result_object.instance; + let func_index = match module.info.exports.get("main") { + Some(&Export::Function(index)) => index, + _ => panic!("Function not found"), + }; + let main: fn() = get_instance_function!(instance, func_index); + main(); + } +} diff --git a/src/integrations/tests/putchar.wast b/src/integrations/tests/putchar.wast new file mode 100644 index 000000000..d61d6ada0 --- /dev/null +++ b/src/integrations/tests/putchar.wast @@ -0,0 +1,16 @@ +(module + (type $FUNCSIG$ii (func (param i32) (result i32))) + (import "env" "putchar" (func $putchar (param i32) (result i32))) + (table 0 anyfunc) + (memory $0 1) + (export "memory" (memory $0)) + (export "main" (func $main)) + (func $main (; 1 ;) (result i32) + (drop + (call $putchar + (i32.const 97) + ) + ) + (i32.const 0) + ) +) diff --git a/src/macros.rs b/src/macros.rs index e09dae517..beda68363 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -9,6 +9,14 @@ macro_rules! get_instance_function { }}; } +macro_rules! include_wast2wasm_bytes { + ($x:expr) => {{ + use wabt::wat2wasm; + const wast_bytes: &[u8] = include_bytes!($x); + wat2wasm(wast_bytes.to_vec()).expect(&format!("Can't convert {} file to wasm", $x)) + }}; +} + // #[cfg(feature = "debug")] #[macro_export] macro_rules! debug { diff --git a/src/main.rs b/src/main.rs index 463b9b9f6..ec3d4be3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(test)] +#![feature(test, libc)] extern crate test; #[macro_use] @@ -18,6 +18,7 @@ use std::time::{Duration, Instant}; // #[macro_use] extern crate log; +use libc; use std::error::Error; use std::fs::File; use std::io; @@ -31,6 +32,7 @@ use wabt::wat2wasm; #[macro_use] mod macros; pub mod common; +pub mod integrations; pub mod spec; pub mod webassembly; diff --git a/src/spec/tests.rs b/src/spec/tests.rs index ba74baf65..e543bdf06 100644 --- a/src/spec/tests.rs +++ b/src/spec/tests.rs @@ -12,7 +12,7 @@ use wabt::script::{Action, Value}; use super::{run_single_file, InvokationResult, ScriptHandler}; use crate::webassembly::{ - compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject, + compile, instantiate, Error, ErrorKind, Export, ImportObject, Instance, Module, ResultObject, }; struct StoreCtrl<'module> { @@ -127,18 +127,15 @@ impl<'module> ScriptHandler for StoreCtrl<'module> { unimplemented!() } fn module(&mut self, bytes: Vec, name: Option) { - let mut import_object = HashMap::new(); - let mut test_import = import_object - .entry("test".to_string()) - .or_insert_with(|| HashMap::new()); - fn identity(x: i32) -> i32 { + let mut import_object = ImportObject::new(); + extern "C" fn identity(x: i32) -> i32 { x }; - test_import.insert("identity".to_string(), identity as *const u8); + import_object.set("test", "identity", identity as *const u8); // let import_object = import_object!{ // test.identity => fn(x: i32) {x}, // } - let module_wrapped = instantiate(bytes, Some(&import_object)); + let module_wrapped = instantiate(bytes, Some(import_object)); let mut result = module_wrapped.expect("Module is invalid"); // let module: &'module Module = result.module; self.last_module = Some(result); @@ -211,8 +208,7 @@ mod tests { macro_rules! instantiate_from_wast { ($x:expr) => {{ - pub const WAST_BYTES: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x)); - let wasm_bytes = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert wat to wasm"); + let wasm_bytes = include_wast2wasm_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x)); let result_object = instantiate(wasm_bytes, None).expect("Not compiled properly"); result_object }}; diff --git a/src/webassembly/import_object.rs b/src/webassembly/import_object.rs new file mode 100644 index 000000000..cf3db5de1 --- /dev/null +++ b/src/webassembly/import_object.rs @@ -0,0 +1,136 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + +// See explanation (1). +#[derive(PartialEq, Eq, Hash)] +struct Pair(A, B); + +#[derive(PartialEq, Eq, Hash)] +struct BorrowedPair<'a, 'b, A: 'a, B: 'b>(&'a A, &'b B); + +// See explanation (2). +trait KeyPair { + /// Obtains the first element of the pair. + fn a(&self) -> &A; + /// Obtains the second element of the pair. + fn b(&self) -> &B; +} + +// See explanation (3). +impl<'a, A, B> Borrow + 'a> for Pair +where + A: Eq + Hash + 'a, + B: Eq + Hash + 'a, +{ + fn borrow(&self) -> &(KeyPair + 'a) { + self + } +} + +// See explanation (4). +impl<'a, A: Hash, B: Hash> Hash for (KeyPair + 'a) { + fn hash(&self, state: &mut H) { + self.a().hash(state); + self.b().hash(state); + } +} + +impl<'a, A: Eq, B: Eq> PartialEq for (KeyPair + 'a) { + fn eq(&self, other: &Self) -> bool { + self.a() == other.a() && self.b() == other.b() + } +} + +impl<'a, A: Eq, B: Eq> Eq for (KeyPair + 'a) {} + +// OP's ImportObject struct +pub struct ImportObject { + map: HashMap, *const u8>, +} + +impl ImportObject { + pub fn new() -> Self { + ImportObject { + map: HashMap::new(), + } + } + + pub fn get(&self, a: &A, b: &B) -> Option<*const u8> { + self.map + .get(&BorrowedPair(a, b) as &KeyPair) + .map(|p| *p) + } + + pub fn set(&mut self, a: A, b: B, v: *const u8) { + self.map.insert(Pair(a, b), v); + } +} + +// pub struct ImportObject { +// map: HashMap, *const u8>, +// } + +// impl ImportObject { +// pub fn new() -> Self { +// ImportObject { map: HashMap::new() } +// } + +// pub fn get(&self, a: &A, b: &B) -> *const u8 { +// *self.map.get(&BorrowedPair(a, b) as &KeyPair).unwrap() +// } + +// pub fn set(&mut self, a: A, b: B, v: *const u8) { +// self.map.insert(Pair(a, b), v); +// } +// } +// Boring stuff below. + +impl KeyPair for Pair +where + A: Eq + Hash, + B: Eq + Hash, +{ + fn a(&self) -> &A { + &self.0 + } + fn b(&self) -> &B { + &self.1 + } +} +impl<'a, 'b, A, B> KeyPair for BorrowedPair<'a, 'b, A, B> +where + A: Eq + Hash + 'a, + B: Eq + Hash + 'b, +{ + fn a(&self) -> &A { + self.0 + } + fn b(&self) -> &B { + self.1 + } +} + +//---------------------------------------------------------------- + +// #[derive(Eq, PartialEq, Hash)] +// struct A(&'static str); + +// #[derive(Eq, PartialEq, Hash)] +// struct B(&'static str); + +#[cfg(test)] +mod tests { + use super::ImportObject; + + #[test] + fn test_import_object() { + fn x() {} + let mut import_object = ImportObject::new(); + import_object.set("abc", "def", x as *const u8); + // import_object.set("123"), A("456"), 45.0); + assert_eq!(import_object.get(&"abc", &"def").unwrap(), x as *const u8); + // assert_eq!(import_object.get(&"abc", &"dxf"), 4.0); + // assert_eq!(import_object.get(&A("123"), &A("456")), 45.0); + } +} diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index ec27d9c6d..8e70dcb3f 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -21,6 +21,7 @@ use std::{mem, slice}; use super::super::common::slice::{BoundedSlice, UncheckedSlice}; use super::errors::ErrorKind; +use super::import_object::ImportObject; use super::memory::LinearMemory; use super::module::Module; use super::module::{DataInitializer, Export, Exportable}; @@ -99,9 +100,6 @@ pub struct UserData { pub instance: Instance, } -// The import object for the instance -pub type ImportObject = HashMap>; - /// An Instance of a WebAssembly module #[derive(Debug)] pub struct Instance { @@ -157,7 +155,7 @@ impl Instance { /// Create a new `Instance`. pub fn new( module: &Module, - import_object: Option<&ImportObject>, + import_object: &ImportObject<&str, &str>, ) -> Result { let mut tables: Vec> = Vec::new(); let mut memories: Vec = Vec::new(); @@ -178,14 +176,15 @@ impl Instance { // let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len()); for (module, field) in module.info.imported_funcs.iter() { // let function = &import_object.map(|i| i.get(module).map(|m| m.get(field))); - let mut function = fake_fun as *const u8; - if let Some(import_object) = import_object { - if let Some(module) = import_object.get(module) { - if let Some(field_function) = module.get(field) { - function = *field_function; - } - } - } + // let mut function = fake_fun as *const u8; + let mut function = import_object + .get(&module.as_str(), &field.as_str()) + .ok_or_else(|| { + ErrorKind::LinkError(format!( + "Imported function {}.{} was not provided in the import_functions", + module, field + )) + })?; // println!("GET FUNC {:?}", function); import_functions.push(function); relocations.push(vec![]); @@ -444,8 +443,7 @@ impl Instance { self.memories.clone() } pub fn get_function_pointer(&self, func_index: FuncIndex) -> *const u8 { - let func_pointer = &self.functions[func_index.index()]; - func_pointer.as_ptr() + get_function_addr(&func_index, &self.import_functions, &self.functions) } // pub fn is_imported_function(&self, func_index: FuncIndex) -> bool { diff --git a/src/webassembly/mod.rs b/src/webassembly/mod.rs index 5fbe64024..439b32850 100644 --- a/src/webassembly/mod.rs +++ b/src/webassembly/mod.rs @@ -1,4 +1,5 @@ pub mod errors; +pub mod import_object; pub mod instance; pub mod memory; pub mod module; @@ -14,7 +15,8 @@ use target_lexicon::{self, Triple}; use wasmparser; pub use self::errors::{Error, ErrorKind}; -pub use self::instance::{ImportObject, Instance}; +pub use self::import_object::ImportObject; +pub use self::instance::Instance; pub use self::memory::LinearMemory; pub use self::module::{Export, Module, ModuleInfo}; @@ -43,11 +45,15 @@ pub struct ResultObject { /// webassembly::RuntimeError, depending on the cause of the failure. pub fn instantiate( buffer_source: Vec, - import_object: Option<&ImportObject>, + import_object: Option>, ) -> Result { let module = compile(buffer_source)?; debug!("webassembly - creating instance"); - let instance = Instance::new(&module, import_object)?; + let import_object: ImportObject<&str, &str> = match import_object { + Some(import_object) => import_object, + None => ImportObject::new(), + }; + let instance = Instance::new(&module, &import_object)?; debug!("webassembly - instance created"); Ok(ResultObject { module, instance }) }