Migrate from parity-wasmi to wasmi. (#103)

This commit is contained in:
Sergey Pepyakin
2018-04-04 22:39:41 +03:00
committed by Gav Wood
parent bd066e27a6
commit b0d8eeb526
6 changed files with 239 additions and 199 deletions
+21 -4
View File
@@ -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"
+1 -1
View File
@@ -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"
@@ -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 {
+1 -7
View File
@@ -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;
@@ -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<Self> {
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<Self> {
const HEAP_SIZE_IN_PAGES: usize = 8;
let prev_page_count = memory.grow(HEAP_SIZE_IN_PAGES).map_err(
|_: ::parity_wasm::interpreter::Error<DummyUserError>| 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<MemoryInstance>,
memory: MemoryRef,
ext: &'e mut E,
hash_lookup: HashMap<Vec<u8>, Vec<u8>>,
}
impl<'e, E: Externalities> FunctionExecutor<'e, E> {
fn new(m: &Arc<MemoryInstance>, e: &'e mut E) -> Result<Self> {
fn new(m: MemoryRef, e: &'e mut E) -> Result<Self> {
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<Vec<_>, 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::<E>::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::<E>::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();
+132 -134
View File
@@ -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<DummyUserError>;
pub type MemoryInstance = interpreter::MemoryInstance<DummyUserError>;
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<<u32 as ConvertibleToWasm>::NativeType, _> = || { /* ... */ };
/// ```
///
/// we use this function to constrain the type of the closure.
#[inline(always)]
pub fn constrain_closure<R, F>(f: F) -> F
where
F: FnOnce() -> Result<R, ::wasmi::Trap>
{
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<Option<$crate::wasm_utils::RuntimeValue>, $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<DummyUserError>;
}
#[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<Option<$crate::wasmi::RuntimeValue>, $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<E: UserError> interpreter::UserFunctionExecutor<E> for DummyUserFunctionExecutor {
fn execute(&mut self, _name: &str, _context: interpreter::CallerContext<E>) ->
result::Result<Option<interpreter::RuntimeValue>, interpreter::Error<E>>
{
unimplemented!()
}
}
pub trait AddModuleWithoutFullDependentInstance {
fn add_module_by_sigs(
&self,
name: &str,
module: Module,
functions: HashMap<&str, &'static [UserFunctionDescriptor]>,
) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>>;
fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result<ExecutionParams<'a, DummyUserError>, Error>;
}
impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance<DummyUserError> {
fn add_module_by_sigs(
&self,
name: &str,
module: Module,
functions: HashMap<&str, &'static [UserFunctionDescriptor]>
) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>> {
let mut dufe = vec![DummyUserFunctionExecutor; functions.len()];
let dufe_refs = dufe.iter_mut().collect::<Vec<_>>();
let fake_module_map = functions.into_iter()
.zip(dufe_refs.into_iter())
.map(|((dep_mod_name, functions), dufe)| -> result::Result<_, interpreter::Error<DummyUserError>> {
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<interpreter::ModuleInstanceInterface<_>> = fake_module;
Ok((dep_mod_name.into(), fake_module))
})
.collect::<result::Result<HashMap<_, _>, interpreter::Error<DummyUserError>>>()?;
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<ExecutionParams<'a, DummyUserError>, 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()
)?
)
))
}
}