diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 880e992909..ddedd8ca97 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -903,6 +903,11 @@ dependencies = [ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memorydb" version = "0.1.1" @@ -1021,12 +1026,12 @@ dependencies = [ [[package]] name = "parity-wasm" -version = "0.15.4" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1703,7 +1708,6 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1713,6 +1717,7 @@ dependencies = [ "substrate-serializer 0.1.0", "substrate-state-machine 0.1.0", "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2320,6 +2325,16 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasmi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -2474,6 +2489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013b7e4c5e10c764936ebc6bd3662d8e3c92292d267bf6a42ef3f5cad9c793ee" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" @@ -2487,7 +2503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)" = "235801e9531998c4bb307f4ea6833c9f40a4cf132895219ac8c2cd25a9b310f7" +"checksum parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba1ceaec13865445bcf05117867e4c6456d91c3617cdff2f3ef77b92b18cd12" "checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" @@ -2575,6 +2591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba06def0c95a653122299e68a44f2f227eeac2d1f707df61f33abbaf6dd55992" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/substrate/substrate/executor/Cargo.toml b/substrate/substrate/executor/Cargo.toml index 7a4a5c8af0..ef99cd0e3e 100644 --- a/substrate/substrate/executor/Cargo.toml +++ b/substrate/substrate/executor/Cargo.toml @@ -13,7 +13,7 @@ substrate-state-machine = { path = "../state-machine" } ed25519 = { path = "../ed25519" } serde = "1.0" serde_derive = "1.0" -parity-wasm = "0.15.0" +wasmi = "0.1.0" byteorder = "1.1" rustc-hex = "1.0.0" triehash = "0.1.0" diff --git a/substrate/substrate/executor/src/error.rs b/substrate/substrate/executor/src/error.rs index 346fa16c6a..639d23c075 100644 --- a/substrate/substrate/executor/src/error.rs +++ b/substrate/substrate/executor/src/error.rs @@ -17,10 +17,13 @@ //! Rust executor possible errors. use serializer; +use wasmi; error_chain! { foreign_links { InvalidData(serializer::Error) #[doc = "Unserializable Data"]; + Trap(wasmi::Trap) #[doc = "Trap occured during execution"]; + Wasmi(wasmi::Error) #[doc = "Wasmi loading/instantiating error"]; } errors { diff --git a/substrate/substrate/executor/src/lib.rs b/substrate/substrate/executor/src/lib.rs index 1b4588db87..06c378e181 100644 --- a/substrate/substrate/executor/src/lib.rs +++ b/substrate/substrate/executor/src/lib.rs @@ -35,7 +35,7 @@ extern crate substrate_state_machine as state_machine; extern crate ed25519; extern crate serde; -extern crate parity_wasm; +extern crate wasmi; extern crate byteorder; extern crate rustc_hex; extern crate triehash; @@ -47,12 +47,6 @@ extern crate error_chain; #[cfg(test)] extern crate assert_matches; -// TODO: move into own crate -macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) -} #[macro_use] mod wasm_utils; diff --git a/substrate/substrate/executor/src/wasm_executor.rs b/substrate/substrate/executor/src/wasm_executor.rs index d95951e76d..ce17fafac3 100644 --- a/substrate/substrate/executor/src/wasm_executor.rs +++ b/substrate/substrate/executor/src/wasm_executor.rs @@ -16,16 +16,14 @@ //! Rust implementation of Substrate contracts. -use std::sync::Arc; use std::cmp::Ordering; use std::collections::HashMap; -use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; -use parity_wasm::interpreter::{ItemIndex, DummyUserError}; -use parity_wasm::RuntimeValue::{I32, I64}; +use wasmi::{Module, ModuleInstance, MemoryInstance, MemoryRef, ImportsBuilder}; +use wasmi::RuntimeValue::{I32, I64}; +use wasmi::memory_units::{Pages, Bytes}; use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; -use wasm_utils::{MemoryInstance, UserDefinedElements, - AddModuleWithoutFullDependentInstance}; +use wasm_utils::{DummyUserError}; use primitives::{blake2_256, twox_128, twox_256}; use primitives::hexdisplay::HexDisplay; use triehash::ordered_trie_root; @@ -35,22 +33,21 @@ struct Heap { } impl Heap { - fn new(memory: &MemoryInstance) -> Result { - const HEAP_SIZE_IN_PAGES: u32 = 8; - const PAGE_SIZE_IN_BYTES: u32 = 65536; + /// Construct new `Heap` struct. + /// + /// Returns `Err` if the heap couldn't allocate required + /// number of pages. + /// + /// This could mean that wasm binary specifies memory + /// limit and we are trying to allocate beyond that limit. + fn new(memory: &MemoryRef) -> Result { + const HEAP_SIZE_IN_PAGES: usize = 8; - let prev_page_count = memory.grow(HEAP_SIZE_IN_PAGES).map_err( - |_: ::parity_wasm::interpreter::Error| Error::from(ErrorKind::Runtime), - )?; - if prev_page_count == 0xFFFFFFFF { - // Wasm vm refuses to mount the specified amount of new pages. This - // could mean that wasm binary specifies memory limit and we are trying - // to allocate beyond that limit. - return Err(ErrorKind::Runtime.into()); - } - let allocated_area_start = prev_page_count * PAGE_SIZE_IN_BYTES; + let prev_page_count = memory + .grow(Pages(HEAP_SIZE_IN_PAGES)) + .map_err(|_| Error::from(ErrorKind::Runtime))?; Ok(Heap { - end: allocated_area_start as u32, + end: Bytes::from(prev_page_count).0 as u32, }) } fn allocate(&mut self, size: u32) -> u32 { @@ -64,16 +61,16 @@ impl Heap { struct FunctionExecutor<'e, E: Externalities + 'e> { heap: Heap, - memory: Arc, + memory: MemoryRef, ext: &'e mut E, hash_lookup: HashMap, Vec>, } impl<'e, E: Externalities> FunctionExecutor<'e, E> { - fn new(m: &Arc, e: &'e mut E) -> Result { + fn new(m: MemoryRef, e: &'e mut E) -> Result { Ok(FunctionExecutor { - heap: Heap::new(&*m)?, - memory: Arc::clone(m), + heap: Heap::new(&m)?, + memory: m, ext: e, hash_lookup: HashMap::new(), }) @@ -129,50 +126,54 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, println!("{}", message); } } + Ok(()) }, ext_print_hex(data: *const u8, len: u32) => { if let Ok(hex) = this.memory.get(data, len as usize) { println!("{}", HexDisplay::from(&hex)); } + Ok(()) }, ext_print_num(number: u64) => { println!("{}", number); + Ok(()) }, ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => { let sl1 = this.memory.get(s1, n as usize).map_err(|_| DummyUserError)?; let sl2 = this.memory.get(s2, n as usize).map_err(|_| DummyUserError)?; - match sl1.cmp(&sl2) { + Ok(match sl1.cmp(&sl2) { Ordering::Greater => 1, Ordering::Less => -1, Ordering::Equal => 0, - } + }) }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize) .map_err(|_| DummyUserError)?; trace!(target: "runtime-io", "memcpy {} from {}, {} bytes", dest, src, count); - dest + Ok(dest) }, ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { this.memory.copy(src as usize, dest as usize, count as usize) .map_err(|_| DummyUserError)?; trace!(target: "runtime-io", "memmove {} from {}, {} bytes", dest, src, count); - dest + Ok(dest) }, ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { this.memory.clear(dest as usize, val as u8, count as usize) .map_err(|_| DummyUserError)?; trace!(target: "runtime-io", "memset {} with {}, {} bytes", dest, val, count); - dest + Ok(dest) }, ext_malloc(size: usize) -> *mut u8 => { let r = this.heap.allocate(size); trace!(target: "runtime-io", "malloc {} bytes at {}", size, r); - r + Ok(r) }, ext_free(addr: *mut u8) => { this.heap.deallocate(addr); - trace!(target: "runtime-io", "free {}", addr) + trace!(target: "runtime-io", "free {}", addr); + Ok(()) }, ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; @@ -183,6 +184,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, info!(target: "wasm-trace", "*** Setting storage: {} -> {} [k={}]", ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key)); } this.ext.set_storage(key, value); + Ok(()) }, ext_clear_storage(key_data: *const u8, key_len: u32) => { let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; @@ -192,6 +194,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, info!(target: "wasm-trace", "*** Clearing storage: {} [k={}]", ascii_format(&key), HexDisplay::from(&key)); } this.ext.clear_storage(&key); + Ok(()) }, // return 0 and place u32::max_value() into written_out if no value exists for the key. ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { @@ -208,10 +211,10 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, let offset = this.heap.allocate(value.len() as u32) as u32; this.memory.set(offset, &value).map_err(|_| DummyUserError)?; this.memory.write_primitive(written_out, value.len() as u32)?; - offset + Ok(offset) } else { this.memory.write_primitive(written_out, u32::max_value())?; - 0 + Ok(0) } }, // return u32::max_value() if no value exists for the key. @@ -227,14 +230,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, let value = &value[value_offset as usize..]; let written = ::std::cmp::min(value_len as usize, value.len()); this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?; - written as u32 + Ok(written as u32) } else { - u32::max_value() + Ok(u32::max_value()) } }, ext_storage_root(result: *mut u8) => { let r = this.ext.storage_root(); this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?; + Ok(()) }, ext_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => { let values = (0..lens_len) @@ -249,9 +253,10 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, .collect::<::std::result::Result, DummyUserError>>()?; let r = ordered_trie_root(values.into_iter()); this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?; + Ok(()) }, ext_chain_id() -> u64 => { - this.ext.chain_id() + Ok(this.ext.chain_id()) }, ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { let result = if len == 0 { @@ -272,6 +277,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, }; this.memory.set(out, &result).map_err(|_| DummyUserError)?; + Ok(()) }, ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { let result = if len == 0 { @@ -280,6 +286,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, twox_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) }; this.memory.set(out, &result).map_err(|_| DummyUserError)?; + Ok(()) }, ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { let result = if len == 0 { @@ -288,6 +295,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, blake2_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) }; this.memory.set(out, &result).map_err(|_| DummyUserError)?; + Ok(()) }, ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { let mut sig = [0u8; 64]; @@ -296,12 +304,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| DummyUserError)?; let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| DummyUserError)?; - if ::ed25519::verify(&sig, &msg, &pubkey) { + Ok(if ::ed25519::verify(&sig, &msg, &pubkey) { 0 } else { 5 - } - } + }) + }, => <'e, E: Externalities + 'e> ); @@ -324,27 +332,40 @@ impl CodeExecutor for WasmExecutor { // TODO: handle all expects as errors to be returned. println!("Wasm-Calling {}({})", method, HexDisplay::from(&data)); - let program = ProgramInstance::new().expect("this really shouldn't be able to fail; qed"); + let module = Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed"); - let module = deserialize_buffer(code.to_vec()).expect("all modules compiled with rustc are valid wasm code; qed"); - let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("runtime signatures always provided; qed"); + // start module instantiation. Don't run 'start' function yet. + let intermediate_instance = ModuleInstance::new( + &module, + &ImportsBuilder::new() + .with_resolver("env", FunctionExecutor::::resolver()) + )?; - let memory = module.memory(ItemIndex::Internal(0)).expect("all modules compiled with rustc include memory segments; qed"); - let mut fec = FunctionExecutor::new(&memory, ext)?; + // extract a reference to a linear memory and initialize FunctionExecutor. + let memory = intermediate_instance + .not_started_instance() + .export_by_name("memory") + .expect("all modules compiled with rustc should have an export named 'memory'; qed") + .as_memory() + .expect("in module generated by rustc export named 'memory' should be a memory; qed") + .clone(); + let mut fec = FunctionExecutor::new(memory.clone(), ext)?; + + // finish instantiation by running 'start' function (if any). + let instance = intermediate_instance.run_start(&mut fec)?; let size = data.len() as u32; let offset = fec.heap.allocate(size); memory.set(offset, &data).expect("heap always gives a sensible offset to write"); - let returned = program - .params_with_external("env", &mut fec) - .map(|p| p - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32))) - .and_then(|p| module.execute_export(method, p)) - .map_err(|_| -> Error { - ErrorKind::Runtime.into() - })?; + let returned = instance.invoke_export( + method, + &[ + I32(offset as i32), + I32(size as i32) + ], + &mut fec + )?; if let Some(I64(r)) = returned { let offset = r as u32; @@ -365,6 +386,13 @@ mod tests { use codec::Slicable; use state_machine::TestExternalities; + // TODO: move into own crate. + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + #[test] fn returning_should_work() { let mut ext = TestExternalities::default(); diff --git a/substrate/substrate/executor/src/wasm_utils.rs b/substrate/substrate/executor/src/wasm_utils.rs index 0e747b7fe3..1553273728 100644 --- a/substrate/substrate/executor/src/wasm_utils.rs +++ b/substrate/substrate/executor/src/wasm_utils.rs @@ -16,18 +16,18 @@ //! Rust implementation of Substrate contracts. -use std::sync::{Arc}; -use std::collections::HashMap; -pub use std::result; -pub use parity_wasm::builder; -pub use parity_wasm::elements::{ValueType, Module}; -pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor, - UserDefinedElements, env_native_module, DummyUserError, ExecutionParams, UserError}; -use parity_wasm::interpreter; +use wasmi::{ValueType, RuntimeValue, HostError}; +use std::fmt; -pub type Error = interpreter::Error; -pub type MemoryInstance = interpreter::MemoryInstance; -pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>; +#[derive(Debug)] +pub struct DummyUserError; +impl fmt::Display for DummyUserError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DummyUserError") + } +} +impl HostError for DummyUserError { +} pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } } @@ -48,153 +48,151 @@ macro_rules! convert_args { } #[macro_export] -macro_rules! convert_fn { - ( $name:ident ( $( $params:ty ),* ) ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) ); - ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some({ use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE }) ) ); +macro_rules! gen_signature { + ( ( $( $params: ty ),* ) ) => ( + { + $crate::wasmi::Signature::new(&convert_args!($($params),*)[..], None) + } + ); + + ( ( $( $params: ty ),* ) -> $returns: ty ) => ( + { + $crate::wasmi::Signature::new(&convert_args!($($params),*)[..], Some({ + use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE + })) + } + ); +} + +macro_rules! resolve_fn { + (@iter $index:expr, $sig_var:ident, $name_var:ident) => (); + (@iter $index:expr, $sig_var:ident, $name_var:ident $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* => $($tail:tt)* ) => ( + if $name_var == stringify!($name) { + let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* ); + if $sig_var != &signature { + return Err($crate::wasmi::Error::Instantiation( + format!("Export {} has different signature {:?}", $name_var, $sig_var), + )); + } + return Ok($crate::wasmi::FuncInstance::alloc_host(signature, $index)); + } + resolve_fn!(@iter $index + 1, $sig_var, $name_var $($tail)*) + ); + + ($sig_var:ident, $name_var:ident, $($tail:tt)* ) => ( + resolve_fn!(@iter 0, $sig_var, $name_var $($tail)*); + ); } #[macro_export] -macro_rules! reverse_params { - // Entry point, use brackets to recursively reverse above. - ($body:tt, $self:ident, $context:ident, $( $names:ident : $params:ty ),*) => ( - reverse_params!($body $self $context [ $( $names : $params ),* ]); - ); - ($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({ +macro_rules! unmarshall_args { + ( $body:tt, $objectname:ident, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({ $( - let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = match $context.value_stack.pop_as() { - Ok(value) => value, - Err(error) => return Err(error.into()), - }; + let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = + $args_iter.next() + .and_then(|rt_val| rt_val.try_into()) + .expect( + "`$args_iter` comes from an argument of Externals::invoke_index; + args to an external call always matches the signature of the external; + external signatures are built with count and types and in order defined by `$params`; + here, we iterating on `$params`; + qed; + " + ); )* $body - }); - ($body:tt $self:ident $context:ident [ $name:ident : $param:ty $(, $names:ident : $params:ty )* ] $( $reversed_names:ident : $reversed_params:ty ),*) => ( - reverse_params!($body $self $context [ $( $names : $params ),* ] $name : $param $( , $reversed_names : $reversed_params )*); - ); + }) +} + +/// Since we can't specify the type of closure directly at binding site: +/// +/// ```rust,ignore +/// let f: FnOnce() -> Result<::NativeType, _> = || { /* ... */ }; +/// ``` +/// +/// we use this function to constrain the type of the closure. +#[inline(always)] +pub fn constrain_closure(f: F) -> F +where + F: FnOnce() -> Result +{ + f } #[macro_export] macro_rules! marshall { - ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ - reverse_params!($body, $self, $context, $( $names : $params ),*); - Ok(None) + ( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ + let body = $crate::wasm_utils::constrain_closure::< + <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _ + >(|| { + unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) + }); + let r = body()?; + return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) }); - ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let r : <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*); - Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) + ( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ + let body = $crate::wasm_utils::constrain_closure::<(), _>(|| { + unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) + }); + body()?; + return Ok(None) }) } -#[macro_export] -macro_rules! dispatch { - ( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - fn execute(&mut self, name: &str, context: $crate::wasm_utils::CallerContext) - -> $crate::wasm_utils::result::Result, $crate::wasm_utils::Error> { - let $objectname = self; - match name { - $( - stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), - )* - _ => panic!() - } +macro_rules! dispatch_fn { + ( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident) => { + // `$index` comes from an argument of Externals::invoke_index; + // externals are always invoked with index given by resolve_fn! at resolve time; + // For each next function resolve_fn! gives new index, starting from 0; + // Both dispatch_fn! and resolve_fn! are called with the same list of functions; + // qed; + panic!("fn with index {} is undefined", $index); + }; + + ( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident, $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt $($tail:tt)*) => ( + if $index_ident == $index { + { marshall!($args_iter, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body) } } + dispatch_fn!( @iter $index + 1, $index_ident, $objectname, $args_iter $($tail)*) ); -} -#[macro_export] -macro_rules! signatures { - ( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( - const SIGNATURES: &'static [$crate::wasm_utils::UserFunctionDescriptor] = &[ - $( - convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ), - )* - ]; + ( $index_ident:ident, $objectname:ident, $args_iter:ident, $($tail:tt)* ) => ( + dispatch_fn!( @iter 0, $index_ident, $objectname, $args_iter, $($tail)*); ); } -pub trait IntoUserDefinedElements { - fn into_user_defined_elements(&mut self) -> UserDefinedElements; -} - #[macro_export] macro_rules! impl_function_executor { - ( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => ( - impl $($pre)+ $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { - dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); - } - impl $($pre)+ $structname { - signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); - } - impl $($pre)+ $crate::wasm_utils::IntoUserDefinedElements for $structname { - fn into_user_defined_elements(&mut self) -> UserDefinedElements<$crate::wasm_utils::DummyUserError> { - $crate::wasm_utils::UserDefinedElements { - executor: Some(self), - globals: HashMap::new(), // TODO: provide - functions: ::std::borrow::Cow::from(Self::SIGNATURES), + ( $objectname:ident : $structname:ty, + $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt , )* + => $($pre:tt)+ ) => ( + impl $( $pre ) + $structname { + #[allow(unused)] + fn resolver() -> &'static $crate::wasmi::ModuleImportResolver { + struct Resolver; + impl $crate::wasmi::ModuleImportResolver for Resolver { + fn resolve_func(&self, name: &str, signature: &$crate::wasmi::Signature) -> ::std::result::Result<$crate::wasmi::FuncRef, $crate::wasmi::Error> { + resolve_fn!(signature, name, $( $name( $( $params ),* ) $( -> $returns )* => )*); + + Err($crate::wasmi::Error::Instantiation( + format!("Export {} not found", name), + )) + } } + &Resolver + } + } + + impl $( $pre ) + $crate::wasmi::Externals for $structname { + fn invoke_index( + &mut self, + index: usize, + args: $crate::wasmi::RuntimeArgs, + ) -> ::std::result::Result, $crate::wasmi::Trap> { + let $objectname = self; + let mut args = args.as_ref().iter(); + dispatch_fn!(index, $objectname, args, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); } } ); } - -#[derive(Clone)] -struct DummyUserFunctionExecutor; -impl interpreter::UserFunctionExecutor for DummyUserFunctionExecutor { - fn execute(&mut self, _name: &str, _context: interpreter::CallerContext) -> - result::Result, interpreter::Error> - { - unimplemented!() - } -} - -pub trait AddModuleWithoutFullDependentInstance { - fn add_module_by_sigs( - &self, - name: &str, - module: Module, - functions: HashMap<&str, &'static [UserFunctionDescriptor]>, - ) -> result::Result>, interpreter::Error>; - - fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result, Error>; -} - -impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance { - fn add_module_by_sigs( - &self, - name: &str, - module: Module, - functions: HashMap<&str, &'static [UserFunctionDescriptor]> - ) -> result::Result>, interpreter::Error> { - let mut dufe = vec![DummyUserFunctionExecutor; functions.len()]; - let dufe_refs = dufe.iter_mut().collect::>(); - let fake_module_map = functions.into_iter() - .zip(dufe_refs.into_iter()) - .map(|((dep_mod_name, functions), dufe)| -> result::Result<_, interpreter::Error> { - let fake_module = Arc::new( - interpreter::env_native_module( - self.module(dep_mod_name).ok_or(DummyUserError)?, UserDefinedElements { - executor: Some(dufe), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(functions), - } - )? - ); - let fake_module: Arc> = fake_module; - Ok((dep_mod_name.into(), fake_module)) - }) - .collect::, interpreter::Error>>()?; - self.add_module(name, module, Some(&fake_module_map)) - } - - fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result, Error> { - Ok(interpreter::ExecutionParams::with_external( - externals_name.into(), - Arc::new( - interpreter::env_native_module( - self.module(externals_name).ok_or(DummyUserError)?, - externals.into_user_defined_elements() - )? - ) - )) - } -}