From 2fe6e6f03971e2ade7df97d01306173205863cdd Mon Sep 17 00:00:00 2001 From: losfair Date: Sat, 15 Feb 2020 01:31:33 +0800 Subject: [PATCH 01/79] Global trampoline buffer. --- lib/runtime-core/src/trampoline_x64.rs | 51 ++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 3d07484c7..c549498f3 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -9,6 +9,8 @@ use crate::loader::CodeMemory; use crate::vm::Ctx; use std::fmt; +use std::ptr::NonNull; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::{mem, slice}; lazy_static! { @@ -29,6 +31,50 @@ lazy_static! { mem::transmute(ptr) } }; + + static ref TRAMPOLINES: TrampBuffer = TrampBuffer::new(); +} + +struct TrampBuffer { + buffer: CodeMemory, + len: AtomicUsize, +} + +impl TrampBuffer { + /// Creates a trampoline buffer. + fn new() -> TrampBuffer { + // Pre-allocate 64 MiB of virtual memory for code. + let mem = CodeMemory::new(64 * 1048576); + mem.make_writable_executable(); + TrampBuffer { + buffer: mem, + len: AtomicUsize::new(0), + } + } + + /// Bump allocation. Copies `buf` to the end of this code memory. + /// + /// FIXME: Proper storage recycling. + fn append(&self, buf: &[u8]) -> Option> { + let begin = self.len.fetch_add(buf.len(), Ordering::SeqCst); + let end = begin + buf.len(); + + // Length overflowed. Revert and return None. + if end > self.buffer.len() { + self.len.fetch_sub(buf.len(), Ordering::SeqCst); + return None; + } + + // Now we have unique ownership to `self.buffer[begin..end]`. + let slice = unsafe { + std::slice::from_raw_parts_mut( + self.buffer.get_backing_ptr().offset(begin as _), + buf.len(), + ) + }; + slice.copy_from_slice(buf); + Some(NonNull::new(slice.as_mut_ptr()).unwrap()) + } } /// An opaque type for pointers to a callable memory location. @@ -219,6 +265,11 @@ impl TrampolineBufferBuilder { idx } + /// Appends to the global trampoline buffer. + pub fn append_global(self) -> Option> { + TRAMPOLINES.append(&self.code) + } + /// Consumes the builder and builds the trampoline buffer. pub fn build(self) -> TrampolineBuffer { get_context(); // ensure lazy initialization is completed From 12373bb87289f8e68b14bd2c2bbc8887bea922e9 Mon Sep 17 00:00:00 2001 From: losfair Date: Sat, 15 Feb 2020 01:31:49 +0800 Subject: [PATCH 02/79] Func::new_polymorphic --- lib/runtime-core-tests/tests/imports.rs | 31 ++++++++++- lib/runtime-core/src/loader.rs | 20 ++++++- lib/runtime-core/src/typed_func.rs | 74 ++++++++++++++++++++++++- lib/runtime-core/src/vm.rs | 4 +- 4 files changed, 122 insertions(+), 7 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index b461ad2b7..42a09d844 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -1,6 +1,13 @@ +use std::sync::Arc; use wasmer_runtime_core::{ - compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func, - types::MemoryDescriptor, units::Pages, vm, Instance, + compile_with, + error::RuntimeError, + imports, + memory::Memory, + typed_func::Func, + types::{FuncSig, MemoryDescriptor, Type, Value}, + units::Pages, + vm, Instance, }; use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; @@ -68,6 +75,7 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { (import "env" "memory" (memory 1 1)) (import "env" "callback_fn" (func $callback_fn (type $type))) (import "env" "callback_closure" (func $callback_closure (type $type))) + (import "env" "callback_closure_polymorphic" (func $callback_closure_polymorphic (type $type))) (import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type))) (import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type))) (import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type))) @@ -86,6 +94,10 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { get_local 0 call $callback_closure) + (func (export "function_closure_polymorphic") (type $type) + get_local 0 + call $callback_closure_polymorphic) + (func (export "function_closure_with_env") (type $type) get_local 0 call $callback_closure_with_env) @@ -142,6 +154,16 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { Ok(n + 1) }), + "callback_closure_polymorphic" => Func::::new_polymorphic( + Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])), + |_, params| -> Vec { + match params[0] { + Value::I32(x) => vec![Value::I32(x + 1)], + _ => unreachable!() + } + } + ), + // Closure with a captured environment (a single variable + an instance of `Memory`). "callback_closure_with_env" => Func::new(move |n: i32| -> Result { let shift_ = shift + memory.view::()[0].get(); @@ -236,6 +258,11 @@ macro_rules! test { test!(test_fn, function_fn, Ok(2)); test!(test_closure, function_closure, Ok(2)); +test!( + test_closure_polymorphic, + function_closure_polymorphic, + Ok(2) +); test!( test_closure_with_env, function_closure_with_env, diff --git a/lib/runtime-core/src/loader.rs b/lib/runtime-core/src/loader.rs index f516643d0..ea1ca0130 100644 --- a/lib/runtime-core/src/loader.rs +++ b/lib/runtime-core/src/loader.rs @@ -1,7 +1,9 @@ //! The loader module functions are used to load an instance. use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx}; #[cfg(unix)] -use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE}; +use libc::{ + mmap, mprotect, munmap, MAP_ANON, MAP_NORESERVE, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE, +}; use std::{ fmt::Debug, ops::{Deref, DerefMut}, @@ -169,7 +171,7 @@ impl CodeMemory { std::ptr::null_mut(), size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0, ) @@ -196,6 +198,20 @@ impl CodeMemory { panic!("cannot set code memory to writable"); } } + + /// Makes this code memory both writable and executable. + /// + /// Avoid using this if a combination `make_executable` and `make_writable` can be used. + pub fn make_writable_executable(&self) { + if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE | PROT_EXEC) } != 0 { + panic!("cannot set code memory to writable and executable"); + } + } + + /// Returns the backing pointer of this code memory. + pub fn get_backing_ptr(&self) -> *mut u8 { + self.ptr + } } #[cfg(unix)] diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 513ede7fb..2edcf6644 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -4,7 +4,7 @@ use crate::{ error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, - types::{FuncSig, NativeWasmType, Type, WasmExternType}, + types::{FuncSig, NativeWasmType, Type, Value, WasmExternType}, vm, }; use std::{ @@ -240,6 +240,78 @@ where _phantom: PhantomData, } } + + /// Creates a polymorphic function. + #[allow(unused_variables)] + #[cfg(all(unix, target_arch = "x86_64"))] + pub fn new_polymorphic(signature: Arc, func: F) -> Func<'a, Args, Rets, Host> + where + F: Fn(&mut vm::Ctx, &[Value]) -> Vec + 'static, + { + use crate::trampoline_x64::*; + use std::convert::TryFrom; + + struct PolymorphicContext { + arg_types: Vec, + func: Box Vec>, + } + unsafe extern "C" fn enter_host_polymorphic( + ctx: *const CallContext, + args: *const u64, + ) -> u64 { + let ctx = &*(ctx as *const PolymorphicContext); + let vmctx = &mut *(*args.offset(0) as *mut vm::Ctx); + let args: Vec = ctx + .arg_types + .iter() + .enumerate() + .map(|(i, t)| { + let i = i + 1; // skip vmctx + match *t { + Type::I32 => Value::I32(*args.offset(i as _) as i32), + Type::I64 => Value::I64(*args.offset(i as _) as i64), + Type::F32 => Value::F32(f32::from_bits(*args.offset(i as _) as u32)), + Type::F64 => Value::F64(f64::from_bits(*args.offset(i as _) as u64)), + Type::V128 => { + panic!("enter_host_polymorphic: 128-bit types are not supported") + } + } + }) + .collect(); + let rets = (ctx.func)(vmctx, &args); + if rets.len() == 0 { + 0 + } else if rets.len() == 1 { + u64::try_from(rets[0].to_u128()).expect( + "128-bit return value from polymorphic host functions is not yet supported", + ) + } else { + panic!( + "multiple return values from polymorphic host functions is not yet supported" + ); + } + } + let mut builder = TrampolineBufferBuilder::new(); + let ctx = Box::new(PolymorphicContext { + arg_types: signature.params().to_vec(), + func: Box::new(func), + }); + builder.add_callinfo_trampoline( + enter_host_polymorphic, + Box::into_raw(ctx) as *const _, + (signature.params().len() + 1) as u32, // +vmctx + ); + let ptr = builder + .append_global() + .expect("cannot bump-allocate global trampoline memory"); + Func { + inner: Host(()), + func: ptr.cast::(), + func_env: None, + vmctx: ptr::null_mut(), + _phantom: PhantomData, + } + } } impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner> diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 2a39bdec1..a25ee24ff 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -545,13 +545,13 @@ impl Ctx { /// `typed_func` module within the `wrap` functions, to wrap imported /// functions. #[repr(transparent)] -pub struct Func(pub(self) *mut c_void); +pub struct Func(*mut c_void); /// Represents a function environment pointer, like a captured /// environment of a closure. It is mostly used in the `typed_func` /// module within the `wrap` functions, to wrap imported functions. #[repr(transparent)] -pub struct FuncEnv(pub(self) *mut c_void); +pub struct FuncEnv(*mut c_void); /// Represents a function context. It is used by imported functions /// only. From 5f4561e5efdee87a3a26e520fcde77c8231cbe04 Mon Sep 17 00:00:00 2001 From: losfair Date: Sun, 16 Feb 2020 00:28:43 +0800 Subject: [PATCH 03/79] Fix compilation error on Aarch64. --- lib/runtime-core/src/typed_func.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 2edcf6644..84fbe8add 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -4,7 +4,7 @@ use crate::{ error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, - types::{FuncSig, NativeWasmType, Type, Value, WasmExternType}, + types::{FuncSig, NativeWasmType, Type, WasmExternType}, vm, }; use std::{ @@ -246,9 +246,10 @@ where #[cfg(all(unix, target_arch = "x86_64"))] pub fn new_polymorphic(signature: Arc, func: F) -> Func<'a, Args, Rets, Host> where - F: Fn(&mut vm::Ctx, &[Value]) -> Vec + 'static, + F: Fn(&mut vm::Ctx, &[crate::types::Value]) -> Vec + 'static, { use crate::trampoline_x64::*; + use crate::types::Value; use std::convert::TryFrom; struct PolymorphicContext { From ad20a008e095a471d85a3dccebfa5b7d9ecae4af Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 17 Feb 2020 15:30:25 +0100 Subject: [PATCH 04/79] fix(runtime-core) Use explicit `dyn` for trait objects. --- lib/runtime-core/src/typed_func.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 84fbe8add..eca73f61b 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -254,7 +254,7 @@ where struct PolymorphicContext { arg_types: Vec, - func: Box Vec>, + func: Box Vec>, } unsafe extern "C" fn enter_host_polymorphic( ctx: *const CallContext, From 2ee1e80f3b15f1c266bdfeeb828e5c1aed2919a3 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 17 Feb 2020 15:43:14 +0100 Subject: [PATCH 05/79] feat(runtime-core) Allow dynamic signature for polymorphic host functions. This patch adds a new field in `Func`: `signature`. It contains the signature of the host function. For non-polymorphic host functions, the signature is computed from the `Args` and `Rets` implementation parameters at compile-time. For polymorphic host functions though, to be fully dynamic, the signature given to `new_polymorphic` is used in `Func` as the correct signature. --- lib/runtime-core-tests/tests/imports.rs | 2 +- lib/runtime-core/src/typed_func.rs | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index 42a09d844..ce3cea2c4 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -154,7 +154,7 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { Ok(n + 1) }), - "callback_closure_polymorphic" => Func::::new_polymorphic( + "callback_closure_polymorphic" => Func::new_polymorphic( Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])), |_, params| -> Vec { match params[0] { diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index eca73f61b..4abad5a17 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -189,9 +189,21 @@ where /// Represents a function that can be used by WebAssembly. pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { inner: Inner, + + /// The function pointer. func: NonNull, + + /// The function environment. func_env: Option>, + + /// The famous `vm::Ctx`. vmctx: *mut vm::Ctx, + + /// The signature is usually infered from `Args` and `Rets`. In + /// case of polymorphic function, the signature is only known at + /// runtime. + signature: Arc, + _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -214,6 +226,7 @@ where func, func_env, vmctx, + signature: Arc::new(FuncSig::new(Args::types(), Rets::types())), _phantom: PhantomData, } } @@ -225,7 +238,7 @@ where Rets: WasmTypeList, { /// Creates a new `Func`. - pub fn new(func: F) -> Func<'a, Args, Rets, Host> + pub fn new(func: F) -> Self where Kind: ExternalFunctionKind, F: ExternalFunction, @@ -237,14 +250,17 @@ where func, func_env, vmctx: ptr::null_mut(), + signature: Arc::new(FuncSig::new(Args::types(), Rets::types())), _phantom: PhantomData, } } +} +impl<'a> Func<'a, (), (), Host> { /// Creates a polymorphic function. #[allow(unused_variables)] #[cfg(all(unix, target_arch = "x86_64"))] - pub fn new_polymorphic(signature: Arc, func: F) -> Func<'a, Args, Rets, Host> + pub fn new_polymorphic(signature: Arc, func: F) -> Self where F: Fn(&mut vm::Ctx, &[crate::types::Value]) -> Vec + 'static, { @@ -305,11 +321,13 @@ where let ptr = builder .append_global() .expect("cannot bump-allocate global trampoline memory"); + Func { inner: Host(()), func: ptr.cast::(), func_env: None, vmctx: ptr::null_mut(), + signature, _phantom: PhantomData, } } @@ -751,12 +769,11 @@ where func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env), None => Context::Internal, }; - let signature = Arc::new(FuncSig::new(Args::types(), Rets::types())); Export::Function { func, ctx, - signature, + signature: self.signature.clone(), } } } From b67acbc0e3828a8b99ebf956839f1731dd7b8af0 Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 25 Feb 2020 01:19:19 +0800 Subject: [PATCH 06/79] Add `ErasedFunc` for type-erased functions. --- lib/runtime-core-tests/tests/imports.rs | 4 +- lib/runtime-core/src/typed_func.rs | 75 +++++++++++++++++++++---- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index ce3cea2c4..17c82097a 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -4,7 +4,7 @@ use wasmer_runtime_core::{ error::RuntimeError, imports, memory::Memory, - typed_func::Func, + typed_func::{ErasedFunc, Func}, types::{FuncSig, MemoryDescriptor, Type, Value}, units::Pages, vm, Instance, @@ -154,7 +154,7 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { Ok(n + 1) }), - "callback_closure_polymorphic" => Func::new_polymorphic( + "callback_closure_polymorphic" => ErasedFunc::new_polymorphic( Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])), |_, params| -> Vec { match params[0] { diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 39c8b45e9..6a3558f6d 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -190,6 +190,32 @@ where } } +/// Represents a type-erased function provided by either the host or the WebAssembly program. +#[allow(dead_code)] +pub struct ErasedFunc<'a> { + inner: Box, + + /// The function pointer. + func: NonNull, + + /// The function environment. + func_env: Option>, + + /// The famous `vm::Ctx`. + vmctx: *mut vm::Ctx, + + /// The runtime signature of this function. + /// + /// When converted from a `Func`, this is determined by the static `Args` and `Rets` type parameters. + /// otherwise the signature is dynamically assigned during `ErasedFunc` creation, usually when creating + /// a polymorphic host function. + signature: Arc, + + _phantom: PhantomData<&'a ()>, +} + +unsafe impl<'a> Send for ErasedFunc<'a> {} + /// Represents a function that can be used by WebAssembly. pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { inner: Inner, @@ -203,17 +229,30 @@ pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { /// The famous `vm::Ctx`. vmctx: *mut vm::Ctx, - /// The signature is usually infered from `Args` and `Rets`. In - /// case of polymorphic function, the signature is only known at - /// runtime. - signature: Arc, - _phantom: PhantomData<(&'a (), Args, Rets)>, } unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Wasm> {} unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Host> {} +impl<'a, Args, Rets, Inner> From> for ErasedFunc<'a> +where + Args: WasmTypeList, + Rets: WasmTypeList, + Inner: Kind + 'static, +{ + fn from(that: Func<'a, Args, Rets, Inner>) -> ErasedFunc<'a> { + ErasedFunc { + inner: Box::new(that.inner), + func: that.func, + func_env: that.func_env, + vmctx: that.vmctx, + signature: Arc::new(FuncSig::new(Args::types(), Rets::types())), + _phantom: PhantomData, + } + } +} + impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm> where Args: WasmTypeList, @@ -230,7 +269,6 @@ where func, func_env, vmctx, - signature: Arc::new(FuncSig::new(Args::types(), Rets::types())), _phantom: PhantomData, } } @@ -254,13 +292,12 @@ where func, func_env, vmctx: ptr::null_mut(), - signature: Arc::new(FuncSig::new(Args::types(), Rets::types())), _phantom: PhantomData, } } } -impl<'a> Func<'a, (), (), Host> { +impl<'a> ErasedFunc<'a> { /// Creates a polymorphic function. #[allow(unused_variables)] #[cfg(all(unix, target_arch = "x86_64"))] @@ -326,8 +363,8 @@ impl<'a> Func<'a, (), (), Host> { .append_global() .expect("cannot bump-allocate global trampoline memory"); - Func { - inner: Host(()), + ErasedFunc { + inner: Box::new(Host(())), func: ptr.cast::(), func_env: None, vmctx: ptr::null_mut(), @@ -765,6 +802,22 @@ impl_traits!([C] S24, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T impl_traits!([C] S25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y); impl_traits!([C] S26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); +impl<'a> IsExport for ErasedFunc<'a> { + fn to_export(&self) -> Export { + let func = unsafe { FuncPointer::new(self.func.as_ptr()) }; + let ctx = match self.func_env { + func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env), + None => Context::Internal, + }; + + Export::Function { + func, + ctx, + signature: self.signature.clone(), + } + } +} + impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner> where Args: WasmTypeList, @@ -781,7 +834,7 @@ where Export::Function { func, ctx, - signature: self.signature.clone(), + signature: Arc::new(FuncSig::new(Args::types(), Rets::types())), } } } From b7c9c1843a6c939092ecafbc351b3faf5447e45c Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 01:44:50 +0800 Subject: [PATCH 07/79] Add dynamic executable memory allocation & tests to trampolines. --- lib/runtime-core/src/trampoline_x64.rs | 183 +++++++++++++++++++++---- 1 file changed, 160 insertions(+), 23 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index c549498f3..b15f431b5 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -8,9 +8,10 @@ use crate::loader::CodeMemory; use crate::vm::Ctx; +use std::collections::BTreeMap; use std::fmt; use std::ptr::NonNull; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; use std::{mem, slice}; lazy_static! { @@ -32,43 +33,88 @@ lazy_static! { } }; - static ref TRAMPOLINES: TrampBuffer = TrampBuffer::new(); + static ref TRAMPOLINES: TrampBuffer = TrampBuffer::new(64 * 1048576); } -struct TrampBuffer { +/// The global trampoline buffer. +pub(self) struct TrampBuffer { + /// A fixed-(virtual)-size executable+writable buffer for storing trampolines. buffer: CodeMemory, - len: AtomicUsize, + + /// Allocation state. + alloc: Mutex, +} + +/// The allocation state of a `TrampBuffer`. +struct AllocState { + /// Records all allocated blocks in `buffer`. + blocks: BTreeMap, } impl TrampBuffer { - /// Creates a trampoline buffer. - fn new() -> TrampBuffer { + /// Creates a trampoline buffer with a given (virtual) size. + pub(self) fn new(size: usize) -> TrampBuffer { // Pre-allocate 64 MiB of virtual memory for code. - let mem = CodeMemory::new(64 * 1048576); + let mem = CodeMemory::new(size); mem.make_writable_executable(); TrampBuffer { buffer: mem, - len: AtomicUsize::new(0), + alloc: Mutex::new(AllocState { + blocks: BTreeMap::new(), + }), } } - /// Bump allocation. Copies `buf` to the end of this code memory. - /// - /// FIXME: Proper storage recycling. - fn append(&self, buf: &[u8]) -> Option> { - let begin = self.len.fetch_add(buf.len(), Ordering::SeqCst); - let end = begin + buf.len(); + /// Removes a previously-`insert`ed trampoline. + pub(self) unsafe fn remove(&self, start: NonNull) { + let start = start.as_ptr() as usize - self.buffer.get_backing_ptr() as usize; + let mut alloc = self.alloc.lock().unwrap(); + alloc + .blocks + .remove(&start) + .expect("TrampBuffer::remove(): Attempting to remove a non-existent allocation."); + } - // Length overflowed. Revert and return None. - if end > self.buffer.len() { - self.len.fetch_sub(buf.len(), Ordering::SeqCst); - return None; + /// Allocates a region of executable memory and copies `buf` to the end of this region. + /// + /// Returns `None` if no memory is available. + pub(self) fn insert(&self, buf: &[u8]) -> Option> { + // First, assume an available start position... + let mut assumed_start: usize = 0; + + let mut alloc = self.alloc.lock().unwrap(); + let mut found = false; + + // Then, try invalidating that assumption... + for (&start, &end) in &alloc.blocks { + if start - assumed_start < buf.len() { + // Unavailable. Move to next free block. + assumed_start = end; + } else { + // This free block can be used. + found = true; + break; + } } - // Now we have unique ownership to `self.buffer[begin..end]`. + if !found { + // No previous free blocks were found. Try allocating at the end. + if self.buffer.len() - assumed_start < buf.len() { + // No more free space. Cannot allocate. + return None; + } else { + // Extend towards the end. + } + } + + // Now we know `assumed_start` is valid. + let start = assumed_start; + alloc.blocks.insert(start, start + buf.len()); + + // We have unique ownership to `self.buffer[start..start + buf.len()]`. let slice = unsafe { std::slice::from_raw_parts_mut( - self.buffer.get_backing_ptr().offset(begin as _), + self.buffer.get_backing_ptr().offset(start as _), buf.len(), ) }; @@ -265,9 +311,19 @@ impl TrampolineBufferBuilder { idx } - /// Appends to the global trampoline buffer. - pub fn append_global(self) -> Option> { - TRAMPOLINES.append(&self.code) + /// Inserts to the global trampoline buffer. + pub fn insert_global(self) -> Option> { + TRAMPOLINES.insert(&self.code) + } + + /// Removes from the global trampoline buffer. + pub unsafe fn remove_global(ptr: NonNull) { + TRAMPOLINES.remove(ptr); + } + + /// Gets the current (non-executable) code in this builder. + pub fn code(&self) -> &[u8] { + &self.code } /// Consumes the builder and builds the trampoline buffer. @@ -343,4 +399,85 @@ mod tests { }; assert_eq!(ret, 136); } + + #[test] + fn test_many_global_trampolines() { + unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 { + let n = n as usize; + let mut result: u64 = 0; + for i in 0..n { + result += *args.offset(i as _); + } + result + } + + // Use the smallest possible buffer size (page size) to check memory releasing logic. + let buffer = TrampBuffer::new(4096); + + // Validate the previous trampoline instead of the current one to ensure that no overwrite happened. + let mut prev: Option<(NonNull, u64)> = None; + + for i in 0..5000usize { + let mut builder = TrampolineBufferBuilder::new(); + let n = i % 8; + builder.add_callinfo_trampoline(inner, n as _, n as _); + let ptr = buffer + .insert(builder.code()) + .expect("cannot insert new code into global buffer"); + + if let Some((ptr, expected)) = prev.take() { + use std::mem::transmute; + + // Test different argument counts. + unsafe { + match expected { + 0 => { + let f = transmute::<_, extern "C" fn() -> u64>(ptr); + assert_eq!(f(), 0); + } + 1 => { + let f = transmute::<_, extern "C" fn(u64) -> u64>(ptr); + assert_eq!(f(1), 1); + } + 3 => { + let f = transmute::<_, extern "C" fn(u64, u64) -> u64>(ptr); + assert_eq!(f(1, 2), 3); + } + 6 => { + let f = transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(ptr); + assert_eq!(f(1, 2, 3), 6); + } + 10 => { + let f = transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(ptr); + assert_eq!(f(1, 2, 3, 4), 10); + } + 15 => { + let f = + transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(ptr); + assert_eq!(f(1, 2, 3, 4, 5), 15); + } + 21 => { + let f = transmute::< + _, + extern "C" fn(u64, u64, u64, u64, u64, u64) -> u64, + >(ptr); + assert_eq!(f(1, 2, 3, 4, 5, 6), 21); + } + 28 => { + let f = transmute::< + _, + extern "C" fn(u64, u64, u64, u64, u64, u64, u64) -> u64, + >(ptr); + assert_eq!(f(1, 2, 3, 4, 5, 6, 7), 28); + } + _ => unreachable!(), + } + buffer.remove(ptr); + } + } + + let expected = (0..=n as u64).fold(0u64, |a, b| a + b); + prev = Some((ptr, expected)) + } + } } From 80f824e7080ca6339de9ec5c391a17a00d85e86d Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 01:45:11 +0800 Subject: [PATCH 08/79] Auto-release trampolines. --- lib/runtime-core/src/typed_func.rs | 32 ++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 6a3558f6d..152302bf4 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -360,11 +360,25 @@ impl<'a> ErasedFunc<'a> { (signature.params().len() + 1) as u32, // +vmctx ); let ptr = builder - .append_global() + .insert_global() .expect("cannot bump-allocate global trampoline memory"); + struct AutoRelease { + ptr: NonNull, + } + + impl Drop for AutoRelease { + fn drop(&mut self) { + unsafe { + TrampolineBufferBuilder::remove_global(self.ptr); + } + } + } + + impl Kind for AutoRelease {} + ErasedFunc { - inner: Box::new(Host(())), + inner: Box::new(AutoRelease { ptr }), func: ptr.cast::(), func_env: None, vmctx: ptr::null_mut(), @@ -941,4 +955,18 @@ mod tests { }, }; } + + #[test] + fn test_many_new_polymorphics() { + use crate::types::{FuncSig, Type}; + + // Check that generating a lot (1M) of polymorphic functions doesn't use up the executable buffer. + for _ in 0..1000000 { + let arglist = vec![Type::I32; 100]; + ErasedFunc::new_polymorphic( + Arc::new(FuncSig::new(arglist, vec![Type::I32])), + |_, _| unreachable!(), + ); + } + } } From 96d9e399139ee49ce5a28d42c2bfae661babe069 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 13:40:02 +0800 Subject: [PATCH 09/79] Specify imports instead of using a `*`. --- Cargo.lock | 21 +++++++++++++++------ lib/runtime-core/src/typed_func.rs | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 82c04e1fb..a8faaeaf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,7 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "log", - "smallvec", + "smallvec 1.2.0", "target-lexicon", "thiserror", ] @@ -926,7 +926,7 @@ dependencies = [ "cloudabi", "libc", "redox_syscall", - "smallvec", + "smallvec 1.2.0", "winapi", ] @@ -1414,6 +1414,15 @@ dependencies = [ "serde", ] +[[package]] +name = "smallvec" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +dependencies = [ + "maybe-uninit", +] + [[package]] name = "smallvec" version = "1.2.0" @@ -1764,7 +1773,7 @@ checksum = "6d2e13201ef9ef527ad30a6bf1b08e3e024a40cf2731f393d80375dc88506207" dependencies = [ "cranelift-codegen", "log", - "smallvec", + "smallvec 1.2.0", "target-lexicon", ] @@ -1846,7 +1855,7 @@ dependencies = [ "regex", "rustc_version", "semver", - "smallvec", + "smallvec 0.6.13", "wabt", "wasmer-runtime-core", "wasmparser", @@ -1933,7 +1942,7 @@ dependencies = [ "serde-bench", "serde_bytes", "serde_derive", - "smallvec", + "smallvec 0.6.13", "wasmparser", "winapi", ] @@ -1962,7 +1971,7 @@ dependencies = [ "nix", "serde", "serde_derive", - "smallvec", + "smallvec 0.6.13", "wasmer-runtime-core", ] diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 152302bf4..d8c36188f 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -305,7 +305,7 @@ impl<'a> ErasedFunc<'a> { where F: Fn(&mut vm::Ctx, &[crate::types::Value]) -> Vec + 'static, { - use crate::trampoline_x64::*; + use crate::trampoline_x64::{CallContext, TrampolineBufferBuilder}; use crate::types::Value; use std::convert::TryFrom; From a0ea1af71ff2c8eeeefd4786b51b57e65d5abf3c Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 16:54:33 +0800 Subject: [PATCH 10/79] Remove pub(self). --- lib/runtime-core/src/trampoline_x64.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index b15f431b5..146068612 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -37,7 +37,7 @@ lazy_static! { } /// The global trampoline buffer. -pub(self) struct TrampBuffer { +struct TrampBuffer { /// A fixed-(virtual)-size executable+writable buffer for storing trampolines. buffer: CodeMemory, @@ -53,7 +53,7 @@ struct AllocState { impl TrampBuffer { /// Creates a trampoline buffer with a given (virtual) size. - pub(self) fn new(size: usize) -> TrampBuffer { + fn new(size: usize) -> TrampBuffer { // Pre-allocate 64 MiB of virtual memory for code. let mem = CodeMemory::new(size); mem.make_writable_executable(); @@ -66,7 +66,7 @@ impl TrampBuffer { } /// Removes a previously-`insert`ed trampoline. - pub(self) unsafe fn remove(&self, start: NonNull) { + unsafe fn remove(&self, start: NonNull) { let start = start.as_ptr() as usize - self.buffer.get_backing_ptr() as usize; let mut alloc = self.alloc.lock().unwrap(); alloc @@ -78,7 +78,7 @@ impl TrampBuffer { /// Allocates a region of executable memory and copies `buf` to the end of this region. /// /// Returns `None` if no memory is available. - pub(self) fn insert(&self, buf: &[u8]) -> Option> { + fn insert(&self, buf: &[u8]) -> Option> { // First, assume an available start position... let mut assumed_start: usize = 0; From 262d431b49f1176a035e8ffdc718f0bd4904c58b Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 16:59:31 +0800 Subject: [PATCH 11/79] Remove unneeded allow(dead_code). --- lib/runtime-core/src/typed_func.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index d8c36188f..50780955d 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -191,9 +191,8 @@ where } /// Represents a type-erased function provided by either the host or the WebAssembly program. -#[allow(dead_code)] pub struct ErasedFunc<'a> { - inner: Box, + _inner: Box, /// The function pointer. func: NonNull, @@ -243,7 +242,7 @@ where { fn from(that: Func<'a, Args, Rets, Inner>) -> ErasedFunc<'a> { ErasedFunc { - inner: Box::new(that.inner), + _inner: Box::new(that.inner), func: that.func, func_env: that.func_env, vmctx: that.vmctx, @@ -378,7 +377,7 @@ impl<'a> ErasedFunc<'a> { impl Kind for AutoRelease {} ErasedFunc { - inner: Box::new(AutoRelease { ptr }), + _inner: Box::new(AutoRelease { ptr }), func: ptr.cast::(), func_env: None, vmctx: ptr::null_mut(), From 292e42addcb946ec7ca0da6987cf21f5801a36e4 Mon Sep 17 00:00:00 2001 From: Heyang Zhou Date: Wed, 26 Feb 2020 17:01:16 +0800 Subject: [PATCH 12/79] Update lib/runtime-core/src/typed_func.rs Co-Authored-By: Ivan Enderlin --- lib/runtime-core/src/typed_func.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 50780955d..ec1f580b4 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -330,7 +330,7 @@ impl<'a> ErasedFunc<'a> { Type::F32 => Value::F32(f32::from_bits(*args.offset(i as _) as u32)), Type::F64 => Value::F64(f64::from_bits(*args.offset(i as _) as u64)), Type::V128 => { - panic!("enter_host_polymorphic: 128-bit types are not supported") + todo!("enter_host_polymorphic: 128-bit types are not supported") } } }) From a438a644b657e4c9bfc8fd5113f79788d9787cde Mon Sep 17 00:00:00 2001 From: Heyang Zhou Date: Wed, 26 Feb 2020 17:01:36 +0800 Subject: [PATCH 13/79] fold() -> sum() Co-Authored-By: Ivan Enderlin --- lib/runtime-core/src/trampoline_x64.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 146068612..94bffb3cf 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -476,7 +476,7 @@ mod tests { } } - let expected = (0..=n as u64).fold(0u64, |a, b| a + b); + let expected = (0..=n as u64).sum(); prev = Some((ptr, expected)) } } From b0877b26e5776079681135a444bc1a0a8a9f3cb6 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 17:07:56 +0800 Subject: [PATCH 14/79] Add safety notice for `TrampolineBufferBuilder::remove_global`. --- lib/runtime-core/src/trampoline_x64.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 146068612..ccff1cae1 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -66,6 +66,8 @@ impl TrampBuffer { } /// Removes a previously-`insert`ed trampoline. + /// + /// For safety, refer to the public interface `TrampolineBufferBuilder::remove_global`. unsafe fn remove(&self, start: NonNull) { let start = start.as_ptr() as usize - self.buffer.get_backing_ptr() as usize; let mut alloc = self.alloc.lock().unwrap(); @@ -311,12 +313,18 @@ impl TrampolineBufferBuilder { idx } - /// Inserts to the global trampoline buffer. + /// Inserts this trampoline to the global trampoline buffer. pub fn insert_global(self) -> Option> { TRAMPOLINES.insert(&self.code) } - /// Removes from the global trampoline buffer. + /// Removes the trampoline pointed to by `ptr` from the global trampoline buffer. Panics if `ptr` + /// does not point to any trampoline. + /// + /// # Safety + /// + /// Calling this function invalidates the trampoline `ptr` points to and recycles its memory. You + /// should ensure that `ptr` isn't used after calling `remove_global`. pub unsafe fn remove_global(ptr: NonNull) { TRAMPOLINES.remove(ptr); } From 72e6a85ea5f6fb8f1e85c4d05f395db7e6785430 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 26 Feb 2020 23:42:48 +0800 Subject: [PATCH 15/79] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6871ecb3d..102fc0b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## 0.14.1 - 2020-02-24 +- [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation. - [#1245](https://github.com/wasmerio/wasmer/pull/1245) Use Ubuntu 16.04 in CI so that we use an earlier version of GLIBC. - [#1234](https://github.com/wasmerio/wasmer/pull/1234) Check for unused excluded spectest failures. - [#1232](https://github.com/wasmerio/wasmer/pull/1232) `wasmer-interface-types` has a WAT decoder. From 31a72e59fbd96263277a5827b3576351b6698ed0 Mon Sep 17 00:00:00 2001 From: losfair Date: Fri, 28 Feb 2020 11:41:36 +0800 Subject: [PATCH 16/79] Rename ErasedFunc to DynamicFunc and fix leaky `PolymorphicContext`. --- lib/runtime-core-tests/tests/imports.rs | 16 +++++------ lib/runtime-core/src/typed_func.rs | 35 ++++++++++++++----------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index 17c82097a..1af995368 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -4,7 +4,7 @@ use wasmer_runtime_core::{ error::RuntimeError, imports, memory::Memory, - typed_func::{ErasedFunc, Func}, + typed_func::{DynamicFunc, Func}, types::{FuncSig, MemoryDescriptor, Type, Value}, units::Pages, vm, Instance, @@ -75,7 +75,7 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { (import "env" "memory" (memory 1 1)) (import "env" "callback_fn" (func $callback_fn (type $type))) (import "env" "callback_closure" (func $callback_closure (type $type))) - (import "env" "callback_closure_polymorphic" (func $callback_closure_polymorphic (type $type))) + (import "env" "callback_closure_dynamic" (func $callback_closure_dynamic (type $type))) (import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type))) (import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type))) (import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type))) @@ -94,9 +94,9 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { get_local 0 call $callback_closure) - (func (export "function_closure_polymorphic") (type $type) + (func (export "function_closure_dynamic") (type $type) get_local 0 - call $callback_closure_polymorphic) + call $callback_closure_dynamic) (func (export "function_closure_with_env") (type $type) get_local 0 @@ -154,7 +154,7 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { Ok(n + 1) }), - "callback_closure_polymorphic" => ErasedFunc::new_polymorphic( + "callback_closure_dynamic" => DynamicFunc::new( Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])), |_, params| -> Vec { match params[0] { @@ -258,11 +258,7 @@ macro_rules! test { test!(test_fn, function_fn, Ok(2)); test!(test_closure, function_closure, Ok(2)); -test!( - test_closure_polymorphic, - function_closure_polymorphic, - Ok(2) -); +test!(test_closure_dynamic, function_closure_dynamic, Ok(2)); test!( test_closure_with_env, function_closure_with_env, diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index ec1f580b4..b8d3b0bd0 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -191,7 +191,7 @@ where } /// Represents a type-erased function provided by either the host or the WebAssembly program. -pub struct ErasedFunc<'a> { +pub struct DynamicFunc<'a> { _inner: Box, /// The function pointer. @@ -206,14 +206,14 @@ pub struct ErasedFunc<'a> { /// The runtime signature of this function. /// /// When converted from a `Func`, this is determined by the static `Args` and `Rets` type parameters. - /// otherwise the signature is dynamically assigned during `ErasedFunc` creation, usually when creating + /// otherwise the signature is dynamically assigned during `DynamicFunc` creation, usually when creating /// a polymorphic host function. signature: Arc, _phantom: PhantomData<&'a ()>, } -unsafe impl<'a> Send for ErasedFunc<'a> {} +unsafe impl<'a> Send for DynamicFunc<'a> {} /// Represents a function that can be used by WebAssembly. pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { @@ -234,14 +234,14 @@ pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Wasm> {} unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Host> {} -impl<'a, Args, Rets, Inner> From> for ErasedFunc<'a> +impl<'a, Args, Rets, Inner> From> for DynamicFunc<'a> where Args: WasmTypeList, Rets: WasmTypeList, Inner: Kind + 'static, { - fn from(that: Func<'a, Args, Rets, Inner>) -> ErasedFunc<'a> { - ErasedFunc { + fn from(that: Func<'a, Args, Rets, Inner>) -> DynamicFunc<'a> { + DynamicFunc { _inner: Box::new(that.inner), func: that.func, func_env: that.func_env, @@ -296,11 +296,11 @@ where } } -impl<'a> ErasedFunc<'a> { - /// Creates a polymorphic function. +impl<'a> DynamicFunc<'a> { + /// Creates a dynamic function that is polymorphic over its argument and return types. #[allow(unused_variables)] #[cfg(all(unix, target_arch = "x86_64"))] - pub fn new_polymorphic(signature: Arc, func: F) -> Self + pub fn new(signature: Arc, func: F) -> Self where F: Fn(&mut vm::Ctx, &[crate::types::Value]) -> Vec + 'static, { @@ -349,13 +349,14 @@ impl<'a> ErasedFunc<'a> { } } let mut builder = TrampolineBufferBuilder::new(); - let ctx = Box::new(PolymorphicContext { + let ctx: Box = Box::new(PolymorphicContext { arg_types: signature.params().to_vec(), func: Box::new(func), }); + let ctx = Box::into_raw(ctx); builder.add_callinfo_trampoline( enter_host_polymorphic, - Box::into_raw(ctx) as *const _, + ctx as *const _, (signature.params().len() + 1) as u32, // +vmctx ); let ptr = builder @@ -364,20 +365,22 @@ impl<'a> ErasedFunc<'a> { struct AutoRelease { ptr: NonNull, + ctx: *mut PolymorphicContext, } impl Drop for AutoRelease { fn drop(&mut self) { unsafe { TrampolineBufferBuilder::remove_global(self.ptr); + Box::from_raw(self.ctx); } } } impl Kind for AutoRelease {} - ErasedFunc { - _inner: Box::new(AutoRelease { ptr }), + DynamicFunc { + _inner: Box::new(AutoRelease { ptr, ctx }), func: ptr.cast::(), func_env: None, vmctx: ptr::null_mut(), @@ -815,7 +818,7 @@ impl_traits!([C] S24, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T impl_traits!([C] S25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y); impl_traits!([C] S26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); -impl<'a> IsExport for ErasedFunc<'a> { +impl<'a> IsExport for DynamicFunc<'a> { fn to_export(&self) -> Export { let func = unsafe { FuncPointer::new(self.func.as_ptr()) }; let ctx = match self.func_env { @@ -956,13 +959,13 @@ mod tests { } #[test] - fn test_many_new_polymorphics() { + fn test_many_new_dynamics() { use crate::types::{FuncSig, Type}; // Check that generating a lot (1M) of polymorphic functions doesn't use up the executable buffer. for _ in 0..1000000 { let arglist = vec![Type::I32; 100]; - ErasedFunc::new_polymorphic( + DynamicFunc::new( Arc::new(FuncSig::new(arglist, vec![Type::I32])), |_, _| unreachable!(), ); From 2ddf9ad4c833250185e78de554576b9a9afbd963 Mon Sep 17 00:00:00 2001 From: losfair Date: Fri, 28 Feb 2020 22:16:29 +0800 Subject: [PATCH 17/79] Disallow "fat" closures. --- lib/runtime-core/src/typed_func.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index b8d3b0bd0..0b29d54ea 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -348,6 +348,12 @@ impl<'a> DynamicFunc<'a> { ); } } + + // Disable "fat" closures for possible future changes. + if mem::size_of::() != 0 { + unimplemented!("DynamicFunc with captured environment is not yet supported"); + } + let mut builder = TrampolineBufferBuilder::new(); let ctx: Box = Box::new(PolymorphicContext { arg_types: signature.params().to_vec(), From 84179dbd5e807f97823913787f7a256503c285e7 Mon Sep 17 00:00:00 2001 From: losfair Date: Sat, 29 Feb 2020 11:12:26 +0800 Subject: [PATCH 18/79] Fix changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e09f1dc3..a4c5da26f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation. - [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging: - Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation. The debug info is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend. - Break public middleware APIs: there is now a `source_loc` parameter that should be passed through if applicable. @@ -9,7 +10,6 @@ ## 0.14.1 - 2020-02-24 -- [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation. - [#1245](https://github.com/wasmerio/wasmer/pull/1245) Use Ubuntu 16.04 in CI so that we use an earlier version of GLIBC. - [#1234](https://github.com/wasmerio/wasmer/pull/1234) Check for unused excluded spectest failures. - [#1232](https://github.com/wasmerio/wasmer/pull/1232) `wasmer-interface-types` has a WAT decoder. From 4012645aeea76614b2b4e072da78fdd893cfb916 Mon Sep 17 00:00:00 2001 From: losfair Date: Sat, 29 Feb 2020 11:13:34 +0800 Subject: [PATCH 19/79] Fix `CodeMemory` doc comments. --- lib/runtime-core/src/loader.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/runtime-core/src/loader.rs b/lib/runtime-core/src/loader.rs index ea1ca0130..738c8dfef 100644 --- a/lib/runtime-core/src/loader.rs +++ b/lib/runtime-core/src/loader.rs @@ -140,12 +140,12 @@ impl CodeMemory { unimplemented!("CodeMemory::new"); } - /// Makes this code memory executable. + /// Makes this code memory executable and not writable. pub fn make_executable(&self) { unimplemented!("CodeMemory::make_executable"); } - /// Makes this code memory writable. + /// Makes this code memory writable and not executable. pub fn make_writable(&self) { unimplemented!("CodeMemory::make_writable"); } @@ -185,14 +185,14 @@ impl CodeMemory { } } - /// Makes this code memory executable. + /// Makes this code memory executable and not writable. pub fn make_executable(&self) { if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 { panic!("cannot set code memory to executable"); } } - /// Makes this code memory writable. + /// Makes this code memory writable and not executable. pub fn make_writable(&self) { if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE) } != 0 { panic!("cannot set code memory to writable"); From d443ad8d4069adc79326f7c84b212027d488413f Mon Sep 17 00:00:00 2001 From: losfair Date: Sat, 29 Feb 2020 11:15:09 +0800 Subject: [PATCH 20/79] Remove outdated comment. --- lib/runtime-core/src/trampoline_x64.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 1fe5d674b..bf821ca15 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -54,7 +54,6 @@ struct AllocState { impl TrampBuffer { /// Creates a trampoline buffer with a given (virtual) size. fn new(size: usize) -> TrampBuffer { - // Pre-allocate 64 MiB of virtual memory for code. let mem = CodeMemory::new(size); mem.make_writable_executable(); TrampBuffer { From 3e0d299227077598506955a0fea2551971444482 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 2 Mar 2020 13:41:23 -0800 Subject: [PATCH 21/79] Update to latest inkwell which adds context lifetime to basic blocks. --- Cargo.lock | 4 +- lib/llvm-backend/Cargo.toml | 4 +- lib/llvm-backend/src/code.rs | 114 ++++++++++++++-------------- lib/llvm-backend/src/state.rs | 28 +++---- lib/llvm-backend/src/trampolines.rs | 2 +- 5 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc9ab58bc..27f43a523 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -659,7 +659,7 @@ dependencies = [ [[package]] name = "inkwell" version = "0.1.0" -source = "git+https://github.com/TheDan64/inkwell?rev=0a864ebf68b33d4d514b67796264b03898aa0944#0a864ebf68b33d4d514b67796264b03898aa0944" +source = "git+https://github.com/TheDan64/inkwell?rev=af4cf4efbb27cdea8a54175ffc18ffd91964618c#af4cf4efbb27cdea8a54175ffc18ffd91964618c" dependencies = [ "either", "inkwell_internals", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.1.0" -source = "git+https://github.com/TheDan64/inkwell?rev=0a864ebf68b33d4d514b67796264b03898aa0944#0a864ebf68b33d4d514b67796264b03898aa0944" +source = "git+https://github.com/TheDan64/inkwell?rev=af4cf4efbb27cdea8a54175ffc18ffd91964618c#af4cf4efbb27cdea8a54175ffc18ffd91964618c" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 6236a04ca..2e7e6af68 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -19,13 +19,13 @@ byteorder = "1" [target.'cfg(target_arch = "x86_64")'.dependencies.inkwell] git = "https://github.com/TheDan64/inkwell" -rev = "0a864ebf68b33d4d514b67796264b03898aa0944" +rev = "af4cf4efbb27cdea8a54175ffc18ffd91964618c" default-features = false features = ["llvm8-0", "target-x86"] [target.'cfg(target_arch = "aarch64")'.dependencies.inkwell] git = "https://github.com/TheDan64/inkwell" -rev = "0a864ebf68b33d4d514b67796264b03898aa0944" +rev = "af4cf4efbb27cdea8a54175ffc18ffd91964618c" default-features = false features = ["llvm8-0", "target-aarch64"] diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index d33e67fac..954e0c405 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -354,15 +354,15 @@ fn trap_if_not_representable_as_int<'ctx>( let failure_block = context.append_basic_block(*function, "conversion_failure_block"); let continue_block = context.append_basic_block(*function, "conversion_success_block"); - builder.build_conditional_branch(out_of_bounds, &failure_block, &continue_block); - builder.position_at_end(&failure_block); + builder.build_conditional_branch(out_of_bounds, failure_block, continue_block); + builder.position_at_end(failure_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_illegal_arithmetic], "throw", ); builder.build_unreachable(); - builder.position_at_end(&continue_block); + builder.position_at_end(continue_block); } fn trap_if_zero_or_overflow<'ctx>( @@ -418,15 +418,15 @@ fn trap_if_zero_or_overflow<'ctx>( let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block"); let should_trap_block = context.append_basic_block(*function, "should_trap_block"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); + builder.build_conditional_branch(should_trap, should_trap_block, shouldnt_trap_block); + builder.position_at_end(should_trap_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_illegal_arithmetic], "throw", ); builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); + builder.position_at_end(shouldnt_trap_block); } fn trap_if_zero<'ctx>( @@ -460,15 +460,15 @@ fn trap_if_zero<'ctx>( let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block"); let should_trap_block = context.append_basic_block(*function, "should_trap_block"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); + builder.build_conditional_branch(should_trap, should_trap_block, shouldnt_trap_block); + builder.position_at_end(should_trap_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_illegal_arithmetic], "throw", ); builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); + builder.position_at_end(shouldnt_trap_block); } fn v128_into_int_vec<'ctx>( @@ -774,17 +774,17 @@ fn resolve_memory_ptr<'ctx>( let not_in_bounds_block = context.append_basic_block(*function, "not_in_bounds_block"); builder.build_conditional_branch( ptr_in_bounds, - &in_bounds_continue_block, - ¬_in_bounds_block, + in_bounds_continue_block, + not_in_bounds_block, ); - builder.position_at_end(¬_in_bounds_block); + builder.position_at_end(not_in_bounds_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_memory_oob], "throw", ); builder.build_unreachable(); - builder.position_at_end(&in_bounds_continue_block); + builder.position_at_end(in_bounds_continue_block); } } @@ -918,9 +918,9 @@ fn trap_if_misaligned<'ctx>( let continue_block = context.append_basic_block(*function, "aligned_access_continue_block"); let not_aligned_block = context.append_basic_block(*function, "misaligned_trap_block"); - builder.build_conditional_branch(aligned, &continue_block, ¬_aligned_block); + builder.build_conditional_branch(aligned, continue_block, not_aligned_block); - builder.position_at_end(¬_aligned_block); + builder.position_at_end(not_aligned_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_misaligned_atomic], @@ -928,7 +928,7 @@ fn trap_if_misaligned<'ctx>( ); builder.build_unreachable(); - builder.position_at_end(&continue_block); + builder.position_at_end(continue_block); } #[derive(Debug)] @@ -1053,11 +1053,11 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct .builder .as_ref() .unwrap() - .build_unconditional_branch(&start_of_code_block); + .build_unconditional_branch(start_of_code_block); self.builder .as_ref() .unwrap() - .position_at_end(&start_of_code_block); + .position_at_end(start_of_code_block); let cache_builder = self.context.as_ref().unwrap().create_builder(); cache_builder.position_before(&entry_end_inst); @@ -1210,7 +1210,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct })?; let end_block = context.append_basic_block(function, "end"); - builder.position_at_end(&end_block); + builder.position_at_end(end_block); let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) { let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); @@ -1223,15 +1223,15 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct }; state.push_block(end_block, phis); - builder.position_at_end(¤t_block); + builder.position_at_end(current_block); } Operator::Loop { ty } => { let loop_body = context.append_basic_block(function, "loop_body"); let loop_next = context.append_basic_block(function, "loop_outer"); - builder.build_unconditional_branch(&loop_body); + builder.build_unconditional_branch(loop_body); - builder.position_at_end(&loop_next); + builder.position_at_end(loop_next); let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) { let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); [llvm_ty] @@ -1242,7 +1242,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct SmallVec::new() }; - builder.position_at_end(&loop_body); + builder.position_at_end(loop_body); if self.track_state { if let Some(offset) = opcode_offset { @@ -1299,10 +1299,10 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct // pop a value off the value stack and load it into // the corresponding phi. for (phi, value) in frame.phis().iter().zip(values) { - phi.add_incoming(&[(&value, ¤t_block)]); + phi.add_incoming(&[(&value, current_block)]); } - builder.build_unconditional_branch(frame.br_dest()); + builder.build_unconditional_branch(*frame.br_dest()); state.popn(value_len)?; state.reachable = false; @@ -1327,7 +1327,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct }); for (phi, value) in frame.phis().iter().zip(param_stack) { - phi.add_incoming(&[(&value, ¤t_block)]); + phi.add_incoming(&[(&value, current_block)]); } let else_block = context.append_basic_block(function, "else"); @@ -1338,8 +1338,8 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct intrinsics.i32_zero, &state.var_name(), ); - builder.build_conditional_branch(cond_value, frame.br_dest(), &else_block); - builder.position_at_end(&else_block); + builder.build_conditional_branch(cond_value, *frame.br_dest(), else_block); + builder.position_at_end(else_block); } Operator::BrTable { ref table } => { let current_block = builder.get_insert_block().ok_or(CodegenError { @@ -1360,7 +1360,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct }; for (phi, value) in default_frame.phis().iter().zip(args.iter()) { - phi.add_incoming(&[(value, ¤t_block)]); + phi.add_incoming(&[(value, current_block)]); } let cases: Vec<_> = label_depths @@ -1377,14 +1377,14 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct context.i32_type().const_int(case_index as u64, false); for (phi, value) in frame.phis().iter().zip(args.iter()) { - phi.add_incoming(&[(value, ¤t_block)]); + phi.add_incoming(&[(value, current_block)]); } - Ok((case_index_literal, frame.br_dest())) + Ok((case_index_literal, *frame.br_dest())) }) .collect::>()?; - builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); + builder.build_switch(index.into_int_value(), *default_frame.br_dest(), &cases[..]); let args_len = args.len(); state.popn(args_len)?; @@ -1399,7 +1399,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct let end_block = context.append_basic_block(function, "if_end"); let end_phis = { - builder.position_at_end(&end_block); + builder.position_at_end(end_block); let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) { let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); @@ -1411,7 +1411,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct SmallVec::new() }; - builder.position_at_end(¤t_block); + builder.position_at_end(current_block); phis }; @@ -1424,8 +1424,8 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct &state.var_name(), ); - builder.build_conditional_branch(cond_value, &if_then_block, &if_else_block); - builder.position_at_end(&if_then_block); + builder.build_conditional_branch(cond_value, if_then_block, if_else_block); + builder.position_at_end(if_then_block); state.push_if(if_then_block, if_else_block, end_block, end_phis); } Operator::Else => { @@ -1439,10 +1439,10 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct let (value, info) = state.pop1_extra()?; let value = apply_pending_canonicalization(builder, intrinsics, value, info); - phi.add_incoming(&[(&value, ¤t_block)]) + phi.add_incoming(&[(&value, current_block)]) } let frame = state.frame_at_depth(0)?; - builder.build_unconditional_branch(frame.code_after()); + builder.build_unconditional_branch(*frame.code_after()); } let (if_else_block, if_else_state) = if let ControlFrame::IfElse { @@ -1458,7 +1458,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct *if_else_state = IfElseState::Else; - builder.position_at_end(if_else_block); + builder.position_at_end(*if_else_block); state.reachable = true; } @@ -1473,10 +1473,10 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct let (value, info) = state.pop1_extra()?; let value = apply_pending_canonicalization(builder, intrinsics, value, info); - phi.add_incoming(&[(&value, ¤t_block)]); + phi.add_incoming(&[(&value, current_block)]); } - builder.build_unconditional_branch(frame.code_after()); + builder.build_unconditional_branch(*frame.code_after()); } if let ControlFrame::IfElse { @@ -1487,12 +1487,12 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct } = &frame { if let IfElseState::If = if_else_state { - builder.position_at_end(if_else); - builder.build_unconditional_branch(next); + builder.position_at_end(*if_else); + builder.build_unconditional_branch(*next); } } - builder.position_at_end(frame.code_after()); + builder.position_at_end(*frame.code_after()); state.reset_stack(&frame); state.reachable = true; @@ -1530,11 +1530,11 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct for phi in frame.phis().to_vec().iter() { let (arg, info) = state.pop1_extra()?; let arg = apply_pending_canonicalization(builder, intrinsics, arg, info); - phi.add_incoming(&[(&arg, ¤t_block)]); + phi.add_incoming(&[(&arg, current_block)]); } let frame = state.outermost_frame()?; - builder.build_unconditional_branch(frame.br_dest()); + builder.build_unconditional_branch(*frame.br_dest()); state.reachable = false; } @@ -2073,17 +2073,17 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct context.append_basic_block(function, "not_in_bounds_block"); builder.build_conditional_branch( index_in_bounds, - &in_bounds_continue_block, - ¬_in_bounds_block, + in_bounds_continue_block, + not_in_bounds_block, ); - builder.position_at_end(¬_in_bounds_block); + builder.position_at_end(not_in_bounds_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_call_indirect_oob], "throw", ); builder.build_unreachable(); - builder.position_at_end(&in_bounds_continue_block); + builder.position_at_end(in_bounds_continue_block); // Next, check if the signature id is correct. @@ -2114,18 +2114,18 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct context.append_basic_block(function, "sigindices_notequal_block"); builder.build_conditional_branch( sigindices_equal, - &continue_block, - &sigindices_notequal_block, + continue_block, + sigindices_notequal_block, ); - builder.position_at_end(&sigindices_notequal_block); + builder.position_at_end(sigindices_notequal_block); builder.build_call( intrinsics.throw_trap, &[intrinsics.trap_call_indirect_sig], "throw", ); builder.build_unreachable(); - builder.position_at_end(&continue_block); + builder.position_at_end(continue_block); let wasmer_fn_sig = &info.signatures[sig_index]; let fn_ty = signatures[sig_index]; @@ -8763,10 +8763,10 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod let mut state: State<'ctx> = State::new(); let entry_block = context.append_basic_block(*function, "entry"); let alloca_builder = context.create_builder(); - alloca_builder.position_at_end(&entry_block); + alloca_builder.position_at_end(entry_block); let return_block = context.append_basic_block(*function, "return"); - builder.position_at_end(&return_block); + builder.position_at_end(return_block); let phis: SmallVec<[PhiValue; 1]> = func_sig .returns() @@ -8776,7 +8776,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod .collect(); state.push_block(return_block, phis); - builder.position_at_end(&entry_block); + builder.position_at_end(entry_block); let mut locals = Vec::new(); locals.extend( diff --git a/lib/llvm-backend/src/state.rs b/lib/llvm-backend/src/state.rs index ed3f8bb3a..cdbb8f25e 100644 --- a/lib/llvm-backend/src/state.rs +++ b/lib/llvm-backend/src/state.rs @@ -10,20 +10,20 @@ use std::ops::{BitAnd, BitOr, BitOrAssign}; #[derive(Debug)] pub enum ControlFrame<'ctx> { Block { - next: BasicBlock, + next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>, stack_size_snapshot: usize, }, Loop { - body: BasicBlock, - next: BasicBlock, + body: BasicBlock<'ctx>, + next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>, stack_size_snapshot: usize, }, IfElse { - if_then: BasicBlock, - if_else: BasicBlock, - next: BasicBlock, + if_then: BasicBlock<'ctx>, + if_else: BasicBlock<'ctx>, + next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>, stack_size_snapshot: usize, if_else_state: IfElseState, @@ -37,7 +37,7 @@ pub enum IfElseState { } impl<'ctx> ControlFrame<'ctx> { - pub fn code_after(&self) -> &BasicBlock { + pub fn code_after(&self) -> &BasicBlock<'ctx> { match self { ControlFrame::Block { ref next, .. } | ControlFrame::Loop { ref next, .. } @@ -45,7 +45,7 @@ impl<'ctx> ControlFrame<'ctx> { } } - pub fn br_dest(&self) -> &BasicBlock { + pub fn br_dest(&self) -> &BasicBlock<'ctx> { match self { ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next, ControlFrame::Loop { ref body, .. } => body, @@ -367,7 +367,7 @@ impl<'ctx> State<'ctx> { Ok(()) } - pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue<'ctx>; 1]>) { + pub fn push_block(&mut self, next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>) { self.control_stack.push(ControlFrame::Block { next, phis, @@ -377,8 +377,8 @@ impl<'ctx> State<'ctx> { pub fn push_loop( &mut self, - body: BasicBlock, - next: BasicBlock, + body: BasicBlock<'ctx>, + next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>, ) { self.control_stack.push(ControlFrame::Loop { @@ -391,9 +391,9 @@ impl<'ctx> State<'ctx> { pub fn push_if( &mut self, - if_then: BasicBlock, - if_else: BasicBlock, - next: BasicBlock, + if_then: BasicBlock<'ctx>, + if_else: BasicBlock<'ctx>, + next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>, ) { self.control_stack.push(ControlFrame::IfElse { diff --git a/lib/llvm-backend/src/trampolines.rs b/lib/llvm-backend/src/trampolines.rs index 56d552cdd..51dd7f43e 100644 --- a/lib/llvm-backend/src/trampolines.rs +++ b/lib/llvm-backend/src/trampolines.rs @@ -55,7 +55,7 @@ fn generate_trampoline<'ctx>( intrinsics: &Intrinsics<'ctx>, ) -> Result<(), String> { let entry_block = context.append_basic_block(trampoline_func, "entry"); - builder.position_at_end(&entry_block); + builder.position_at_end(entry_block); let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice() { From 30e72eb796984200298dac5070f3c66c518bc81a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 16:57:05 +0100 Subject: [PATCH 22/79] feat(interface-types) Implement lifting and lowering instructions. --- .../instructions/lowering_lifting.rs | 342 ++++++++++++++++++ .../src/interpreter/instructions/mod.rs | 2 + lib/interface-types/src/interpreter/mod.rs | 39 ++ lib/interface-types/src/macros.rs | 17 +- 4 files changed, 392 insertions(+), 8 deletions(-) create mode 100644 lib/interface-types/src/interpreter/instructions/lowering_lifting.rs diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs new file mode 100644 index 000000000..cd43104d1 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -0,0 +1,342 @@ +use crate::interpreter::wasm::values::InterfaceValue; +use std::convert::TryInto; + +macro_rules! lowering_lifting { + ($instruction_function_name:ident, $instruction_name:expr, $from_variant:ident, $to_variant:ident) => { + executable_instruction!( + $instruction_function_name() -> _ { + move |runtime| -> _ { + match runtime.stack.pop1() { + Some(InterfaceValue::$from_variant(value)) => { + runtime + .stack + .push(InterfaceValue::$to_variant(value.try_into().map_err( + |_| { + concat!( + "Failed to cast ", + stringify!($from_variant), + " to ", + stringify!($to_variant), + "." + ).to_string() + }, + )?)) + } + + Some(_) => { + return Err(concat!( + "Instruction `", + $instruction_name, + "` expects a `i32` value on the stack." + ) + .to_string()) + }, + + None => { + return Err(concat!( + "Instruction `", + $instruction_name, + "` needs one value on the stack." + ) + .to_string()) + } + } + + Ok(()) + } + } + ); + }; +} + +lowering_lifting!(i32_to_s8, "i32-to-s8", I32, S8); +lowering_lifting!(i32_to_u8, "i32-to-u8", I32, U8); +lowering_lifting!(i32_to_s16, "i32-to-s16", I32, S16); +lowering_lifting!(i32_to_u16, "i32-to-u16", I32, U16); +lowering_lifting!(i32_to_s32, "i32-to-s32", I32, S32); +lowering_lifting!(i32_to_u32, "i32-to-u32", I32, U32); +lowering_lifting!(i32_to_s64, "i32-to-s64", I32, S64); +lowering_lifting!(i32_to_u64, "i32-to-u64", I32, U64); +lowering_lifting!(i64_to_s8, "i64-to-s8", I64, S8); +lowering_lifting!(i64_to_u8, "i64-to-u8", I64, U8); +lowering_lifting!(i64_to_s16, "i64-to-s16", I64, S16); +lowering_lifting!(i64_to_u16, "i64-to-u16", I64, U16); +lowering_lifting!(i64_to_s32, "i64-to-s32", I64, S32); +lowering_lifting!(i64_to_u32, "i64-to-u32", I64, U32); +lowering_lifting!(i64_to_s64, "i64-to-s64", I64, S64); +lowering_lifting!(i64_to_u64, "i64-to-u64", I64, U64); +lowering_lifting!(s8_to_i32, "s8-to-i32", S8, I32); +lowering_lifting!(u8_to_i32, "u8-to-i32", U8, I32); +lowering_lifting!(s16_to_i32, "s16-to-i32", S16, I32); +lowering_lifting!(u16_to_i32, "u16-to-i32", U16, I32); +lowering_lifting!(s32_to_i32, "s32-to-i32", S32, I32); +lowering_lifting!(u32_to_i32, "u32-to-i32", U32, I32); +lowering_lifting!(s64_to_i32, "s64-to-i32", S64, I32); +lowering_lifting!(u64_to_i32, "u64-to-i32", U64, I32); +lowering_lifting!(s8_to_i64, "s8-to-i64", S8, I64); +lowering_lifting!(u8_to_i64, "u8-to-i64", U8, I64); +lowering_lifting!(s16_to_i64, "s16-to-i64", S16, I64); +lowering_lifting!(u16_to_i64, "u16-to-i64", U16, I64); +lowering_lifting!(s32_to_i64, "s32-to-i64", S32, I64); +lowering_lifting!(u32_to_i64, "u32-to-i64", U32, I64); +lowering_lifting!(s64_to_i64, "s64-to-i64", S64, I64); +lowering_lifting!(u64_to_i64, "u64-to-i64", U64, I64); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_i32_to_s8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S8(42)], + ); + + test_executable_instruction!( + test_i32_to_u8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU8], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U8(42)], + ); + + test_executable_instruction!( + test_i32_to_s16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS16], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S16(42)], + ); + + test_executable_instruction!( + test_i32_to_u16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU16], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U16(42)], + ); + + test_executable_instruction!( + test_i32_to_s32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS32], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S32(42)], + ); + + test_executable_instruction!( + test_i32_to_u32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU32], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U32(42)], + ); + + test_executable_instruction!( + test_i32_to_s64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS64], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S64(42)], + ); + + test_executable_instruction!( + test_i32_to_u64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU64], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U64(42)], + ); + + test_executable_instruction!( + test_i64_to_s8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS8], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S8(42)], + ); + + test_executable_instruction!( + test_i64_to_u8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU8], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U8(42)], + ); + + test_executable_instruction!( + test_i64_to_s16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS16], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S16(42)], + ); + + test_executable_instruction!( + test_i64_to_u16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU16], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U16(42)], + ); + + test_executable_instruction!( + test_i64_to_s32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS32], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S32(42)], + ); + + test_executable_instruction!( + test_i64_to_u32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU32], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U32(42)], + ); + + test_executable_instruction!( + test_i64_to_s64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS64], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S64(42)], + ); + + test_executable_instruction!( + test_i64_to_u64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU64], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U64(42)], + ); + + test_executable_instruction!( + test_s8_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8ToI32], + invocation_inputs: [InterfaceValue::S8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u8_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8ToI32], + invocation_inputs: [InterfaceValue::U8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s16_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16ToI32], + invocation_inputs: [InterfaceValue::S16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u16_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16ToI32], + invocation_inputs: [InterfaceValue::U16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s32_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32ToI32], + invocation_inputs: [InterfaceValue::S32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u32_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32ToI32], + invocation_inputs: [InterfaceValue::U32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s64_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64ToI32], + invocation_inputs: [InterfaceValue::S64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u64_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64ToI32], + invocation_inputs: [InterfaceValue::U64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s8_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8ToI64], + invocation_inputs: [InterfaceValue::S8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u8_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8ToI64], + invocation_inputs: [InterfaceValue::U8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_s16_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16ToI64], + invocation_inputs: [InterfaceValue::S16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u16_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16ToI64], + invocation_inputs: [InterfaceValue::U16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_s32_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32ToI64], + invocation_inputs: [InterfaceValue::S32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u32_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32ToI64], + invocation_inputs: [InterfaceValue::U32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_s64_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64ToI64], + invocation_inputs: [InterfaceValue::S64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u64_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64ToI64], + invocation_inputs: [InterfaceValue::U64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index d84d2b47e..28aa5209b 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -1,12 +1,14 @@ mod argument_get; mod call; mod call_export; +mod lowering_lifting; mod read_utf8; mod write_utf8; pub(crate) use argument_get::argument_get; pub(crate) use call::call; pub(crate) use call_export::call_export; +pub(crate) use lowering_lifting::*; pub(crate) use read_utf8::read_utf8; pub(crate) use write_utf8::write_utf8; diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 5cba4613c..2fbb84685 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -206,6 +206,45 @@ where Instruction::WriteUtf8 { allocator_name } => { instructions::write_utf8((*allocator_name).to_owned(), instruction_name) } + + Instruction::I32ToS8 => instructions::i32_to_s8(), + //Instruction::I32ToS8X + Instruction::I32ToU8 => instructions::i32_to_u8(), + Instruction::I32ToS16 => instructions::i32_to_s16(), + //Instruction::I32ToS16X + Instruction::I32ToU16 => instructions::i32_to_u16(), + Instruction::I32ToS32 => instructions::i32_to_s32(), + Instruction::I32ToU32 => instructions::i32_to_u32(), + Instruction::I32ToS64 => instructions::i32_to_s64(), + Instruction::I32ToU64 => instructions::i32_to_u64(), + Instruction::I64ToS8 => instructions::i64_to_s8(), + //Instruction::I64ToS8X + Instruction::I64ToU8 => instructions::i64_to_u8(), + Instruction::I64ToS16 => instructions::i64_to_s16(), + //Instruction::I64ToS16X + Instruction::I64ToU16 => instructions::i64_to_u16(), + Instruction::I64ToS32 => instructions::i64_to_s32(), + Instruction::I64ToU32 => instructions::i64_to_u32(), + Instruction::I64ToS64 => instructions::i64_to_s64(), + Instruction::I64ToU64 => instructions::i64_to_u64(), + Instruction::S8ToI32 => instructions::s8_to_i32(), + Instruction::U8ToI32 => instructions::u8_to_i32(), + Instruction::S16ToI32 => instructions::s16_to_i32(), + Instruction::U16ToI32 => instructions::u16_to_i32(), + Instruction::S32ToI32 => instructions::s32_to_i32(), + Instruction::U32ToI32 => instructions::u32_to_i32(), + Instruction::S64ToI32 => instructions::s64_to_i32(), + //Instruction::S64ToI32X + Instruction::U64ToI32 => instructions::u64_to_i32(), + //Instruction::U64ToI32X + Instruction::S8ToI64 => instructions::s8_to_i64(), + Instruction::U8ToI64 => instructions::u8_to_i64(), + Instruction::S16ToI64 => instructions::s16_to_i64(), + Instruction::U16ToI64 => instructions::u16_to_i64(), + Instruction::S32ToI64 => instructions::s32_to_i64(), + Instruction::U32ToI64 => instructions::u32_to_i64(), + Instruction::S64ToI64 => instructions::s64_to_i64(), + Instruction::U64ToI64 => instructions::u64_to_i64(), _ => unimplemented!(), } }) diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index 7ddfb690f..5e2217e29 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -40,18 +40,19 @@ macro_rules! consume { /// Check the existing executable instruction to get more examples. macro_rules! executable_instruction { ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { - use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable}; - pub(crate) fn $name( $($argument_name: $argument_type),* - ) -> ExecutableInstruction + ) -> crate::interpreter::ExecutableInstruction where - Export: wasm::structures::Export, - LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance, { + #[allow(unused_imports)] + use crate::interpreter::{stack::Stackable}; + Box::new($implementation) } }; From 6576dfd64b1aed61bc6f661645ec7cc13edfda53 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 17:04:26 +0100 Subject: [PATCH 23/79] test(interface-types) Add test cases for errors. --- .../instructions/lowering_lifting.rs | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index cd43104d1..1ba8a386e 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -23,11 +23,17 @@ macro_rules! lowering_lifting { )?)) } - Some(_) => { - return Err(concat!( - "Instruction `", - $instruction_name, - "` expects a `i32` value on the stack." + Some(wrong_value) => { + return Err(format!( + concat!( + "Instruction `", + $instruction_name, + "` expects a `", + stringify!($from_variant), + "` value on the stack, got `{:?}`.", + ), + wrong_value + ) .to_string()) }, @@ -92,6 +98,22 @@ mod tests { stack: [InterfaceValue::S8(42)], ); + test_executable_instruction!( + test_type_mismatch = + instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + error: "Instruction `i32-to-s8` expects a `I32` value on the stack, got `I64(42)`." + ); + + test_executable_instruction!( + test_no_value_on_the_stack = + instructions: [Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + error: "Instruction `i32-to-s8` needs one value on the stack." + ); + test_executable_instruction!( test_i32_to_u8 = instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU8], From c157bdaf637848d294e71cd5e5a371ff1217ee3a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 17:10:10 +0100 Subject: [PATCH 24/79] test(interface-types) Test when lowering or lifting fails because of the value. --- .../interpreter/instructions/lowering_lifting.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index 1ba8a386e..c24fd992b 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -13,11 +13,11 @@ macro_rules! lowering_lifting { .push(InterfaceValue::$to_variant(value.try_into().map_err( |_| { concat!( - "Failed to cast ", + "Failed to cast `", stringify!($from_variant), - " to ", + "` to `", stringify!($to_variant), - "." + "`." ).to_string() }, )?)) @@ -98,6 +98,14 @@ mod tests { stack: [InterfaceValue::S8(42)], ); + test_executable_instruction!( + test_convert_fails = + instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I32(128)], + instance: Instance::new(), + error: "Failed to cast `I32` to `S8`." + ); + test_executable_instruction!( test_type_mismatch = instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], From de53659ee7425d4b1eafaed97dab1869541dc85a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 17:17:26 +0100 Subject: [PATCH 25/79] feat(interface-types) Implement `s64-to-i32x` and `u64-to-i32x` Actually, `s64-to-i32` and `u64-to-i32` already error when overflow happens. --- lib/interface-types/src/interpreter/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 2fbb84685..1cfe66531 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -233,10 +233,8 @@ where Instruction::U16ToI32 => instructions::u16_to_i32(), Instruction::S32ToI32 => instructions::s32_to_i32(), Instruction::U32ToI32 => instructions::u32_to_i32(), - Instruction::S64ToI32 => instructions::s64_to_i32(), - //Instruction::S64ToI32X - Instruction::U64ToI32 => instructions::u64_to_i32(), - //Instruction::U64ToI32X + Instruction::S64ToI32 | Instruction::S64ToI32X => instructions::s64_to_i32(), + Instruction::U64ToI32 | Instruction::U64ToI32X => instructions::u64_to_i32(), Instruction::S8ToI64 => instructions::s8_to_i64(), Instruction::U8ToI64 => instructions::u8_to_i64(), Instruction::S16ToI64 => instructions::s16_to_i64(), From d9e744d9dcae54710a5bdf3988c9badb63abeef4 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 4 Mar 2020 01:56:48 +0800 Subject: [PATCH 26/79] Resolve review comments. --- lib/runtime-core/src/trampoline_x64.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index bf821ca15..048098321 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -48,6 +48,8 @@ struct TrampBuffer { /// The allocation state of a `TrampBuffer`. struct AllocState { /// Records all allocated blocks in `buffer`. + /// + /// Maps the start address of each block to its end address. blocks: BTreeMap, } @@ -103,8 +105,6 @@ impl TrampBuffer { if self.buffer.len() - assumed_start < buf.len() { // No more free space. Cannot allocate. return None; - } else { - // Extend towards the end. } } From 469e43312ada7bbd1c08a5df9aeb9fefadcfa3a4 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 3 Mar 2020 16:16:29 -0800 Subject: [PATCH 27/79] Improve `WasmPtr` documentation --- lib/runtime-core/src/memory/ptr.rs | 77 +++++++++++++++++++++++++----- lib/runtime-core/src/types.rs | 11 ++++- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index ac381d106..a4bfc0758 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -1,4 +1,4 @@ -//! A reusable pointer abstraction for getting memory from the guest's memory. +//! Types for a reusable pointer abstraction for accessing Wasm linear memory. //! //! This abstraction is safe: it ensures the memory is in bounds and that the pointer //! is aligned (avoiding undefined behavior). @@ -12,18 +12,36 @@ use crate::{ }; use std::{cell::Cell, fmt, marker::PhantomData, mem}; -/// Array. +/// The `Array` marker type. This type can be used like `WasmPtr` +/// to get access to methods pub struct Array; -/// Item. +/// The `Item` marker type. This is the default and does not usually need to be +/// specified. pub struct Item; -/// A pointer to a Wasm item. +/// A zero-cost type that represents a pointer to something in Wasm linear +/// memory. +/// +/// This type can be used directly in the host function arguments: +/// ``` +/// # use wasmer_runtime_core::vm::Ctx; +/// # use wasmer_runtime_core::memory::ptr::WasmPtr; +/// pub fn host_import(ctx: &mut Ctx, ptr: WasmPtr) { +/// let memory = ctx.memory(0); +/// let derefed_ptr = ptr.deref(memory).expect("pointer in bounds"); +/// let inner_val: u32 = derefed_ptr.get(); +/// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); +/// // update the value being pointed to +/// derefed_ptr.set(inner_val + 1); +/// } +/// ``` #[repr(transparent)] pub struct WasmPtr { offset: u32, _phantom: PhantomData<(T, Ty)>, } +/// Methods relevant to all types of `WasmPtr`. impl WasmPtr { /// Create a new `WasmPtr` at the given offset. #[inline] @@ -34,7 +52,7 @@ impl WasmPtr { } } - /// Get the offset for this `WasmPtr`. + /// Get the offset into Wasm linear memory for this `WasmPtr`. #[inline] pub fn offset(self) -> u32 { self.offset @@ -48,8 +66,16 @@ fn align_pointer(ptr: usize, align: usize) -> usize { ptr & !(align - 1) } +/// Methods for `WasmPtr`s to data that can be dereferenced, namely to types +/// that implement [`ValueType`], meaning that they're valid for all possible +/// bit patterns. impl WasmPtr { - /// Dereference this `WasmPtr`. + /// Dereference the `WasmPtr` getting access to a `&Cell` allowing for + /// reading and mutating of the inner value. + /// + /// This method is unsound to use with unsynchronized shared memory. + /// If you're unsure what that means, it likely does not apply to you. + /// This invariant will be enforced in the future. #[inline] pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell> { if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { @@ -64,7 +90,13 @@ impl WasmPtr { } } - /// Mutable dereference this `WasmPtr`. + /// Mutably dereference this `WasmPtr` getting a `&mut Cell` allowing for + /// direct access to a `&mut T`. + /// + /// # Safety + /// - This method does not do any aliasing checks: it's possible to create + /// `&mut T` that point to the same memory. You should ensure that you have + /// exclusive access to Wasm linear memory before calling this method. #[inline] pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell> { if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { @@ -78,8 +110,16 @@ impl WasmPtr { } } +/// Methods for `WasmPtr`s to arrays of data that can be dereferenced, namely to +/// types that implement [`ValueType`], meaning that they're valid for all +/// possible bit patterns. impl WasmPtr { - /// Dereference this `WasmPtr`. + /// Dereference the `WasmPtr` getting access to a `&[Cell]` allowing for + /// reading and mutating of the inner values. + /// + /// This method is unsound to use with unsynchronized shared memory. + /// If you're unsure what that means, it likely does not apply to you. + /// This invariant will be enforced in the future. #[inline] pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell]> { // gets the size of the item in the array with padding added such that @@ -102,7 +142,13 @@ impl WasmPtr { } } - /// Mutable dereference this `WasmPtr`. + /// Mutably dereference this `WasmPtr` getting a `&mut [Cell]` allowing for + /// direct access to a `&mut [T]`. + /// + /// # Safety + /// - This method does not do any aliasing checks: it's possible to create + /// `&mut T` that point to the same memory. You should ensure that you have + /// exclusive access to Wasm linear memory before calling this method. #[inline] pub unsafe fn deref_mut( self, @@ -128,7 +174,11 @@ impl WasmPtr { Some(cell_ptrs) } - /// Get a UTF-8 string representation of this `WasmPtr` with the given length. + /// Get a UTF-8 string from the `WasmPtr` with the given length. + /// + /// Note that this method returns a reference to Wasm linear memory. The + /// underlying data can be mutated if the Wasm is allowed to execute or + /// an aliasing `WasmPtr` is used to mutate memory. pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> { if self.offset as usize + str_len as usize > memory.size().bytes().0 { return None; @@ -138,9 +188,14 @@ impl WasmPtr { std::str::from_utf8(slice).ok() } - /// Get a UTF-8 string representation of this `WasmPtr`, where the string is nul-terminated. + /// Get a UTF-8 string from the `WasmPtr`, where the string is nul-terminated. + /// /// Note that this does not account for UTF-8 strings that _contain_ nul themselves, /// [`get_utf8_string`] has to be used for those. + /// + /// Also note that this method returns a reference to Wasm linear memory. The + /// underlying data can be mutated if the Wasm is allowed to execute or + /// an aliasing `WasmPtr` is used to mutate memory. pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<&str> { memory.view::()[(self.offset as usize)..] .iter() diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index fade7accd..122aa544a 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -216,7 +216,16 @@ wasm_extern_type!(f64 => f64); // fn swap(&self, other: Self::Primitive) -> Self::Primitive; // } -/// Trait for a Value type. +/// Trait for a Value type. A Value type is a type that is always valid and may +/// be safely copied. +/// +/// That is, for all possible bit patterns a valid Value type can be constructed +/// from those bits. +/// +/// Concretely a `u32` is a Value type because every combination of 32 bits is +/// a valid `u32`. However a `bool` is _not_ a Value type because any bit patterns +/// other than `0` and `1` are invalid in Rust and may cause undefined behavior if +/// a `bool` is constructed from those bytes. pub unsafe trait ValueType: Copy where Self: Sized, From 4a6c96382d02c24d6bdeed8e7cbc86c4d0b033be Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 3 Mar 2020 17:04:52 -0800 Subject: [PATCH 28/79] Update wording of doc comment from feedback --- lib/runtime-core/src/memory/ptr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index a4bfc0758..ecc20eb11 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -73,7 +73,7 @@ impl WasmPtr { /// Dereference the `WasmPtr` getting access to a `&Cell` allowing for /// reading and mutating of the inner value. /// - /// This method is unsound to use with unsynchronized shared memory. + /// This method is unsound if used with unsynchronized shared memory. /// If you're unsure what that means, it likely does not apply to you. /// This invariant will be enforced in the future. #[inline] @@ -117,7 +117,7 @@ impl WasmPtr { /// Dereference the `WasmPtr` getting access to a `&[Cell]` allowing for /// reading and mutating of the inner values. /// - /// This method is unsound to use with unsynchronized shared memory. + /// This method is unsound if used with unsynchronized shared memory. /// If you're unsure what that means, it likely does not apply to you. /// This invariant will be enforced in the future. #[inline] From 69fd76af104007a5b2be5444ea88f70e78a2c2c2 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 4 Mar 2020 11:48:32 -0800 Subject: [PATCH 29/79] Improve support for preopened directories in WASI syscalls Also filters our default logging more and improves some debug statements --- CHANGELOG.md | 1 + lib/wasi/src/state/mod.rs | 9 +++++++ lib/wasi/src/syscalls/mod.rs | 50 ++++++++++++++++++++++++++---------- src/logging.rs | 6 ++++- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b13ac7a..29cb90abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages. - [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation. - [#1252](https://github.com/wasmerio/wasmer/pull/1252) Allow `/` in wasi `--mapdir` wasm path. - [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging: diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index f05c44b91..5acf250b8 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -1077,6 +1077,15 @@ impl WasiFs { fs_rights_inheriting: 0, }) } + VIRTUAL_ROOT_FD => { + return Ok(__wasi_fdstat_t { + fs_filetype: __WASI_FILETYPE_DIRECTORY, + fs_flags: 0, + // TODO: fix this + fs_rights_base: ALL_RIGHTS, + fs_rights_inheriting: ALL_RIGHTS, + }); + } _ => (), } let fd = self.get_fd(fd)?; diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index ea253968c..f8216afdb 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -370,8 +370,7 @@ pub fn fd_allocate( /// - `__WASI_EBADF` /// If `fd` is invalid or not open pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { - debug!("wasi::fd_close"); - debug!("=> fd={}", fd); + debug!("wasi::fd_close: fd={}", fd); let (memory, state) = get_memory_and_wasi_state(ctx, 0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); @@ -649,7 +648,7 @@ pub fn fd_pread( offset: __wasi_filesize_t, nread: WasmPtr, ) -> __wasi_errno_t { - debug!("wasi::fd_pread"); + debug!("wasi::fd_pread: fd={}, offset={}", fd, offset); let (memory, state) = get_memory_and_wasi_state(ctx, 0); let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len)); @@ -674,6 +673,10 @@ pub fn fd_pread( if !(has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) && has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK)) { + debug!( + "Invalid rights on {:X}: expected READ and SEEK", + fd_entry.rights + ); return __WASI_EACCES; } match &mut state.fs.inodes[inode].kind { @@ -699,6 +702,7 @@ pub fn fd_pread( }; nread_cell.set(bytes_read); + debug!("Success: {} bytes read", bytes_read); __WASI_ESUCCESS } @@ -971,24 +975,38 @@ pub fn fd_readdir( let mut cur_cookie = cookie; let mut buf_idx = 0; - let entries = match &state.fs.inodes[working_dir.inode].kind { - Kind::Dir { path, .. } => { + let entries: Vec<(String, u8, u64)> = match &state.fs.inodes[working_dir.inode].kind { + Kind::Dir { path, entries, .. } => { // TODO: refactor this code // we need to support multiple calls, // simple and obviously correct implementation for now: // maintain consistent order via lexacographic sorting - let mut entries = wasi_try!(wasi_try!(std::fs::read_dir(path).map_err(|_| __WASI_EIO)) - .collect::, _>>() + let fs_info = wasi_try!(wasi_try!(std::fs::read_dir(path).map_err(|_| __WASI_EIO)) + .collect::, _>>() .map_err(|_| __WASI_EIO)); - entries.sort_by(|a, b| a.file_name().cmp(&b.file_name())); - wasi_try!(entries + let mut entry_vec = wasi_try!(fs_info .into_iter() .map(|entry| Ok(( entry.file_name().to_string_lossy().to_string(), host_file_type_to_wasi_file_type(entry.file_type().map_err(|_| __WASI_EIO)?), 0, // TODO: inode ))) - .collect::, __wasi_errno_t>>()) + .collect::, _>>()); + entry_vec.extend( + entries + .iter() + .filter(|(_, inode)| state.fs.inodes[**inode].is_preopened) + .map(|(name, inode)| { + let entry = &state.fs.inodes[*inode]; + ( + format!("{}", entry.name), + entry.stat.st_filetype, + entry.stat.st_ino, + ) + }), + ); + entry_vec.sort_by(|a, b| a.0.cmp(&b.0)); + entry_vec } Kind::Root { entries } => { let sorted_entries = { @@ -1435,10 +1453,14 @@ pub fn path_filestat_get( path_string, flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )); - let stat = wasi_try!(state - .fs - .get_stat_for_kind(&state.fs.inodes[file_inode].kind) - .ok_or(__WASI_EIO)); + let stat = if state.fs.inodes[file_inode].is_preopened { + state.fs.inodes[file_inode].stat.clone() + } else { + wasi_try!(state + .fs + .get_stat_for_kind(&state.fs.inodes[file_inode].kind) + .ok_or(__WASI_EIO)) + }; let buf_cell = wasi_try!(buf.deref(memory)); buf_cell.set(stat); diff --git a/src/logging.rs b/src/logging.rs index e9f8dbb6a..0cc1530e4 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -46,7 +46,11 @@ pub fn set_up_logging() -> Result<(), String> { }) }; - base.chain(std::io::stdout()) + base + .filter(|metadata| { + metadata.target().starts_with("wasmer") + }) + .chain(std::io::stdout()) }); dispatch.apply().map_err(|e| format!("{}", e))?; From c295adbaa8a53ff290703c3b2bb637a785523195 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 4 Mar 2020 12:26:19 -0800 Subject: [PATCH 30/79] Prepare for 0.15.0 release --- CHANGELOG.md | 2 ++ Cargo.lock | 38 ++++++++++----------- Cargo.toml | 2 +- lib/clif-backend/Cargo.toml | 6 ++-- lib/dev-utils/Cargo.toml | 2 +- lib/emscripten-tests/Cargo.toml | 14 ++++---- lib/emscripten/Cargo.toml | 4 +-- lib/interface-types/Cargo.toml | 2 +- lib/llvm-backend-tests/Cargo.toml | 6 ++-- lib/llvm-backend/Cargo.toml | 4 +-- lib/middleware-common-tests/Cargo.toml | 12 +++---- lib/middleware-common/Cargo.toml | 4 +-- lib/runtime-c-api/Cargo.toml | 10 +++--- lib/runtime-core-tests/Cargo.toml | 10 +++--- lib/runtime-core/Cargo.toml | 2 +- lib/runtime/Cargo.toml | 8 ++--- lib/singlepass-backend/Cargo.toml | 4 +-- lib/spectests/Cargo.toml | 10 +++--- lib/wasi-experimental-io-devices/Cargo.toml | 6 ++-- lib/wasi-tests/Cargo.toml | 14 ++++---- lib/wasi/Cargo.toml | 4 +-- lib/win-exception-handler/Cargo.toml | 4 +-- scripts/update_version_numbers.sh | 4 +-- src/installer/wasmer.iss | 2 +- 24 files changed, 88 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29cb90abf..87e9c8b18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +## 0.15.0 - 2020-03-04 + - [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages. - [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation. - [#1252](https://github.com/wasmerio/wasmer/pull/1252) Allow `/` in wasi `--mapdir` wasm path. diff --git a/Cargo.lock b/Cargo.lock index dc9ab58bc..225649ef9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1820,7 +1820,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "0.14.1" +version = "0.15.0" dependencies = [ "atty", "byteorder", @@ -1851,7 +1851,7 @@ dependencies = [ [[package]] name = "wasmer-clif-backend" -version = "0.14.1" +version = "0.15.0" dependencies = [ "byteorder", "cranelift-codegen", @@ -1902,14 +1902,14 @@ dependencies = [ [[package]] name = "wasmer-dev-utils" -version = "0.14.1" +version = "0.15.0" dependencies = [ "libc", ] [[package]] name = "wasmer-emscripten" -version = "0.14.1" +version = "0.15.0" dependencies = [ "byteorder", "getrandom", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "wasmer-emscripten-tests" -version = "0.14.1" +version = "0.15.0" dependencies = [ "glob 0.3.0", "wabt", @@ -1936,7 +1936,7 @@ dependencies = [ [[package]] name = "wasmer-interface-types" -version = "0.14.1" +version = "0.15.0" dependencies = [ "nom", "wast", @@ -1952,7 +1952,7 @@ dependencies = [ [[package]] name = "wasmer-llvm-backend" -version = "0.14.1" +version = "0.15.0" dependencies = [ "byteorder", "cc", @@ -1983,14 +1983,14 @@ dependencies = [ [[package]] name = "wasmer-middleware-common" -version = "0.14.1" +version = "0.15.0" dependencies = [ "wasmer-runtime-core", ] [[package]] name = "wasmer-middleware-common-tests" -version = "0.14.1" +version = "0.15.0" dependencies = [ "criterion", "wabt", @@ -2003,7 +2003,7 @@ dependencies = [ [[package]] name = "wasmer-runtime" -version = "0.14.1" +version = "0.15.0" dependencies = [ "criterion", "lazy_static", @@ -2020,7 +2020,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-c-api" -version = "0.14.1" +version = "0.15.0" dependencies = [ "cbindgen", "libc", @@ -2032,7 +2032,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core" -version = "0.14.1" +version = "0.15.0" dependencies = [ "bincode", "blake3", @@ -2060,7 +2060,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core-tests" -version = "0.14.1" +version = "0.15.0" dependencies = [ "wabt", "wasmer-clif-backend", @@ -2071,7 +2071,7 @@ dependencies = [ [[package]] name = "wasmer-singlepass-backend" -version = "0.14.1" +version = "0.15.0" dependencies = [ "bincode", "byteorder", @@ -2088,7 +2088,7 @@ dependencies = [ [[package]] name = "wasmer-spectests" -version = "0.14.1" +version = "0.15.0" dependencies = [ "glob 0.3.0", "wabt", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wasmer-wasi" -version = "0.14.1" +version = "0.15.0" dependencies = [ "bincode", "byteorder", @@ -2117,7 +2117,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-experimental-io-devices" -version = "0.14.1" +version = "0.15.0" dependencies = [ "log", "minifb", @@ -2130,7 +2130,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-tests" -version = "0.14.1" +version = "0.15.0" dependencies = [ "glob 0.3.0", "wasmer-clif-backend", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "wasmer-win-exception-handler" -version = "0.14.1" +version = "0.15.0" dependencies = [ "cmake", "libc", diff --git a/Cargo.toml b/Cargo.toml index c111dc374..b3f532d57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.14.1" +version = "0.15.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 03cc445af..282f4e2d1 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } cranelift-native = "0.59.0" cranelift-codegen = "0.59.0" cranelift-entity = "0.59.0" @@ -38,7 +38,7 @@ version = "0.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } -wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.14.1" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.15.0" } [features] generate-debug-information = ["wasm-debug"] diff --git a/lib/dev-utils/Cargo.toml b/lib/dev-utils/Cargo.toml index 1d418a4cc..1c06313ba 100644 --- a/lib/dev-utils/Cargo.toml +++ b/lib/dev-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-dev-utils" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/emscripten-tests/Cargo.toml b/lib/emscripten-tests/Cargo.toml index 0b4710130..2e6e68a6f 100644 --- a/lib/emscripten-tests/Cargo.toml +++ b/lib/emscripten-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten-tests" -version = "0.14.1" +version = "0.15.0" description = "Tests for our Emscripten implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,15 +9,15 @@ publish = false build = "build/mod.rs" [dependencies] -wasmer-emscripten = { path = "../emscripten", version = "0.14.1" } -wasmer-runtime = { path = "../runtime", version = "0.14.1", default-features = false } -wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", optional = true, features = ["test"] } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true } +wasmer-emscripten = { path = "../emscripten", version = "0.15.0" } +wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false } +wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", optional = true, features = ["test"] } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } [dev-dependencies] wabt = "0.9.1" -wasmer-dev-utils = { path = "../dev-utils", version = "0.14.1"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.15.0"} [build-dependencies] glob = "0.3" diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index 3a4b790ed..25091e89b 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -15,7 +15,7 @@ lazy_static = "1.4" libc = "0.2.60" log = "0.4" time = "0.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } [target.'cfg(windows)'.dependencies] getrandom = "0.1" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index e7b46a12d..44758c1fe 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types" -version = "0.14.1" +version = "0.15.0" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/llvm-backend-tests/Cargo.toml b/lib/llvm-backend-tests/Cargo.toml index fbfda02d0..4a5e7e80c 100644 --- a/lib/llvm-backend-tests/Cargo.toml +++ b/lib/llvm-backend-tests/Cargo.toml @@ -9,8 +9,8 @@ edition = "2018" [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } -wasmer-runtime = { path = "../runtime", version = "0.14.1" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime = { path = "../runtime", version = "0.15.0" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"] } [features] diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 6236a04ca..3c2b390ab 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-llvm-backend" -version = "0.14.1" +version = "0.15.0" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" @@ -10,7 +10,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1", features = ["generate-debug-information-no-export-symbols"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0", features = ["generate-debug-information-no-export-symbols"] } wasmparser = "0.51.3" smallvec = "0.6" goblin = "0.0.24" diff --git a/lib/middleware-common-tests/Cargo.toml b/lib/middleware-common-tests/Cargo.toml index c66f51826..f83da0256 100644 --- a/lib/middleware-common-tests/Cargo.toml +++ b/lib/middleware-common-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common-tests" -version = "0.14.1" +version = "0.15.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -8,11 +8,11 @@ license = "MIT" publish = false [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } -wasmer-middleware-common = { path = "../middleware-common", version = "0.14.1" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-middleware-common = { path = "../middleware-common", version = "0.15.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } [features] clif = ["wasmer-clif-backend"] diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index 31707372f..c44fba176 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common" -version = "0.14.1" +version = "0.15.0" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime common middlewares" license = "MIT" @@ -10,4 +10,4 @@ categories = ["wasm"] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 3ba6fd94b..38c099375 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.14.1" +version = "0.15.0" description = "Wasmer C API library" documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/" license = "MIT" @@ -20,22 +20,22 @@ libc = "0.2.60" [dependencies.wasmer-runtime] default-features = false path = "../runtime" -version = "0.14.1" +version = "0.15.0" [dependencies.wasmer-runtime-core] default-features = false path = "../runtime-core" -version = "0.14.1" +version = "0.15.0" [dependencies.wasmer-wasi] default-features = false path = "../wasi" -version = "0.14.1" +version = "0.15.0" optional = true [dependencies.wasmer-emscripten] path = "../emscripten" -version = "0.14.1" +version = "0.15.0" optional = true [features] diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml index 8697caada..a97b44b08 100644 --- a/lib/runtime-core-tests/Cargo.toml +++ b/lib/runtime-core-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core-tests" -version = "0.14.1" +version = "0.15.0" description = "Tests for the Wasmer runtime core crate" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ publish = false [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } [features] default = ["backend-cranelift"] diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index b49e88fc6..b309a552d 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index f9c6ec31a..f836c9693 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,17 +11,17 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } lazy_static = "1.4" memmap = "0.7" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.14.1" +version = "0.15.0" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.14.1" +version = "0.15.0" optional = true # Dependencies for caching. diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index e86e0b8d1..b1964dc3d 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-singlepass-backend" -version = "0.14.1" +version = "0.15.0" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime single pass compiler backend" license = "MIT" @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } dynasm = "0.5" dynasmrt = "0.5" lazy_static = "1.4" diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index 7d88e3e7a..c5497c476 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.14.1" +version = "0.15.0" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ edition = "2018" [dependencies] glob = "0.3" -wasmer-runtime = { path = "../runtime", version = "0.14.1", default-features = false} -wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true } +wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false} +wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } [build-dependencies] wabt = "0.9.1" diff --git a/lib/wasi-experimental-io-devices/Cargo.toml b/lib/wasi-experimental-io-devices/Cargo.toml index d29d30379..32c8e266b 100644 --- a/lib/wasi-experimental-io-devices/Cargo.toml +++ b/lib/wasi-experimental-io-devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-experimental-io-devices" -version = "0.14.1" +version = "0.15.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -14,8 +14,8 @@ maintenance = { status = "experimental" } [dependencies] log = "0.4" minifb = "0.13" -wasmer-wasi = { version = "0.14.1", path = "../wasi" } -wasmer-runtime-core = { version = "0.14.1", path = "../runtime-core" } +wasmer-wasi = { version = "0.15.0", path = "../wasi" } +wasmer-runtime-core = { version = "0.15.0", path = "../runtime-core" } ref_thread_local = "0.0" serde = "1" typetag = "0.1" diff --git a/lib/wasi-tests/Cargo.toml b/lib/wasi-tests/Cargo.toml index e93d4d9cd..c7208be5d 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-tests" -version = "0.14.1" +version = "0.15.0" description = "Tests for our WASI implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,18 +10,18 @@ build = "build/mod.rs" [dependencies] # We set default features to false to be able to use the singlepass backend properly -wasmer-runtime = { path = "../runtime", version = "0.14.1", default-features = false } -wasmer-wasi = { path = "../wasi", version = "0.14.1" } +wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false } +wasmer-wasi = { path = "../wasi", version = "0.15.0" } # hack to get tests to work -wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true} -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true } +wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true} +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } [build-dependencies] glob = "0.3" [dev-dependencies] -wasmer-dev-utils = { path = "../dev-utils", version = "0.14.1"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.15.0"} [features] clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index ce7669475..35886c948 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime WASI implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -19,7 +19,7 @@ getrandom = "0.1" time = "0.1" typetag = "0.1" serde = { version = "1", features = ["derive"] } -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index a221815a7..0a92ae94b 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.14.1" +version = "0.15.0" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.60" diff --git a/scripts/update_version_numbers.sh b/scripts/update_version_numbers.sh index 9fd20ae24..77392a5a3 100755 --- a/scripts/update_version_numbers.sh +++ b/scripts/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.14.0' -NEXT_VERSION='0.14.1' +PREVIOUS_VERSION='0.14.1' +NEXT_VERSION='0.15.0' # quick hack fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 3bb42cacd..408a92e39 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=0.14.1 +AppVersion=0.15.0 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 From 548f8b19a2d4059a813ab7c5327e23e0f36fe868 Mon Sep 17 00:00:00 2001 From: Syrus Date: Wed, 4 Mar 2020 14:19:13 -0800 Subject: [PATCH 31/79] Disable test-capi-singlepass --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 33aedc461..cda6825ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,9 @@ before_deploy: - make release-singlepass - mkdir -p artifacts # Make capi - - make test-capi-singlepass + ## Disable capi tests for now: + ## They are failing because trampolines are not yet implemented for ARM + # - make test-capi-singlepass - make capi-singlepass - make build-capi-package - cp ./wasmer-c-api.tar.gz ./artifacts/$(./scripts/capi-name.sh) From 4d3e8ee1172cf12d34b89244900952ad64849f86 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 5 Mar 2020 14:17:04 +0100 Subject: [PATCH 32/79] test(runtime-core) Test polymorphic host functions with more types. --- lib/runtime-core-tests/tests/imports.rs | 197 +++++++++++++++++++----- 1 file changed, 162 insertions(+), 35 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index 1af995368..bb0339459 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{convert::TryInto, sync::Arc}; use wasmer_runtime_core::{ compile_with, error::RuntimeError, @@ -12,10 +12,11 @@ use wasmer_runtime_core::{ use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; macro_rules! call_and_assert { - ($instance:ident, $function:ident, $expected_value:expr) => { - let $function: Func = $instance.func(stringify!($function)).unwrap(); + ($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => { + #[allow(unused_parens)] + let $function: Func<( $( $inputs ),* ), $output> = $instance.func(stringify!($function)).expect(concat!("Failed to get the `", stringify!($function), "` export function.")); - let result = $function.call(1); + let result = $function.call( $( $arguments ),* ); match (result, $expected_value) { (Ok(value), expected_value) => assert_eq!( @@ -75,7 +76,12 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { (import "env" "memory" (memory 1 1)) (import "env" "callback_fn" (func $callback_fn (type $type))) (import "env" "callback_closure" (func $callback_closure (type $type))) - (import "env" "callback_closure_dynamic" (func $callback_closure_dynamic (type $type))) + (import "env" "callback_fn_dynamic" (func $callback_fn_dynamic (type $type))) + (import "env" "callback_closure_dynamic_0" (func $callback_closure_dynamic_0)) + (import "env" "callback_closure_dynamic_1" (func $callback_closure_dynamic_1 (param i32) (result i32))) + (import "env" "callback_closure_dynamic_2" (func $callback_closure_dynamic_2 (param i32 i64) (result i64))) + (import "env" "callback_closure_dynamic_3" (func $callback_closure_dynamic_3 (param i32 i64 f32) (result f32))) + (import "env" "callback_closure_dynamic_4" (func $callback_closure_dynamic_4 (param i32 i64 f32 f64) (result f64))) (import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type))) (import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type))) (import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type))) @@ -94,9 +100,34 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { get_local 0 call $callback_closure) - (func (export "function_closure_dynamic") (type $type) + (func (export "function_fn_dynamic") (type $type) get_local 0 - call $callback_closure_dynamic) + call $callback_fn_dynamic) + + (func (export "function_closure_dynamic_0") + call $callback_closure_dynamic_0) + + (func (export "function_closure_dynamic_1") (param i32) (result i32) + get_local 0 + call $callback_closure_dynamic_1) + + (func (export "function_closure_dynamic_2") (param i32 i64) (result i64) + get_local 0 + get_local 1 + call $callback_closure_dynamic_2) + + (func (export "function_closure_dynamic_3") (param i32 i64 f32) (result f32) + get_local 0 + get_local 1 + get_local 2 + call $callback_closure_dynamic_3) + + (func (export "function_closure_dynamic_4") (param i32 i64 f32 f64) (result f64) + get_local 0 + get_local 1 + get_local 2 + get_local 3 + call $callback_closure_dynamic_4) (func (export "function_closure_with_env") (type $type) get_local 0 @@ -154,13 +185,77 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { Ok(n + 1) }), - "callback_closure_dynamic" => DynamicFunc::new( + // Regular polymorphic function. + "callback_fn_dynamic" => DynamicFunc::new( Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])), - |_, params| -> Vec { - match params[0] { - Value::I32(x) => vec![Value::I32(x + 1)], - _ => unreachable!() - } + callback_fn_dynamic, + ), + + // Polymorphic closure “closures”. + "callback_closure_dynamic_0" => DynamicFunc::new( + Arc::new(FuncSig::new(vec![], vec![])), + |_, inputs: &[Value]| -> Vec { + assert!(inputs.is_empty()); + + vec![] + } + ), + "callback_closure_dynamic_1" => DynamicFunc::new( + Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])), + move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec { + assert_eq!(inputs.len(), 1); + + let memory = vmctx.memory(0); + let shift_ = shift + memory.view::()[0].get(); + let n: i32 = (&inputs[0]).try_into().unwrap(); + + vec![Value::I32(shift_ + n)] + } + ), + "callback_closure_dynamic_2" => DynamicFunc::new( + Arc::new(FuncSig::new(vec![Type::I32, Type::I64], vec![Type::I64])), + move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec { + assert_eq!(inputs.len(), 2); + + let memory = vmctx.memory(0); + let shift_ = shift + memory.view::()[0].get(); + let i: i32 = (&inputs[0]).try_into().unwrap(); + let j: i64 = (&inputs[1]).try_into().unwrap(); + + vec![Value::I64(shift_ as i64 + i as i64 + j)] + } + ), + "callback_closure_dynamic_3" => DynamicFunc::new( + Arc::new(FuncSig::new(vec![Type::I32, Type::I64, Type::F32], vec![Type::F32])), + move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec { + assert_eq!(inputs.len(), 3); + + let memory = vmctx.memory(0); + let shift_ = shift + memory.view::()[0].get(); + let i: i32 = (&inputs[0]).try_into().unwrap(); + let j: i64 = (&inputs[1]).try_into().unwrap(); + let k: f32 = (&inputs[2]).try_into().unwrap(); + + dbg!(i); + dbg!(j); + dbg!(k); + + vec![Value::F32(shift_ as f32 + i as f32 + j as f32 + k)] + } + ), + "callback_closure_dynamic_4" => DynamicFunc::new( + Arc::new(FuncSig::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![Type::F64])), + move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec { + assert_eq!(inputs.len(), 4); + + let memory = vmctx.memory(0); + let shift_ = shift + memory.view::()[0].get(); + let i: i32 = (&inputs[0]).try_into().unwrap(); + let j: i64 = (&inputs[1]).try_into().unwrap(); + let k: f32 = (&inputs[2]).try_into().unwrap(); + let l: f64 = (&inputs[3]).try_into().unwrap(); + + vec![Value::F64(shift_ as f64 + i as f64 + j as f64 + k as f64 + l)] } ), @@ -227,6 +322,13 @@ fn callback_fn(n: i32) -> Result { Ok(n + 1) } +fn callback_fn_dynamic(_: &mut vm::Ctx, inputs: &[Value]) -> Vec { + match inputs[0] { + Value::I32(x) => vec![Value::I32(x + 1)], + _ => unreachable!(), + } +} + fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { let memory = vmctx.memory(0); let shift_: i32 = memory.view()[0].get(); @@ -246,57 +348,82 @@ fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { + ($test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => { #[test] fn $test_name() { imported_functions_forms(&|instance| { - call_and_assert!(instance, $function, $expected_value); + call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value); }); } }; } -test!(test_fn, function_fn, Ok(2)); -test!(test_closure, function_closure, Ok(2)); -test!(test_closure_dynamic, function_closure_dynamic, Ok(2)); +test!(test_fn, function_fn(i32) -> i32, (1) == Ok(2)); +test!(test_closure, function_closure(i32) -> i32, (1) == Ok(2)); +test!(test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2)); +test!( + test_closure_dynamic_0, + function_closure_dynamic_0(()) -> (), + () == Ok(()) +); +test!( + test_closure_dynamic_1, + function_closure_dynamic_1(i32) -> i32, + (1) == Ok(1 + shift + SHIFT) +); +test!( + test_closure_dynamic_2, + function_closure_dynamic_2(i32, i64) -> i64, + (1, 2) == Ok(1 + 2 + shift as i64 + SHIFT as i64) +); +test!( + test_closure_dynamic_3, + function_closure_dynamic_3(i32, i64, f32) -> f32, + (1, 2, 3.) == Ok(1. + 2. + 3. + shift as f32 + SHIFT as f32) +); +test!( + test_closure_dynamic_4, + function_closure_dynamic_4(i32, i64, f32, f64) -> f64, + (1, 2, 3., 4.) == Ok(1. + 2. + 3. + 4. + shift as f64 + SHIFT as f64) +); test!( test_closure_with_env, - function_closure_with_env, - Ok(2 + shift + SHIFT) + function_closure_with_env(i32) -> i32, + (1) == Ok(2 + shift + SHIFT) ); -test!(test_fn_with_vmctx, function_fn_with_vmctx, Ok(2 + SHIFT)); +test!(test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT)); test!( test_closure_with_vmctx, - function_closure_with_vmctx, - Ok(2 + SHIFT) + function_closure_with_vmctx(i32) -> i32, + (1) == Ok(2 + SHIFT) ); test!( test_closure_with_vmctx_and_env, - function_closure_with_vmctx_and_env, - Ok(2 + shift + SHIFT) + function_closure_with_vmctx_and_env(i32) -> i32, + (1) == Ok(2 + shift + SHIFT) ); test!( test_fn_trap, - function_fn_trap, - Err(RuntimeError(Box::new(format!("foo {}", 2)))) + function_fn_trap(i32) -> i32, + (1) == Err(RuntimeError(Box::new(format!("foo {}", 2)))) ); test!( test_closure_trap, - function_closure_trap, - Err(RuntimeError(Box::new(format!("bar {}", 2)))) + function_closure_trap(i32) -> i32, + (1) == Err(RuntimeError(Box::new(format!("bar {}", 2)))) ); test!( test_fn_trap_with_vmctx, - function_fn_trap_with_vmctx, - Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT)))) + function_fn_trap_with_vmctx(i32) -> i32, + (1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT)))) ); test!( test_closure_trap_with_vmctx, - function_closure_trap_with_vmctx, - Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT)))) + function_closure_trap_with_vmctx(i32) -> i32, + (1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT)))) ); test!( test_closure_trap_with_vmctx_and_env, - function_closure_trap_with_vmctx_and_env, - Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT)))) + function_closure_trap_with_vmctx_and_env(i32) -> i32, + (1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT)))) ); From a5de17fb1897afb4619d078dfa10d72b0fb96748 Mon Sep 17 00:00:00 2001 From: losfair Date: Thu, 5 Mar 2020 22:49:58 +0800 Subject: [PATCH 33/79] runtime-core: Correctly allocate floating point registers for trampolines. --- lib/runtime-core/src/state.rs | 29 +++++ lib/runtime-core/src/trampoline_x64.rs | 140 +++++++++++++++++++++---- lib/runtime-core/src/typed_func.rs | 6 +- 3 files changed, 152 insertions(+), 23 deletions(-) diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 1dfcae813..11cdc4ccf 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -610,6 +610,35 @@ pub mod x64_decl { _ => return None, }) } + + /// Returns the instruction prefix for `movq %this_reg, ?(%rsp)`. + /// + /// To build a instruction, append the memory location as a 32-bit + /// offset to the stack pointer to this prefix. + pub fn prefix_mov_to_stack(&self) -> Option<&'static [u8]> { + Some(match *self { + X64Register::GPR(gpr) => match gpr { + GPR::RDI => &[0x48, 0x89, 0xbc, 0x24], + GPR::RSI => &[0x48, 0x89, 0xb4, 0x24], + GPR::RDX => &[0x48, 0x89, 0x94, 0x24], + GPR::RCX => &[0x48, 0x89, 0x8c, 0x24], + GPR::R8 => &[0x4c, 0x89, 0x84, 0x24], + GPR::R9 => &[0x4c, 0x89, 0x8c, 0x24], + _ => return None, + }, + X64Register::XMM(xmm) => match xmm { + XMM::XMM0 => &[0x66, 0x0f, 0xd6, 0x84, 0x24], + XMM::XMM1 => &[0x66, 0x0f, 0xd6, 0x8c, 0x24], + XMM::XMM2 => &[0x66, 0x0f, 0xd6, 0x94, 0x24], + XMM::XMM3 => &[0x66, 0x0f, 0xd6, 0x9c, 0x24], + XMM::XMM4 => &[0x66, 0x0f, 0xd6, 0xa4, 0x24], + XMM::XMM5 => &[0x66, 0x0f, 0xd6, 0xac, 0x24], + XMM::XMM6 => &[0x66, 0x0f, 0xd6, 0xb4, 0x24], + XMM::XMM7 => &[0x66, 0x0f, 0xd6, 0xbc, 0x24], + _ => return None, + }, + }) + } } } diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 048098321..6ace06076 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -7,6 +7,8 @@ //! Variadic functions are not supported because `rax` is used by the trampoline code. use crate::loader::CodeMemory; +use crate::state::x64_decl::{X64Register, GPR, XMM}; +use crate::types::Type; use crate::vm::Ctx; use std::collections::BTreeMap; use std::fmt; @@ -145,6 +147,53 @@ pub struct TrampolineBuffer { offsets: Vec, } +#[derive(Default)] +struct ArgumentRegisterAllocator { + n_gprs: usize, + n_xmms: usize, +} + +impl ArgumentRegisterAllocator { + fn next(&mut self, ty: Type) -> Option { + static GPR_SEQ: &'static [GPR] = + &[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; + static XMM_SEQ: &'static [XMM] = &[ + XMM::XMM0, + XMM::XMM1, + XMM::XMM2, + XMM::XMM3, + XMM::XMM4, + XMM::XMM5, + XMM::XMM6, + XMM::XMM7, + ]; + match ty { + Type::I32 | Type::I64 => { + if self.n_gprs < GPR_SEQ.len() { + let gpr = GPR_SEQ[self.n_gprs]; + self.n_gprs += 1; + Some(X64Register::GPR(gpr)) + } else { + None + } + } + Type::F32 | Type::F64 => { + if self.n_xmms < XMM_SEQ.len() { + let xmm = XMM_SEQ[self.n_xmms]; + self.n_xmms += 1; + Some(X64Register::XMM(xmm)) + } else { + None + } + } + _ => todo!( + "ArgumentRegisterAllocator::next: Unsupported type: {:?}", + ty + ), + } + } +} + fn value_to_bytes(ptr: &T) -> &[u8] { unsafe { slice::from_raw_parts(ptr as *const T as *const u8, mem::size_of::()) } } @@ -246,44 +295,49 @@ impl TrampolineBufferBuilder { &mut self, target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64, context: *const CallContext, - num_params: u32, + params: &[Type], ) -> usize { let idx = self.offsets.len(); self.offsets.push(self.code.len()); - let mut stack_offset: u32 = num_params.checked_mul(8).unwrap(); + let mut stack_offset: u32 = params.len().checked_mul(8).unwrap() as u32; if stack_offset % 16 == 0 { stack_offset += 8; } self.code.extend_from_slice(&[0x48, 0x81, 0xec]); // sub ?, %rsp self.code.extend_from_slice(value_to_bytes(&stack_offset)); - for i in 0..num_params { - match i { - 0..=5 => { - // mov %?, ?(%rsp) - let prefix: &[u8] = match i { - 0 => &[0x48, 0x89, 0xbc, 0x24], // rdi - 1 => &[0x48, 0x89, 0xb4, 0x24], // rsi - 2 => &[0x48, 0x89, 0x94, 0x24], // rdx - 3 => &[0x48, 0x89, 0x8c, 0x24], // rcx - 4 => &[0x4c, 0x89, 0x84, 0x24], // r8 - 5 => &[0x4c, 0x89, 0x8c, 0x24], // r9 - _ => unreachable!(), - }; + + let mut allocator = ArgumentRegisterAllocator::default(); + + let mut source_stack_count: u32 = 0; // # of allocated slots in the source stack. + + for (i, ty) in params.iter().enumerate() { + match allocator.next(*ty) { + Some(reg) => { + // This argument is allocated to a register. + + let prefix = reg + .prefix_mov_to_stack() + .expect("cannot get instruction prefix for argument register"); self.code.extend_from_slice(prefix); - self.code.extend_from_slice(value_to_bytes(&(i * 8u32))); + self.code + .extend_from_slice(value_to_bytes(&((i as u32) * 8u32))); } - _ => { + None => { + // This argument is allocated to the stack. + self.code.extend_from_slice(&[ 0x48, 0x8b, 0x84, 0x24, // mov ?(%rsp), %rax ]); self.code.extend_from_slice(value_to_bytes( - &((i - 6) * 8u32 + stack_offset + 8/* ret addr */), + &(source_stack_count * 8u32 + stack_offset + 8/* ret addr */), )); // mov %rax, ?(%rsp) self.code.extend_from_slice(&[0x48, 0x89, 0x84, 0x24]); - self.code.extend_from_slice(value_to_bytes(&(i * 8u32))); + self.code + .extend_from_slice(value_to_bytes(&((i as u32) * 8u32))); + source_stack_count += 1; } } } @@ -395,8 +449,12 @@ mod tests { } let mut builder = TrampolineBufferBuilder::new(); let ctx = TestContext { value: 100 }; - let idx = - builder.add_callinfo_trampoline(do_add, &ctx as *const TestContext as *const _, 8); + let param_types: Vec = (0..8).map(|_| Type::I32).collect(); + let idx = builder.add_callinfo_trampoline( + do_add, + &ctx as *const TestContext as *const _, + ¶m_types, + ); let buf = builder.build(); let t = buf.get_trampoline(idx); let ret = unsafe { @@ -407,6 +465,43 @@ mod tests { assert_eq!(ret, 136); } + #[test] + fn test_trampolines_with_floating_point() { + unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 { + let n = n as usize; + let mut result: u64 = 0; + for i in 0..n { + result += *args.offset(i as _); + } + result + } + let buffer = TrampBuffer::new(4096); + let mut builder = TrampolineBufferBuilder::new(); + builder.add_callinfo_trampoline( + inner, + 8 as _, + &[ + Type::I32, + Type::I32, + Type::I32, + Type::F32, + Type::I32, + Type::I32, + Type::I32, + Type::I32, + ], + ); + let ptr = buffer.insert(builder.code()).unwrap(); + let ret = unsafe { + let f = std::mem::transmute::< + _, + extern "C" fn(i32, i32, i32, f32, i32, i32, i32, i32) -> i32, + >(ptr); + f(1, 2, 3, f32::from_bits(4), 5, 6, 7, 8) + }; + assert_eq!(ret, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8); + } + #[test] fn test_many_global_trampolines() { unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 { @@ -427,7 +522,8 @@ mod tests { for i in 0..5000usize { let mut builder = TrampolineBufferBuilder::new(); let n = i % 8; - builder.add_callinfo_trampoline(inner, n as _, n as _); + let param_types: Vec<_> = (0..n).map(|_| Type::I32).collect(); + builder.add_callinfo_trampoline(inner, n as _, ¶m_types); let ptr = buffer .insert(builder.code()) .expect("cannot insert new code into global buffer"); diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 0b29d54ea..4dc192b0b 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -360,10 +360,14 @@ impl<'a> DynamicFunc<'a> { func: Box::new(func), }); let ctx = Box::into_raw(ctx); + + let mut native_param_types = vec![Type::I32]; // vm::Ctx is the first parameter. + native_param_types.extend_from_slice(signature.params()); + builder.add_callinfo_trampoline( enter_host_polymorphic, ctx as *const _, - (signature.params().len() + 1) as u32, // +vmctx + &native_param_types, ); let ptr = builder .insert_global() From e62095da5d966681eccfd07676a04dc0b4b677f6 Mon Sep 17 00:00:00 2001 From: losfair Date: Fri, 6 Mar 2020 00:37:48 +0800 Subject: [PATCH 34/79] runtime-core: Move ArgumentRegisterAllocator into `state`. --- lib/runtime-core/src/state.rs | 53 +++++++++++++++++++++++++- lib/runtime-core/src/trampoline_x64.rs | 49 +----------------------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 11cdc4ccf..fb19476b1 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -480,10 +480,11 @@ impl InstanceImage { } } -/// Declarations for x86-64 registers. +/// X64-specific structures and methods that do not depend on an x64 machine to run. #[cfg(unix)] pub mod x64_decl { use super::*; + use crate::types::Type; /// General-purpose registers. #[repr(u8)] @@ -640,8 +641,58 @@ pub mod x64_decl { }) } } + + /// An allocator that allocates registers for function arguments according to the System V ABI. + #[derive(Default)] + pub struct ArgumentRegisterAllocator { + n_gprs: usize, + n_xmms: usize, + } + + impl ArgumentRegisterAllocator { + /// Allocates a register for argument type `ty`. Returns `None` if no register is available for this type.. + pub fn next(&mut self, ty: Type) -> Option { + static GPR_SEQ: &'static [GPR] = + &[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; + static XMM_SEQ: &'static [XMM] = &[ + XMM::XMM0, + XMM::XMM1, + XMM::XMM2, + XMM::XMM3, + XMM::XMM4, + XMM::XMM5, + XMM::XMM6, + XMM::XMM7, + ]; + match ty { + Type::I32 | Type::I64 => { + if self.n_gprs < GPR_SEQ.len() { + let gpr = GPR_SEQ[self.n_gprs]; + self.n_gprs += 1; + Some(X64Register::GPR(gpr)) + } else { + None + } + } + Type::F32 | Type::F64 => { + if self.n_xmms < XMM_SEQ.len() { + let xmm = XMM_SEQ[self.n_xmms]; + self.n_xmms += 1; + Some(X64Register::XMM(xmm)) + } else { + None + } + } + _ => todo!( + "ArgumentRegisterAllocator::next: Unsupported type: {:?}", + ty + ), + } + } + } } +/// X64-specific structures and methods that only work on an x64 machine. #[cfg(unix)] pub mod x64 { //! The x64 state module contains functions to generate state and code for x64 targets. diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 6ace06076..24fc4f385 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -7,7 +7,7 @@ //! Variadic functions are not supported because `rax` is used by the trampoline code. use crate::loader::CodeMemory; -use crate::state::x64_decl::{X64Register, GPR, XMM}; +use crate::state::x64_decl::ArgumentRegisterAllocator; use crate::types::Type; use crate::vm::Ctx; use std::collections::BTreeMap; @@ -147,53 +147,6 @@ pub struct TrampolineBuffer { offsets: Vec, } -#[derive(Default)] -struct ArgumentRegisterAllocator { - n_gprs: usize, - n_xmms: usize, -} - -impl ArgumentRegisterAllocator { - fn next(&mut self, ty: Type) -> Option { - static GPR_SEQ: &'static [GPR] = - &[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; - static XMM_SEQ: &'static [XMM] = &[ - XMM::XMM0, - XMM::XMM1, - XMM::XMM2, - XMM::XMM3, - XMM::XMM4, - XMM::XMM5, - XMM::XMM6, - XMM::XMM7, - ]; - match ty { - Type::I32 | Type::I64 => { - if self.n_gprs < GPR_SEQ.len() { - let gpr = GPR_SEQ[self.n_gprs]; - self.n_gprs += 1; - Some(X64Register::GPR(gpr)) - } else { - None - } - } - Type::F32 | Type::F64 => { - if self.n_xmms < XMM_SEQ.len() { - let xmm = XMM_SEQ[self.n_xmms]; - self.n_xmms += 1; - Some(X64Register::XMM(xmm)) - } else { - None - } - } - _ => todo!( - "ArgumentRegisterAllocator::next: Unsupported type: {:?}", - ty - ), - } - } -} - fn value_to_bytes(ptr: &T) -> &[u8] { unsafe { slice::from_raw_parts(ptr as *const T as *const u8, mem::size_of::()) } } From 1c6424a754c30b61b2eb877c7ee116598df4b282 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 5 Mar 2020 13:23:50 -0800 Subject: [PATCH 35/79] Fix WasmPtr to work with accesses accessing the final valid byte This also returns `None` for all accesses of zero sized types and arrays of length 0. Because the array accesses have a non-inclusive length, length of 0 is not valid. These checks prevent returning empty slices that point just outside of memory bounds. --- lib/runtime-core/src/memory/ptr.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index ecc20eb11..5668f8240 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -78,7 +78,9 @@ impl WasmPtr { /// This invariant will be enforced in the future. #[inline] pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell> { - if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { + if (self.offset as usize) + mem::size_of::() > memory.size().bytes().0 + || mem::size_of::() == 0 + { return None; } unsafe { @@ -99,7 +101,9 @@ impl WasmPtr { /// exclusive access to Wasm linear memory before calling this method. #[inline] pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell> { - if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { + if (self.offset as usize) + mem::size_of::() > memory.size().bytes().0 + || mem::size_of::() == 0 + { return None; } let cell_ptr = align_pointer( @@ -127,7 +131,10 @@ impl WasmPtr { let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); let slice_full_len = index as usize + length as usize; - if (self.offset as usize) + (item_size * slice_full_len) >= memory.size().bytes().0 { + if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0 + || length == 0 + || mem::size_of::() == 0 + { return None; } @@ -161,7 +168,10 @@ impl WasmPtr { let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); let slice_full_len = index as usize + length as usize; - if (self.offset as usize) + (item_size * slice_full_len) >= memory.size().bytes().0 { + if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0 + || length == 0 + || mem::size_of::() == 0 + { return None; } From eb4c1bdace9451bd168e3fa3f917c7c7bc1323b1 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 5 Mar 2020 13:31:37 -0800 Subject: [PATCH 36/79] Prevent 0 length utf8 strings from WasmPtr too --- lib/runtime-core/src/memory/ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index 5668f8240..8e8a6cd8a 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -190,7 +190,7 @@ impl WasmPtr { /// underlying data can be mutated if the Wasm is allowed to execute or /// an aliasing `WasmPtr` is used to mutate memory. pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> { - if self.offset as usize + str_len as usize > memory.size().bytes().0 { + if self.offset as usize + str_len as usize > memory.size().bytes().0 || str_len == 0 { return None; } let ptr = unsafe { memory.view::().as_ptr().add(self.offset as usize) as *const u8 }; From f8d34e0b5771e6f69691d001464c1780b45bcbd5 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 5 Mar 2020 15:06:00 -0800 Subject: [PATCH 37/79] Add WasmPtr access edge case test --- lib/runtime-core/src/memory/ptr.rs | 91 ++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index 8e8a6cd8a..f9c798af1 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -255,3 +255,94 @@ impl fmt::Debug for WasmPtr { write!(f, "WasmPtr({:#x})", self.offset) } } + +#[cfg(test)] +mod test { + use super::*; + use crate::memory; + use crate::units::Pages; + + /// Ensure that memory accesses work on the edges of memory and that out of + /// bounds errors are caught with both `deref` and `deref_mut`. + #[test] + fn wasm_ptr_memory_bounds_checks_hold() { + // create a memory + let memory_descriptor = + memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap(); + let memory = memory::Memory::new(memory_descriptor).unwrap(); + + // test that basic access works and that len = 0 is caught correctly + let start_wasm_ptr: WasmPtr = WasmPtr::new(0); + let start_wasm_ptr_array: WasmPtr = WasmPtr::new(0); + + assert!(start_wasm_ptr.deref(&memory).is_some()); + assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() }); + assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_none()); + assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_none()); + assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_none() }); + assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some()); + assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() }); + + // test that accessing the last valid memory address works correctly and OOB is caught + let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32; + let end_wasm_ptr: WasmPtr = WasmPtr::new(last_valid_address_for_u8); + assert!(end_wasm_ptr.deref(&memory).is_some()); + assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() }); + + let end_wasm_ptr_array: WasmPtr = WasmPtr::new(last_valid_address_for_u8); + + assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some()); + assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() }); + let invalid_idx_len_combos: [(u32, u32); 3] = [(0, 0), (0, 2), (1, 1)]; + for &(idx, len) in invalid_idx_len_combos.into_iter() { + assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none()); + assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() }); + } + assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none()); + + // test that accesing the last valid memory address for a u32 is valid + // (same as above test but with more edge cases to assert on) + let last_valid_address_for_u32 = (memory.size().bytes().0 - 4) as u32; + let end_wasm_ptr: WasmPtr = WasmPtr::new(last_valid_address_for_u32); + assert!(end_wasm_ptr.deref(&memory).is_some()); + assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() }); + assert!(end_wasm_ptr.deref(&memory).is_some()); + assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() }); + + let end_wasm_ptr_oob_array: [WasmPtr; 4] = [ + WasmPtr::new(last_valid_address_for_u32 + 1), + WasmPtr::new(last_valid_address_for_u32 + 2), + WasmPtr::new(last_valid_address_for_u32 + 3), + WasmPtr::new(last_valid_address_for_u32 + 4), + ]; + for oob_end_ptr in end_wasm_ptr_oob_array.into_iter() { + assert!(oob_end_ptr.deref(&memory).is_none()); + assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() }); + } + let end_wasm_ptr_array: WasmPtr = WasmPtr::new(last_valid_address_for_u32); + assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some()); + assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() }); + + let invalid_idx_len_combos: [(u32, u32); 4] = [(0, 0), (1, 0), (0, 2), (1, 1)]; + for &(idx, len) in invalid_idx_len_combos.into_iter() { + assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none()); + assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() }); + } + + let end_wasm_ptr_array_oob_array: [WasmPtr; 4] = [ + WasmPtr::new(last_valid_address_for_u32 + 1), + WasmPtr::new(last_valid_address_for_u32 + 2), + WasmPtr::new(last_valid_address_for_u32 + 3), + WasmPtr::new(last_valid_address_for_u32 + 4), + ]; + + for oob_end_array_ptr in end_wasm_ptr_array_oob_array.into_iter() { + assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none()); + assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() }); + assert!(oob_end_array_ptr.deref(&memory, 0, 0).is_none()); + assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 0).is_none() }); + assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none()); + assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() }); + } + } +} From 8a8b3a43acafafc2dfa98aff623b58fb70c0fdc5 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 5 Mar 2020 15:45:54 -0800 Subject: [PATCH 38/79] Add `WasmPtr` bug fix to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e9c8b18..0856e49dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` + ## 0.15.0 - 2020-03-04 - [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages. From 4b99a41e17236e2a51ffce5dfbd541dc04869818 Mon Sep 17 00:00:00 2001 From: Syrus Date: Thu, 5 Mar 2020 17:22:16 -0800 Subject: [PATCH 39/79] Added support for wapm wax --- src/installer/wasmer.iss | 3 +++ src/installer/wax.cmd | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 src/installer/wax.cmd diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 408a92e39..390f90a46 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -23,6 +23,9 @@ Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName: "WASMER_CACHE_DI [Files] Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin" +{ The cmd shortcut for wax } +Source: "wax.cmd"; DestDir: "{app}\bin" +{ Other ways of doing it: https://superuser.com/questions/193000/running-programs-by-typing-some-alias-in-windows } [Dirs] Name: "{%USERPROFILE}\.wasmer" diff --git a/src/installer/wax.cmd b/src/installer/wax.cmd new file mode 100644 index 000000000..efe9eaffb --- /dev/null +++ b/src/installer/wax.cmd @@ -0,0 +1,2 @@ +@echo off +wapm.exe execute %* From d1f79ae1e860e31c3818b86f9862a8d091f14179 Mon Sep 17 00:00:00 2001 From: Syrus Date: Thu, 5 Mar 2020 18:05:51 -0800 Subject: [PATCH 40/79] Fixed comments --- src/installer/wasmer.iss | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 390f90a46..299d638a4 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -23,9 +23,7 @@ Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName: "WASMER_CACHE_DI [Files] Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin" -{ The cmd shortcut for wax } Source: "wax.cmd"; DestDir: "{app}\bin" -{ Other ways of doing it: https://superuser.com/questions/193000/running-programs-by-typing-some-alias-in-windows } [Dirs] Name: "{%USERPROFILE}\.wasmer" From bd9aae1ed53a407449ff022f144f9ca1825975dd Mon Sep 17 00:00:00 2001 From: Syrus Date: Fri, 6 Mar 2020 12:17:01 -0800 Subject: [PATCH 41/79] Updated Wasmer icons --- src/installer/media/wizard_logo.ico | Bin 30138 -> 22142 bytes src/installer/media/wizard_logo_2.bmp | Bin 980726 -> 1305578 bytes src/installer/media/wizard_logo_small.bmp | Bin 4158 -> 12238 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/installer/media/wizard_logo.ico b/src/installer/media/wizard_logo.ico index 664b0d7a16bf2c6388add01b22ff370850e25aa5..80d69b0558df39ca5b7ab62c121b31fb0333a1ee 100644 GIT binary patch literal 22142 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 A_y7O^ literal 30138 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 z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U yAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0Rlr8`2PWz45niM literal 980726 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 z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb z2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$## zAOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz z00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb z2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$## zAOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz z00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb z2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$## zAOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz z00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb z2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$## zAOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z g1Rwwb2tWV=5P$##AOHafKmY;|fB*y_kSYTIAA~+r82|tP diff --git a/src/installer/media/wizard_logo_small.bmp b/src/installer/media/wizard_logo_small.bmp index 9ca204fcabda77087321ab8256b6d1887220c6ec..03d6e4dec76dbb1a860092dac344b0f7a451ff94 100644 GIT binary patch literal 12238 zcmeI2U2GIp6vvlfO!U?GYIrclHxqgA#Ro!+2&EQkYmGvR8ds4B1!6)_Oa&{76rL1o z)F5gUqYr{2CRz{+n#f0NG!)J5cG+dw?S7Q*NB4V=^Iv+VGqXE0ciNp@x7m|!&wSiD z=ltfLduR9DS>Ar}K1W&hN&jD!J`c=%7CDwU7E9X?&77Au6+#Zj%|9HYM1jUYay^rU%Es-=SCG%v$N#_ z0lMD5yDC$&vK3P)ihlGlx!T$dxtb{_OQjOUPyR^bJx`i4S?#x+{~D#vU#Q^>AZ{xC z*WcvZv{{%*l&zRdQRIUU$+fDTVn;p`52yw$^Pvd}4(zqeWX4u17HR6l59H~7LbhEm zwqhnj(IcOdyM2x9o8z^jtKHt|(NXek+*Dz=9&Cx>Q#Ah6`U>Axn7FNyEf)+@aL<6Q zT|3z_SFVu1Z@aEZtMOWC&wl62mXF0~V({+mx<$J+eVt(+M(I6RW^6m<1IG6c{6U^K}|C;G9uM!jq3^EZZuo)&9^A=^Dz0hY@O`~Y0V~bXo!l*BqI@+ zvYv7BZg_UK-OT!jUjH?c4RvysoK)LEjDc5n5!Tw2Qi#Xt`s;gynV7XvZ2p(F5Z0lT zqOEb4@_CAVdDJpfquGpVFUY^tALq%{v3AZ};Jb0`xP0sCIhPcs;<8`f029_IFfa1G zpNqcw-1C&WaFNZHb-maEJ9eu4Vh8I~p?&-3#pHRcn{Ir2OqJ`nU(@++N{FdrUc>!xq*glfV70GK*(?{HNja+MD)zk$sghogLUgtrj&~GZA6K zI4Wf;`2b0>Bb_+?i_vM-IT1F@F?v#%oGd@@_2Lyhj&W0K5n-GB z`kM;37`xHKpUkVDm^-xp0AcN^)?&owUEfQIGry`_BL5Oneb~LaW8PetkFZ8)msDfJ zx-sU@Vm4dh*87b3cV$m!clnv2U5CaRF4l7G7A+gbRz4EZb<25Vrm_Bwu?WF4746a2 zSaUC?)4KL+d4k#eoBJqzdCb(b857QX)^<@o9F}cijt%X7oARNMe5@WD`0a^j62_5; zh_E4_n^8pA@O`@(#f%MON?C(wCPpJRyu*ig`I=R#vEf}yymQ&?qGZE6v3NIDTbpQB Z#xk2!2g5tec$ax03Enl=*1_t@_aE%9msJ1& literal 4158 zcmeI#c~n$Y90%}cw%BHyG}>yGWviwnGE9IYfwGDsn}Qpnfk+ApRN#^zR4l1!3JCbl z5s5HkF)J5NS(1PW0v*vnITal^!Z2*ZY~SuM%$xg$H{kR~e|gXO&HLSZ-)DaB-M5?> zsfYc;%wU^+C&Nr5naxm)VaDkv$mhB7`e4S84v8bv9gsiW5&1KvD3~!9g)^i$I!lUU zGo5hM+6e_V&M35VMWMtEMGl@QndOW!YZnsp@vZd&oSyBDGbGO1c;Lr5UijJ88|Q5o zp+dYEzmmA13wvKwNqkT(S%MmSKOB>K>Pv^GWVP38dNWfM2&AaY6By2IcO6uFONb!*|xf_ z#6Kk3-B+Q*GX$L^WL|3^TNnzNPZ+xV)}zZm0$o8HQ63(H^7XN}ydoO)A+cx(i^uN~ zTk%s=0_3Y>AP@nsMGxs$CZ?k@ zDFan0yKymfAFgf7K+WDPTu;kJXL>ezcJ4uM%6{~wX5vQ1A=DknLtQ3|hMau-nN@(k zJ$dNcdl>yWg}8F02v@%<#1hm{J&)F3E})~l0?P99P@bzqTXhY(s%p`7u@1@$HBeR7qWw}GX8K|Vn4%G>cj zt^q69@-}Y~9OniZE~Ar#1>$JBKRg$5NsGi{%Uybi9buNbtcbUD%2F5SqF0u=9SaT0 z68CYCQPD1U0X2x~-aF)o+tjpW%z*AhkWDk+ZyxY3KL=Cx&z)6Ieb4iPb zsX>>${zaWKj*UT0Gp&Bo5qD`B_6-c;!OOPvgg@Al}|h!Iv|@he6~o!@~KQ~rZH z9QF(|*>d;6^W^1dvD@WLl*R5dzk3FmW%Y8_-mh1d)tmmDL0RU8e(nrnOV;JKVb9TX>=w4Iw z`B^7w2l$Hbym#tfZj^I%W5!xpJ*4~7!EeTT^WWDiy57u#z6>*dET0Va49-6C zkbzOhxt%3Wuo>KLPEqyVWX-wz*6R0XC}ynZZ^qrVByDJaAvbAxhOnEP$W;+=o+-Ln z`uv^^a3|+FC5BThJIqPr^|O?l`Z>!^efr&8ikZ4MHsuY?8r@{V*ll}hWm4~*)d#s* zqkE4VqY~b6?x!X=H!hPJ Date: Mon, 9 Mar 2020 14:33:17 +0100 Subject: [PATCH 42/79] feat(interface-types) Rename `call` to `call-core`, and remove `call-export`. --- lib/interface-types/src/ast.rs | 8 +- lib/interface-types/src/decoders/binary.rs | 20 +- lib/interface-types/src/decoders/wat.rs | 21 +-- lib/interface-types/src/encoders/binary.rs | 15 +- lib/interface-types/src/encoders/wat.rs | 9 +- .../src/interpreter/instruction.rs | 10 +- .../instructions/{call.rs => call_core.rs} | 36 ++-- .../interpreter/instructions/call_export.rs | 177 ------------------ .../src/interpreter/instructions/mod.rs | 6 +- lib/interface-types/src/interpreter/mod.rs | 38 +--- 10 files changed, 52 insertions(+), 288 deletions(-) rename lib/interface-types/src/interpreter/instructions/{call.rs => call_core.rs} (81%) delete mode 100644 lib/interface-types/src/interpreter/instructions/call_export.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 20a671965..7b86c64ed 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -51,12 +51,16 @@ pub enum InterfaceType { } /// Represents a type signature. +/// +/// ```wasm,ignore +/// (@interface type (param i32 i32) (result string)) +/// ``` #[derive(PartialEq, Debug)] pub struct Type { - /// Types for the parameters. + /// Types for the parameters (`(param …)`). pub inputs: Vec, - /// Types for the results. + /// Types for the results (`(result …)`). pub outputs: Vec, } diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 571548c9f..b064ac018 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -168,22 +168,12 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( consume!((input, argument_0) = uleb(input)?); ( input, - Instruction::Call { + Instruction::CallCore { function_index: argument_0 as usize, }, ) } - 0x02 => { - consume!((input, argument_0) = string(input)?); - ( - input, - Instruction::CallExport { - export_name: argument_0, - }, - ) - } - 0x03 => (input, Instruction::ReadUtf8), 0x04 => { @@ -637,10 +627,9 @@ mod tests { #[test] fn test_instructions() { let input = &[ - 0x2c, // list of 44 items + 0x2b, // list of 43 items 0x00, 0x01, // ArgumentGet { index: 1 } - 0x01, 0x01, // Call { function_index: 1 } - 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } + 0x01, 0x01, // CallCore { function_index: 1 } 0x03, // ReadUtf8 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } 0x07, // I32ToS8 @@ -688,8 +677,7 @@ mod tests { &[0x0a][..], vec![ Instruction::ArgumentGet { index: 1 }, - Instruction::Call { function_index: 1 }, - Instruction::CallExport { export_name: "abc" }, + Instruction::CallCore { function_index: 1 }, Instruction::ReadUtf8, Instruction::WriteUtf8 { allocator_name: "abc", diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 2342017ed..093fea05f 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -27,8 +27,7 @@ mod keyword { // Instructions. custom_keyword!(argument_get = "arg.get"); - custom_keyword!(call); - custom_keyword!(call_export = "call-export"); + custom_keyword!(call_core = "call-core"); custom_keyword!(read_utf8 = "read-utf8"); custom_keyword!(write_utf8 = "write-utf8"); custom_keyword!(i32_to_s8 = "i32-to-s8"); @@ -149,18 +148,12 @@ impl<'a> Parse<'a> for Instruction<'a> { Ok(Instruction::ArgumentGet { index: parser.parse()?, }) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; - Ok(Instruction::Call { + Ok(Instruction::CallCore { function_index: parser.parse::()? as usize, }) - } else if lookahead.peek::() { - parser.parse::()?; - - Ok(Instruction::CallExport { - export_name: parser.parse()?, - }) } else if lookahead.peek::() { parser.parse::()?; @@ -673,8 +666,7 @@ mod tests { fn test_instructions() { let inputs = vec![ "arg.get 7", - "call 7", - r#"call-export "foo""#, + "call-core 7", "read-utf8", r#"write-utf8 "foo""#, "i32-to-s8", @@ -719,8 +711,7 @@ mod tests { ]; let outputs = vec![ Instruction::ArgumentGet { index: 7 }, - Instruction::Call { function_index: 7 }, - Instruction::CallExport { export_name: "foo" }, + Instruction::CallCore { function_index: 7 }, Instruction::ReadUtf8, Instruction::WriteUtf8 { allocator_name: "foo", diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 7486d5eb4..311649579 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -255,16 +255,11 @@ where (*index as u64).to_bytes(writer)?; } - Instruction::Call { function_index } => { + Instruction::CallCore { function_index } => { 0x01_u8.to_bytes(writer)?; (*function_index as u64).to_bytes(writer)?; } - Instruction::CallExport { export_name } => { - 0x02_u8.to_bytes(writer)?; - export_name.to_bytes(writer)?; - } - Instruction::ReadUtf8 => 0x03_u8.to_bytes(writer)?, Instruction::WriteUtf8 { allocator_name } => { @@ -554,8 +549,7 @@ mod tests { assert_to_bytes!( vec![ Instruction::ArgumentGet { index: 1 }, - Instruction::Call { function_index: 1 }, - Instruction::CallExport { export_name: "abc" }, + Instruction::CallCore { function_index: 1 }, Instruction::ReadUtf8, Instruction::WriteUtf8 { allocator_name: "abc", @@ -601,10 +595,9 @@ mod tests { Instruction::U64ToI64, ], &[ - 0x2c, // list of 44 items + 0x2b, // list of 43 items 0x00, 0x01, // ArgumentGet { index: 1 } - 0x01, 0x01, // Call { function_index: 1 } - 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } + 0x01, 0x01, // CallCore { function_index: 1 } 0x03, // ReadUtf8 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } 0x07, // I32ToS8 diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 1e883b764..fa79ddd81 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -84,8 +84,7 @@ impl<'input> ToString for &Instruction<'input> { fn to_string(&self) -> String { match self { Instruction::ArgumentGet { index } => format!("arg.get {}", index), - Instruction::Call { function_index } => format!("call {}", function_index), - Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name), + Instruction::CallCore { function_index } => format!("call-core {}", function_index), Instruction::ReadUtf8 => "read-utf8".into(), Instruction::WriteUtf8 { allocator_name } => { format!(r#"write-utf8 "{}""#, allocator_name) @@ -361,8 +360,7 @@ mod tests { fn test_instructions() { let inputs: Vec = vec![ (&Instruction::ArgumentGet { index: 7 }).to_string(), - (&Instruction::Call { function_index: 7 }).to_string(), - (&Instruction::CallExport { export_name: "foo" }).to_string(), + (&Instruction::CallCore { function_index: 7 }).to_string(), (&Instruction::ReadUtf8).to_string(), (&Instruction::WriteUtf8 { allocator_name: "foo", @@ -410,8 +408,7 @@ mod tests { ]; let outputs = vec![ "arg.get 7", - "call 7", - r#"call-export "foo""#, + "call-core 7", "read-utf8", r#"write-utf8 "foo""#, "i32-to-s8", diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index feca1cc7e..1798720af 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -9,18 +9,12 @@ pub enum Instruction<'input> { index: u32, }, - /// The `call` instruction. - Call { + /// The `call-core` instruction. + CallCore { /// The function index. function_index: usize, }, - /// The `call-export` instruction. - CallExport { - /// The exported function name. - export_name: &'input str, - }, - /// The `read-utf8` instruction. ReadUtf8, diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call_core.rs similarity index 81% rename from lib/interface-types/src/interpreter/instructions/call.rs rename to lib/interface-types/src/interpreter/instructions/call_core.rs index 5fe896bb9..3179c9080 100644 --- a/lib/interface-types/src/interpreter/instructions/call.rs +++ b/lib/interface-types/src/interpreter/instructions/call_core.rs @@ -4,7 +4,7 @@ use crate::interpreter::wasm::{ }; executable_instruction!( - call(function_index: usize, instruction_name: String) -> _ { + call_core(function_index: usize, instruction_name: String) -> _ { move |runtime| -> _ { let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(function_index); @@ -65,11 +65,11 @@ executable_instruction!( #[cfg(test)] mod tests { test_executable_instruction!( - test_call = + test_call_core = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, + Instruction::CallCore { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -80,39 +80,39 @@ mod tests { ); test_executable_instruction!( - test_call__invalid_local_import_index = + test_call_core__invalid_local_import_index = instructions: [ - Instruction::Call { function_index: 42 }, + Instruction::CallCore { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), InterfaceValue::I32(4), ], instance: Default::default(), - error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#, + error: r#"`call-core 42` cannot call the local or imported function `42` because it doesn't exist."#, ); test_executable_instruction!( - test_call__stack_is_too_small = + test_call_core__stack_is_too_small = instructions: [ Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, - // ^^ `42` expects 2 values on the stack, only one is present + Instruction::CallCore { function_index: 42 }, + // ^^ `42` expects 2 values on the stack, only one is present ], invocation_inputs: [ InterfaceValue::I32(3), InterfaceValue::I32(4), ], instance: Instance::new(), - error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, + error: r#"`call-core 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, ); test_executable_instruction!( - test_call__invalid_types_in_the_stack = + test_call_core__invalid_types_in_the_stack = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, + Instruction::CallCore { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -120,15 +120,15 @@ mod tests { // ^^^ mismatch with `42` signature ], instance: Instance::new(), - error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + error: r#"`call-core 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, ); test_executable_instruction!( - test_call__failure_when_calling = + test_call_core__failure_when_calling = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, + Instruction::CallCore { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -151,15 +151,15 @@ mod tests { }, ..Default::default() }, - error: r#"`call 42` failed when calling the local or imported function `42`."#, + error: r#"`call-core 42` failed when calling the local or imported function `42`."#, ); test_executable_instruction!( - test_call__void = + test_call_core__void = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, + Instruction::CallCore { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), diff --git a/lib/interface-types/src/interpreter/instructions/call_export.rs b/lib/interface-types/src/interpreter/instructions/call_export.rs deleted file mode 100644 index 9afe98417..000000000 --- a/lib/interface-types/src/interpreter/instructions/call_export.rs +++ /dev/null @@ -1,177 +0,0 @@ -use crate::interpreter::wasm::values::InterfaceType; - -executable_instruction!( - call_export(export_name: String, instruction_name: String) -> _ { - move |runtime| -> _ { - let instance = &mut runtime.wasm_instance; - - match instance.export(&export_name) { - Some(export) => { - let inputs_cardinality = export.inputs_cardinality(); - - match runtime.stack.pop(inputs_cardinality) { - Some(inputs) => { - let input_types = inputs - .iter() - .map(Into::into) - .collect::>(); - - if input_types != export.inputs() { - return Err(format!( - "`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", - instruction_name, - export_name, - export.inputs(), - )) - } - - match export.call(&inputs) { - Ok(outputs) => { - for output in outputs.iter() { - runtime.stack.push(output.clone()); - } - - Ok(()) - } - Err(_) => Err(format!( - "`{}` failed when calling the exported function `{}`.", - instruction_name, - export_name - )) - } - } - None => Err(format!( - "`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - export_name, - inputs_cardinality, - )) - } - } - None => Err(format!( - "`{}` cannot call the exported function `{}` because it doesn't exist.", - instruction_name, - export_name, - )) - } - } - } -); - -#[cfg(test)] -mod tests { - test_executable_instruction!( - test_call_export = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance::new(), - stack: [InterfaceValue::I32(7)], - ); - - test_executable_instruction!( - test_call_export__invalid_export_name = - instructions: [Instruction::CallExport { export_name: "bar" }], - invocation_inputs: [], - instance: Instance::new(), - error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#, - ); - - test_executable_instruction!( - test_call_export__stack_is_too_small = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance::new(), - error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#, - ); - - test_executable_instruction!( - test_call_export__invalid_types_in_the_stack = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I64(4), - // ^^^ mismatch with `sum` signature - ], - instance: Instance::new(), - error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, - ); - - test_executable_instruction!( - test_call_export__failure_when_calling = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - hashmap - }, - ..Default::default() - }, - error: r#"`call-export "sum"` failed when calling the exported function `sum`."#, - ); - - test_executable_instruction!( - test_call_export__void = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Ok(vec![]), - // ^^^^^^^^^^ void function - }, - ); - - hashmap - }, - ..Default::default() - }, - stack: [], - ); -} diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 28aa5209b..df174614b 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -1,13 +1,11 @@ mod argument_get; -mod call; -mod call_export; +mod call_core; mod lowering_lifting; mod read_utf8; mod write_utf8; pub(crate) use argument_get::argument_get; -pub(crate) use call::call; -pub(crate) use call_export::call_export; +pub(crate) use call_core::call_core; pub(crate) use lowering_lifting::*; pub(crate) use read_utf8::read_utf8; pub(crate) use write_utf8::write_utf8; diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 1cfe66531..f21c37985 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -71,7 +71,7 @@ pub(crate) type ExecutableInstruction = (&vec![ /// Instruction::ArgumentGet { index: 1 }, /// Instruction::ArgumentGet { index: 0 }, -/// Instruction::CallExport { export_name: "sum" }, +/// Instruction::CallCore { function_index: 42 }, /// ]) /// .try_into() /// .unwrap(); @@ -81,12 +81,12 @@ pub(crate) type ExecutableInstruction i32 { a + b }`. -/// exports: { +/// // 3.1. Defines one function: `fn sum(a: i32, b: i32) -> i32 { a + b }`. +/// locals_or_imports: { /// let mut hashmap = HashMap::new(); /// hashmap.insert( -/// "sum".into(), -/// Export { +/// 42, +/// LocalImport { /// // Defines the argument types of the function. /// inputs: vec![InterfaceType::I32, InterfaceType::I32], /// @@ -196,11 +196,8 @@ where Instruction::ArgumentGet { index } => { instructions::argument_get(*index, instruction_name) } - Instruction::Call { function_index } => { - instructions::call(*function_index, instruction_name) - } - Instruction::CallExport { export_name } => { - instructions::call_export((*export_name).to_owned(), instruction_name) + Instruction::CallCore { function_index } => { + instructions::call_core(*function_index, instruction_name) } Instruction::ReadUtf8 => instructions::read_utf8(instruction_name), Instruction::WriteUtf8 { allocator_name } => { @@ -253,24 +250,3 @@ where }) } } - -#[cfg(test)] -mod tests { - use super::{wasm::structures::EmptyMemoryView, Instruction, Interpreter}; - use std::convert::TryInto; - - #[test] - fn test_interpreter_from_instructions() { - let instructions = vec![ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "foo" }, - Instruction::ReadUtf8, - Instruction::Call { function_index: 7 }, - ]; - let interpreter: Interpreter<(), (), (), (), EmptyMemoryView> = - (&instructions).try_into().unwrap(); - - assert_eq!(interpreter.executable_instructions.len(), 5); - } -} From 3cc4c6142c1e4e2f9d486218b34024dbf62b88ca Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 9 Mar 2020 14:53:11 +0100 Subject: [PATCH 43/79] doc(interface-types) Improve the `README.md`. --- lib/interface-types/README.md | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/lib/interface-types/README.md b/lib/interface-types/README.md index 242b14ea2..f8f349013 100644 --- a/lib/interface-types/README.md +++ b/lib/interface-types/README.md @@ -30,3 +30,68 @@ more](https://github.com/wasmerio/wasmer). This crate is an implementation of [the living WebAssembly Interface Types standard](https://github.com/WebAssembly/interface-types). + +## Encoders and decoders + +The `wasmer-interface-types` crate comes with an encoder and a decoder +for the WAT format, and the binary format, for the WebAssembly +Interface Types. An encoder writes an AST into another format, like +WAT or binary. A decoder reads an AST from another format, like WAT or +binary. + +## Instructions + +Very basically, WebAssembly Interface Types defines a set of +instructions, used by adapters to transform the data between +WebAssembly core and the outside world ([learn +mode](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md)). + +Here is the instructions that are implemented: + +| Instruction | WAT encoder | Binary encoder | WAT decoder | Binary decoder | Interpreter | +|-|-|-|-|-|-| +| `arg.get` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `call-core` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `memory-to-string` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `string-to-memory` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `call-adapter` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `defer-call-core` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `i32-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-s8x` | ✅ | ✅ | ✅ | ✅ | ❌ | +| `i32-to-u8` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-s16` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-s16x` | ✅ | ✅ | ✅ | ✅ | ❌ | +| `i32-to-u16` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-s32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-u32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-s64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i32-to-u64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s8x` | ✅ | ✅ | ✅ | ✅ | ❌ | +| `i64-to-u8` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s16` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s16x` | ✅ | ✅ | ✅ | ✅ | ❌ | +| `i64-to-u16` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s32x` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-u32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-s64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `i64-to-u64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s8-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u8-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s16-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u16-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s32-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u32-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s64-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s64-to-i32x` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u64-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u64-to-i32x` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s8-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u8-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s16-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u16-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s32-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u32-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `s64-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `u64-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ | From 39d491e4658c6c659697857e0f0cd22555219371 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 9 Mar 2020 15:06:35 +0100 Subject: [PATCH 44/79] feat(interface-types) `read-utf8` is renamed `memory-to-string`. --- lib/interface-types/src/decoders/binary.rs | 6 ++--- lib/interface-types/src/decoders/wat.rs | 12 ++++----- lib/interface-types/src/encoders/binary.rs | 6 ++--- lib/interface-types/src/encoders/wat.rs | 6 ++--- .../src/interpreter/instruction.rs | 4 +-- .../{read_utf8.rs => memory_to_string.rs} | 26 +++++++++---------- .../src/interpreter/instructions/mod.rs | 4 +-- .../interpreter/instructions/write_utf8.rs | 4 +-- lib/interface-types/src/interpreter/mod.rs | 4 +-- 9 files changed, 36 insertions(+), 36 deletions(-) rename lib/interface-types/src/interpreter/instructions/{read_utf8.rs => memory_to_string.rs} (82%) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 571548c9f..6412cbaa5 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -184,7 +184,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( ) } - 0x03 => (input, Instruction::ReadUtf8), + 0x03 => (input, Instruction::MemoryToString), 0x04 => { consume!((input, argument_0) = string(input)?); @@ -641,7 +641,7 @@ mod tests { 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // Call { function_index: 1 } 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } - 0x03, // ReadUtf8 + 0x03, // MemoryToString 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } 0x07, // I32ToS8 0x08, // I32ToS8X @@ -690,7 +690,7 @@ mod tests { Instruction::ArgumentGet { index: 1 }, Instruction::Call { function_index: 1 }, Instruction::CallExport { export_name: "abc" }, - Instruction::ReadUtf8, + Instruction::MemoryToString, Instruction::WriteUtf8 { allocator_name: "abc", }, diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 2342017ed..8a25064f2 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -29,7 +29,7 @@ mod keyword { custom_keyword!(argument_get = "arg.get"); custom_keyword!(call); custom_keyword!(call_export = "call-export"); - custom_keyword!(read_utf8 = "read-utf8"); + custom_keyword!(memory_to_string = "memory-to-string"); custom_keyword!(write_utf8 = "write-utf8"); custom_keyword!(i32_to_s8 = "i32-to-s8"); custom_keyword!(i32_to_s8x = "i32-to-s8x"); @@ -161,10 +161,10 @@ impl<'a> Parse<'a> for Instruction<'a> { Ok(Instruction::CallExport { export_name: parser.parse()?, }) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; - Ok(Instruction::ReadUtf8) + Ok(Instruction::MemoryToString) } else if lookahead.peek::() { parser.parse::()?; @@ -675,7 +675,7 @@ mod tests { "arg.get 7", "call 7", r#"call-export "foo""#, - "read-utf8", + "memory-to-string", r#"write-utf8 "foo""#, "i32-to-s8", "i32-to-s8x", @@ -721,7 +721,7 @@ mod tests { Instruction::ArgumentGet { index: 7 }, Instruction::Call { function_index: 7 }, Instruction::CallExport { export_name: "foo" }, - Instruction::ReadUtf8, + Instruction::MemoryToString, Instruction::WriteUtf8 { allocator_name: "foo", }, diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 7486d5eb4..1d6762553 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -265,7 +265,7 @@ where export_name.to_bytes(writer)?; } - Instruction::ReadUtf8 => 0x03_u8.to_bytes(writer)?, + Instruction::MemoryToString => 0x03_u8.to_bytes(writer)?, Instruction::WriteUtf8 { allocator_name } => { 0x04_u8.to_bytes(writer)?; @@ -556,7 +556,7 @@ mod tests { Instruction::ArgumentGet { index: 1 }, Instruction::Call { function_index: 1 }, Instruction::CallExport { export_name: "abc" }, - Instruction::ReadUtf8, + Instruction::MemoryToString, Instruction::WriteUtf8 { allocator_name: "abc", }, @@ -605,7 +605,7 @@ mod tests { 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // Call { function_index: 1 } 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } - 0x03, // ReadUtf8 + 0x03, // MemoryToString 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } 0x07, // I32ToS8 0x08, // I32ToS8X diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 1e883b764..c0cff2299 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -86,7 +86,7 @@ impl<'input> ToString for &Instruction<'input> { Instruction::ArgumentGet { index } => format!("arg.get {}", index), Instruction::Call { function_index } => format!("call {}", function_index), Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name), - Instruction::ReadUtf8 => "read-utf8".into(), + Instruction::MemoryToString => "memory-to-string".into(), Instruction::WriteUtf8 { allocator_name } => { format!(r#"write-utf8 "{}""#, allocator_name) } @@ -363,7 +363,7 @@ mod tests { (&Instruction::ArgumentGet { index: 7 }).to_string(), (&Instruction::Call { function_index: 7 }).to_string(), (&Instruction::CallExport { export_name: "foo" }).to_string(), - (&Instruction::ReadUtf8).to_string(), + (&Instruction::MemoryToString).to_string(), (&Instruction::WriteUtf8 { allocator_name: "foo", }) @@ -412,7 +412,7 @@ mod tests { "arg.get 7", "call 7", r#"call-export "foo""#, - "read-utf8", + "memory-to-string", r#"write-utf8 "foo""#, "i32-to-s8", "i32-to-s8x", diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index feca1cc7e..9cccacd05 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -21,8 +21,8 @@ pub enum Instruction<'input> { export_name: &'input str, }, - /// The `read-utf8` instruction. - ReadUtf8, + /// The `memory-to-string` instruction. + MemoryToString, /// The `write-utf8` instruction. WriteUtf8 { diff --git a/lib/interface-types/src/interpreter/instructions/read_utf8.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs similarity index 82% rename from lib/interface-types/src/interpreter/instructions/read_utf8.rs rename to lib/interface-types/src/interpreter/instructions/memory_to_string.rs index a06bc5630..678d08d74 100644 --- a/lib/interface-types/src/interpreter/instructions/read_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -2,7 +2,7 @@ use crate::interpreter::wasm::values::InterfaceValue; use std::{cell::Cell, convert::TryFrom}; executable_instruction!( - read_utf8(instruction_name: String) -> _ { + memory_to_string(instruction_name: String) -> _ { move |runtime| -> _ { match runtime.stack.pop(2) { Some(inputs) => match runtime.wasm_instance.memory(0) { @@ -55,11 +55,11 @@ executable_instruction!( #[cfg(test)] mod tests { test_executable_instruction!( - test_read_utf8 = + test_memory_to_string = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, + Instruction::MemoryToString, ], invocation_inputs: [ InterfaceValue::I32(13), @@ -75,11 +75,11 @@ mod tests { ); test_executable_instruction!( - test_read_utf8__read_out_of_memory = + test_memory_to_string__read_out_of_memory = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, + Instruction::MemoryToString, ], invocation_inputs: [ InterfaceValue::I32(13), @@ -91,15 +91,15 @@ mod tests { memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), ..Default::default() }, - error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, + error: r#"`memory-to-string` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, ); test_executable_instruction!( - test_read_utf8__invalid_encoding = + test_memory_to_string__invalid_encoding = instructions: [ Instruction::ArgumentGet { index: 1 }, Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, + Instruction::MemoryToString, ], invocation_inputs: [ InterfaceValue::I32(4), @@ -111,21 +111,21 @@ mod tests { memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::>>()), ..Default::default() }, - error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, + error: r#"`memory-to-string` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, ); test_executable_instruction!( - test_read_utf8__stack_is_too_small = + test_memory_to_string__stack_is_too_small = instructions: [ Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, - // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. + Instruction::MemoryToString, + // ^^^^^^^^^^^^^^ `memory-to-string` expects 2 values on the stack, only one is present. ], invocation_inputs: [ InterfaceValue::I32(13), InterfaceValue::I32(0), ], instance: Instance::new(), - error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#, + error: r#"`memory-to-string` failed because there is not enough data on the stack (needs 2)."#, ); } diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 28aa5209b..3bf7ed4a8 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -2,14 +2,14 @@ mod argument_get; mod call; mod call_export; mod lowering_lifting; -mod read_utf8; +mod memory_to_string; mod write_utf8; pub(crate) use argument_get::argument_get; pub(crate) use call::call; pub(crate) use call_export::call_export; pub(crate) use lowering_lifting::*; -pub(crate) use read_utf8::read_utf8; +pub(crate) use memory_to_string::memory_to_string; pub(crate) use write_utf8::write_utf8; #[cfg(test)] diff --git a/lib/interface-types/src/interpreter/instructions/write_utf8.rs b/lib/interface-types/src/interpreter/instructions/write_utf8.rs index a1e21509f..a3f53d550 100644 --- a/lib/interface-types/src/interpreter/instructions/write_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/write_utf8.rs @@ -90,11 +90,11 @@ mod tests { ); test_executable_instruction!( - test_write_utf8__roundtrip_with_read_utf8 = + test_write_utf8__roundtrip_with_memory_to_string = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::WriteUtf8 { allocator_name: "alloc" }, - Instruction::ReadUtf8, + Instruction::MemoryToString, ], invocation_inputs: [InterfaceValue::String("Hello, World!".into())], instance: Instance::new(), diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 1cfe66531..1dfd5af7d 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -202,7 +202,7 @@ where Instruction::CallExport { export_name } => { instructions::call_export((*export_name).to_owned(), instruction_name) } - Instruction::ReadUtf8 => instructions::read_utf8(instruction_name), + Instruction::MemoryToString => instructions::memory_to_string(instruction_name), Instruction::WriteUtf8 { allocator_name } => { instructions::write_utf8((*allocator_name).to_owned(), instruction_name) } @@ -265,7 +265,7 @@ mod tests { Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 }, Instruction::CallExport { export_name: "foo" }, - Instruction::ReadUtf8, + Instruction::MemoryToString, Instruction::Call { function_index: 7 }, ]; let interpreter: Interpreter<(), (), (), (), EmptyMemoryView> = From 79613e42d78b7c0fae10878929f1afcf8b8ca82e Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 10 Mar 2020 02:45:31 +0800 Subject: [PATCH 45/79] Prettier format panic message in spectest. --- lib/spectests/tests/spectest.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/spectests/tests/spectest.rs b/lib/spectests/tests/spectest.rs index 514403d70..a0bab9ca1 100644 --- a/lib/spectests/tests/spectest.rs +++ b/lib/spectests/tests/spectest.rs @@ -256,6 +256,16 @@ mod tests { Memory, Table, }; + fn format_panic(e: &dyn std::any::Any) -> String { + if let Some(s) = e.downcast_ref::<&str>() { + format!("{}", s) + } else if let Some(s) = e.downcast_ref::() { + format!("{}", s) + } else { + "(unknown)".into() + } + } + fn parse_and_run( path: &PathBuf, file_excludes: &HashSet, @@ -342,7 +352,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "Module"), - message: format!("caught panic {:?}", e), + message: format!("caught panic {:?}", format_panic(&e)), }, &test_key, excludes, @@ -798,7 +808,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "AssertInvalid"), - message: format!("caught panic {:?}", p), + message: format!("caught panic {:?}", format_panic(&p)), }, &test_key, excludes, @@ -851,7 +861,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "AssertMalformed"), - message: format!("caught panic {:?}", p), + message: format!("caught panic {:?}", format_panic(&p)), }, &test_key, excludes, @@ -975,7 +985,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "AssertUnlinkable"), - message: format!("caught panic {:?}", e), + message: format!("caught panic {:?}", format_panic(&e)), }, &test_key, excludes, From 65962f01860668688635cd9f82f94ea2aa7e5e80 Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 10 Mar 2020 02:46:13 +0800 Subject: [PATCH 46/79] Add a translation layer before import call in singlepass. --- lib/clif-backend/src/code.rs | 2 +- lib/llvm-backend/src/code.rs | 2 +- lib/runtime-core/src/codegen.rs | 2 +- lib/runtime-core/src/parse.rs | 65 +++++++++------- lib/singlepass-backend/src/codegen_x64.rs | 93 ++++++++++++++++++++++- 5 files changed, 129 insertions(+), 35 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 67506f931..ada71d0a3 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -209,7 +209,7 @@ impl ModuleCodeGenerator Ok(()) } - fn feed_import_function(&mut self) -> Result<(), CodegenError> { + fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), CodegenError> { Ok(()) } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 954e0c405..cebde5dae 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -8984,7 +8984,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod Ok(()) } - fn feed_import_function(&mut self) -> Result<(), CodegenError> { + fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), CodegenError> { self.func_import_count += 1; Ok(()) } diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index b65234cf4..d3ae27583 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -143,7 +143,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, Ok(()) } /// Adds an import function. - fn feed_import_function(&mut self) -> Result<(), E>; + fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), E>; /// Sets the signatures. fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; /// Sets function signatures. diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index d2b88f07a..e65dfae8d 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -110,11 +110,46 @@ pub fn read_module< let mut namespace_builder = Some(StringTableBuilder::new()); let mut name_builder = Some(StringTableBuilder::new()); let mut func_count: usize = 0; + let mut mcg_signatures_fed = false; let mut mcg_info_fed = false; loop { use wasmparser::ParserState; let state = parser.read(); + + // Feed signature and namespace information as early as possible. + match *state { + ParserState::BeginFunctionBody { .. } + | ParserState::ImportSectionEntry { .. } + | ParserState::EndWasm => { + if !mcg_signatures_fed { + mcg_signatures_fed = true; + let info_read = info.read().unwrap(); + mcg.feed_signatures(info_read.signatures.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + } + _ => {} + } + match *state { + ParserState::BeginFunctionBody { .. } | ParserState::EndWasm => { + if !mcg_info_fed { + mcg_info_fed = true; + { + let mut info_write = info.write().unwrap(); + info_write.namespace_table = namespace_builder.take().unwrap().finish(); + info_write.name_table = name_builder.take().unwrap().finish(); + } + let info_read = info.read().unwrap(); + mcg.feed_function_signatures(info_read.func_assoc.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + mcg.check_precondition(&info_read) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + } + _ => {} + } + match *state { ParserState::Error(ref err) => return Err(err.clone().into()), ParserState::TypeSectionEntry(ref ty) => { @@ -136,7 +171,7 @@ pub fn read_module< let sigindex = SigIndex::new(sigindex as usize); info.write().unwrap().imported_functions.push(import_name); info.write().unwrap().func_assoc.push(sigindex); - mcg.feed_import_function() + mcg.feed_import_function(sigindex) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } ImportSectionEntryType::Table(table_ty) => { @@ -218,22 +253,6 @@ pub fn read_module< } ParserState::BeginFunctionBody { range } => { let id = func_count; - if !mcg_info_fed { - mcg_info_fed = true; - { - let mut info_write = info.write().unwrap(); - info_write.namespace_table = namespace_builder.take().unwrap().finish(); - info_write.name_table = name_builder.take().unwrap().finish(); - } - let info_read = info.read().unwrap(); - mcg.feed_signatures(info_read.signatures.clone()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.feed_function_signatures(info_read.func_assoc.clone()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.check_precondition(&info_read) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - let fcg = mcg .next_function( Arc::clone(&info), @@ -432,18 +451,6 @@ pub fn read_module< info.write().unwrap().globals.push(global_init); } ParserState::EndWasm => { - // TODO Consolidate with BeginFunction body if possible - if !mcg_info_fed { - info.write().unwrap().namespace_table = - namespace_builder.take().unwrap().finish(); - info.write().unwrap().name_table = name_builder.take().unwrap().finish(); - mcg.feed_signatures(info.read().unwrap().signatures.clone()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.feed_function_signatures(info.read().unwrap().func_assoc.clone()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.check_precondition(&info.read().unwrap()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } break; } _ => {} diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 78bc0af9a..32290341f 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -32,8 +32,9 @@ use wasmer_runtime_core::{ memory::MemoryType, module::{ModuleInfo, ModuleInner}, state::{ - x64::new_machine_state, x64::X64Register, FunctionStateMap, MachineState, MachineValue, - ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue, + x64::new_machine_state, x64::X64Register, x64_decl::ArgumentRegisterAllocator, + FunctionStateMap, MachineState, MachineValue, ModuleStateMap, OffsetInfo, SuspendOffset, + WasmAbstractValue, }, structures::{Map, TypedIndex}, typed_func::{Trampoline, Wasm}, @@ -869,7 +870,7 @@ impl ModuleCodeGenerator Ok(()) } - fn feed_import_function(&mut self) -> Result<(), CodegenError> { + fn feed_import_function(&mut self, sigindex: SigIndex) -> Result<(), CodegenError> { let labels = self.function_labels.as_mut().unwrap(); let id = labels.len(); @@ -880,6 +881,92 @@ impl ModuleCodeGenerator a.emit_label(label); labels.insert(id, (label, Some(offset))); + // Singlepass internally treats all arguments as integers, but the standard System V calling convention requires + // floating point arguments to be passed in XMM registers. + // + // FIXME: This is only a workaround. We should fix singlepass to use the standard CC. + let sig = self + .signatures + .as_ref() + .expect("signatures itself") + .get(sigindex) + .expect("signatures"); + // Translation is expensive, so only do it if needed. + if sig + .params() + .iter() + .find(|&&x| x == Type::F32 || x == Type::F64) + .is_some() + { + let mut param_locations: Vec = vec![]; + + // Allocate stack space for arguments. + let stack_offset: i32 = if sig.params().len() > 5 { + 5 * 8 + } else { + (sig.params().len() as i32) * 8 + }; + if stack_offset > 0 { + a.emit_sub( + Size::S64, + Location::Imm32(stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + + // Store all arguments to the stack to prevent overwrite. + for i in 0..sig.params().len() { + let loc = match i { + 0..=4 => { + static PARAM_REGS: &'static [GPR] = + &[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; + let loc = Location::Memory(GPR::RSP, (i * 8) as i32); + a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc); + loc + } + _ => Location::Memory(GPR::RSP, stack_offset + 8 + ((i - 5) * 8) as i32), + }; + param_locations.push(loc); + } + + // Copy arguments. + let mut argalloc = ArgumentRegisterAllocator::default(); + argalloc.next(Type::I32).unwrap(); // skip vm::Ctx + let mut caller_stack_offset: i32 = 0; + for (i, ty) in sig.params().iter().enumerate() { + let prev_loc = param_locations[i]; + let target = match argalloc.next(*ty) { + Some(X64Register::GPR(gpr)) => Location::GPR(gpr), + Some(X64Register::XMM(xmm)) => Location::XMM(xmm), + None => { + // No register can be allocated. Put this argument on the stack. + // + // Since here we never use fewer registers than by the original call, on the caller's frame + // we always have enough space to store the rearranged arguments, and the copy "backward" between different + // slots in the caller argument region will always work. + a.emit_mov(Size::S64, prev_loc, Location::GPR(GPR::RAX)); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::Memory(GPR::RSP, stack_offset + 8 + caller_stack_offset), + ); + caller_stack_offset += 8; + continue; + } + }; + a.emit_mov(Size::S64, prev_loc, target); + } + + // Restore stack pointer. + if stack_offset > 0 { + a.emit_add( + Size::S64, + Location::Imm32(stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + // Emits a tail call trampoline that loads the address of the target import function // from Ctx and jumps to it. From 7e2ede3960ee54c86db57bb587f3f071796a682c Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 10 Mar 2020 03:16:22 +0800 Subject: [PATCH 47/79] Fix floating point return values. --- lib/runtime-core/src/typed_func.rs | 62 ++++++++++++++++++----- lib/singlepass-backend/src/codegen_x64.rs | 46 +++++++++++++++-- 2 files changed, 92 insertions(+), 16 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4dc192b0b..e49202e3a 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -306,16 +306,15 @@ impl<'a> DynamicFunc<'a> { { use crate::trampoline_x64::{CallContext, TrampolineBufferBuilder}; use crate::types::Value; - use std::convert::TryFrom; struct PolymorphicContext { arg_types: Vec, func: Box Vec>, } - unsafe extern "C" fn enter_host_polymorphic( + unsafe fn do_enter_host_polymorphic( ctx: *const CallContext, args: *const u64, - ) -> u64 { + ) -> Vec { let ctx = &*(ctx as *const PolymorphicContext); let vmctx = &mut *(*args.offset(0) as *mut vm::Ctx); let args: Vec = ctx @@ -335,13 +334,40 @@ impl<'a> DynamicFunc<'a> { } }) .collect(); - let rets = (ctx.func)(vmctx, &args); + (ctx.func)(vmctx, &args) + } + unsafe extern "C" fn enter_host_polymorphic_i( + ctx: *const CallContext, + args: *const u64, + ) -> u64 { + let rets = do_enter_host_polymorphic(ctx, args); if rets.len() == 0 { 0 } else if rets.len() == 1 { - u64::try_from(rets[0].to_u128()).expect( - "128-bit return value from polymorphic host functions is not yet supported", - ) + match rets[0] { + Value::I32(x) => x as u64, + Value::I64(x) => x as u64, + _ => panic!("enter_host_polymorphic_i: invalid return type"), + } + } else { + panic!( + "multiple return values from polymorphic host functions is not yet supported" + ); + } + } + unsafe extern "C" fn enter_host_polymorphic_f( + ctx: *const CallContext, + args: *const u64, + ) -> f64 { + let rets = do_enter_host_polymorphic(ctx, args); + if rets.len() == 0 { + 0.0 + } else if rets.len() == 1 { + match rets[0] { + Value::F32(x) => f64::from_bits(x.to_bits() as u64), + Value::F64(x) => x, + _ => panic!("enter_host_polymorphic_f: invalid return type"), + } } else { panic!( "multiple return values from polymorphic host functions is not yet supported" @@ -364,11 +390,23 @@ impl<'a> DynamicFunc<'a> { let mut native_param_types = vec![Type::I32]; // vm::Ctx is the first parameter. native_param_types.extend_from_slice(signature.params()); - builder.add_callinfo_trampoline( - enter_host_polymorphic, - ctx as *const _, - &native_param_types, - ); + match signature.returns() { + [x] if *x == Type::F32 || *x == Type::F64 => { + builder.add_callinfo_trampoline( + unsafe { std::mem::transmute(enter_host_polymorphic_f as usize) }, + ctx as *const _, + &native_param_types, + ); + } + _ => { + builder.add_callinfo_trampoline( + enter_host_polymorphic_i, + ctx as *const _, + &native_param_types, + ); + } + } + let ptr = builder .insert_global() .expect("cannot bump-allocate global trampoline memory"); diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 32290341f..5903508fc 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -205,6 +205,7 @@ pub struct X64FunctionCode { signatures: Arc>, function_signatures: Arc>, + signature: FuncSig, fsm: FunctionStateMap, offset: usize, @@ -713,11 +714,22 @@ impl ModuleCodeGenerator machine.track_state = self.config.as_ref().unwrap().track_state; assembler.emit_label(begin_label); + + let signatures = self.signatures.as_ref().unwrap(); + let function_signatures = self.function_signatures.as_ref().unwrap(); + let sig_index = function_signatures + .get(FuncIndex::new( + self.functions.len() + self.func_import_count, + )) + .unwrap() + .clone(); + let sig = signatures.get(sig_index).unwrap().clone(); let code = X64FunctionCode { local_function_id: self.functions.len(), - signatures: self.signatures.as_ref().unwrap().clone(), - function_signatures: self.function_signatures.as_ref().unwrap().clone(), + signatures: signatures.clone(), + function_signatures: function_signatures.clone(), + signature: sig, fsm: FunctionStateMap::new(new_machine_state(), self.functions.len(), 32, vec![]), // only a placeholder; this is initialized later in `begin_body` offset: begin_offset.0, @@ -6347,7 +6359,14 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + match return_types[0] { + WpType::F32 | WpType::F64 => { + a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret); + } + _ => { + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } } } Operator::CallIndirect { index, table_index } => { @@ -6486,7 +6505,14 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + match return_types[0] { + WpType::F32 | WpType::F64 => { + a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret); + } + _ => { + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } } } Operator::If { ty } => { @@ -7701,6 +7727,18 @@ impl FunctionCodeGenerator for X64FunctionCode { self.machine.finalize_locals(a, &self.locals); a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP)); a.emit_pop(Size::S64, Location::GPR(GPR::RBP)); + + // Make a copy of the return value in XMM0, as required by the SysV CC. + match self.signature.returns() { + [x] if *x == Type::F32 || *x == Type::F64 => { + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::XMM(XMM::XMM0), + ); + } + _ => {} + } a.emit_ret(); } else { let released = &self.value_stack[frame.value_stack_depth..]; From 7d2d89b606ceb0241607532621f9ace9d443c998 Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 10 Mar 2020 12:28:54 +0800 Subject: [PATCH 48/79] Resolve review comments. --- lib/runtime-core-tests/tests/imports.rs | 4 ---- lib/runtime-core/src/trampoline_x64.rs | 11 ++++++++--- lib/runtime-core/src/typed_func.rs | 4 +++- lib/spectests/tests/spectest.rs | 8 ++++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index bb0339459..e44d5159f 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -236,10 +236,6 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) { let j: i64 = (&inputs[1]).try_into().unwrap(); let k: f32 = (&inputs[2]).try_into().unwrap(); - dbg!(i); - dbg!(j); - dbg!(k); - vec![Value::F32(shift_ as f32 + i as f32 + j as f32 + k)] } ), diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 24fc4f385..e85d2d910 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -249,6 +249,7 @@ impl TrampolineBufferBuilder { target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64, context: *const CallContext, params: &[Type], + _returns: &[Type], ) -> usize { let idx = self.offsets.len(); self.offsets.push(self.code.len()); @@ -402,11 +403,12 @@ mod tests { } let mut builder = TrampolineBufferBuilder::new(); let ctx = TestContext { value: 100 }; - let param_types: Vec = (0..8).map(|_| Type::I32).collect(); + let param_types: Vec = vec![Type::I32; 8]; let idx = builder.add_callinfo_trampoline( do_add, &ctx as *const TestContext as *const _, ¶m_types, + &[Type::I32], ); let buf = builder.build(); let t = buf.get_trampoline(idx); @@ -421,6 +423,7 @@ mod tests { #[test] fn test_trampolines_with_floating_point() { unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 { + // `n` is not really a pointer. It is the length of the argument list, casted into the pointer type. let n = n as usize; let mut result: u64 = 0; for i in 0..n { @@ -443,6 +446,7 @@ mod tests { Type::I32, Type::I32, ], + &[Type::I32], ); let ptr = buffer.insert(builder.code()).unwrap(); let ret = unsafe { @@ -458,6 +462,7 @@ mod tests { #[test] fn test_many_global_trampolines() { unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 { + // `n` is not really a pointer. It is the length of the argument list, casted into the pointer type. let n = n as usize; let mut result: u64 = 0; for i in 0..n { @@ -475,8 +480,8 @@ mod tests { for i in 0..5000usize { let mut builder = TrampolineBufferBuilder::new(); let n = i % 8; - let param_types: Vec<_> = (0..n).map(|_| Type::I32).collect(); - builder.add_callinfo_trampoline(inner, n as _, ¶m_types); + let param_types: Vec<_> = (0..n).map(|_| Type::I64).collect(); + builder.add_callinfo_trampoline(inner, n as _, ¶m_types, &[Type::I64]); let ptr = buffer .insert(builder.code()) .expect("cannot insert new code into global buffer"); diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index e49202e3a..3280e9e89 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -387,7 +387,7 @@ impl<'a> DynamicFunc<'a> { }); let ctx = Box::into_raw(ctx); - let mut native_param_types = vec![Type::I32]; // vm::Ctx is the first parameter. + let mut native_param_types = vec![Type::I64]; // vm::Ctx is the first parameter. native_param_types.extend_from_slice(signature.params()); match signature.returns() { @@ -396,6 +396,7 @@ impl<'a> DynamicFunc<'a> { unsafe { std::mem::transmute(enter_host_polymorphic_f as usize) }, ctx as *const _, &native_param_types, + signature.returns(), ); } _ => { @@ -403,6 +404,7 @@ impl<'a> DynamicFunc<'a> { enter_host_polymorphic_i, ctx as *const _, &native_param_types, + signature.returns(), ); } } diff --git a/lib/spectests/tests/spectest.rs b/lib/spectests/tests/spectest.rs index a0bab9ca1..409a9d908 100644 --- a/lib/spectests/tests/spectest.rs +++ b/lib/spectests/tests/spectest.rs @@ -352,7 +352,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "Module"), - message: format!("caught panic {:?}", format_panic(&e)), + message: format!("caught panic {}", format_panic(&e)), }, &test_key, excludes, @@ -808,7 +808,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "AssertInvalid"), - message: format!("caught panic {:?}", format_panic(&p)), + message: format!("caught panic {}", format_panic(&p)), }, &test_key, excludes, @@ -861,7 +861,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "AssertMalformed"), - message: format!("caught panic {:?}", format_panic(&p)), + message: format!("caught panic {}", format_panic(&p)), }, &test_key, excludes, @@ -985,7 +985,7 @@ mod tests { file: filename.to_string(), line: line, kind: format!("{}", "AssertUnlinkable"), - message: format!("caught panic {:?}", format_panic(&e)), + message: format!("caught panic {}", format_panic(&e)), }, &test_key, excludes, From 3e63f1aaa9eb69e4f3f78ff29a164c59a7ef18c9 Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 10 Mar 2020 12:37:40 +0800 Subject: [PATCH 49/79] Deprecate wasmer_trampoline_buffer_builder_add_callinfo_trampoline. --- lib/runtime-c-api/src/trampoline.rs | 10 +++++++++- lib/runtime-c-api/wasmer.h | 2 ++ lib/runtime-c-api/wasmer.hh | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index ed7b8971b..9555927ea 100644 --- a/lib/runtime-c-api/src/trampoline.rs +++ b/lib/runtime-c-api/src/trampoline.rs @@ -34,6 +34,8 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline } /// Adds a callinfo trampoline to the builder. +/// +/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline( @@ -42,8 +44,14 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampolin ctx: *const c_void, num_params: u32, ) -> usize { + use wasmer_runtime_core::types::Type; let builder = &mut *(builder as *mut TrampolineBufferBuilder); - builder.add_callinfo_trampoline(mem::transmute(func), ctx as *const CallContext, num_params) + builder.add_callinfo_trampoline( + mem::transmute(func), + ctx as *const CallContext, + &vec![Type::I64; num_params as usize], + &[Type::I64], + ) } /// Finalizes the trampoline builder into an executable buffer. diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 94b2fbb34..7a872f65c 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -1386,6 +1386,8 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits) #if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Adds a callinfo trampoline to the builder. + * + * Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function. */ uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder, const wasmer_trampoline_callable_t *func, diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 047f8bebb..6bd66d40b 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -1146,6 +1146,8 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits) #if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Adds a callinfo trampoline to the builder. +/// +/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function. uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder, const wasmer_trampoline_callable_t *func, const void *ctx, From cfbcd886d02b7f6822312979d1a3854c919abc48 Mon Sep 17 00:00:00 2001 From: Heyang Zhou Date: Tue, 10 Mar 2020 12:38:39 +0800 Subject: [PATCH 50/79] Fix typo (1) Co-Authored-By: nlewycky --- lib/runtime-core/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index fb19476b1..b153e13e1 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -614,7 +614,7 @@ pub mod x64_decl { /// Returns the instruction prefix for `movq %this_reg, ?(%rsp)`. /// - /// To build a instruction, append the memory location as a 32-bit + /// To build an instruction, append the memory location as a 32-bit /// offset to the stack pointer to this prefix. pub fn prefix_mov_to_stack(&self) -> Option<&'static [u8]> { Some(match *self { From e521dfe8c15e0369aa3890a820af351fd9ca0ac7 Mon Sep 17 00:00:00 2001 From: Heyang Zhou Date: Tue, 10 Mar 2020 12:38:55 +0800 Subject: [PATCH 51/79] Fix typo (2) Co-Authored-By: nlewycky --- lib/runtime-core/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index b153e13e1..55809e965 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -650,7 +650,7 @@ pub mod x64_decl { } impl ArgumentRegisterAllocator { - /// Allocates a register for argument type `ty`. Returns `None` if no register is available for this type.. + /// Allocates a register for argument type `ty`. Returns `None` if no register is available for this type. pub fn next(&mut self, ty: Type) -> Option { static GPR_SEQ: &'static [GPR] = &[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; From 1b0bda82dd0d07c073c9b839f76a4f35cfc4dfa0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 10:25:58 +0100 Subject: [PATCH 52/79] feat(interface-types) Rename `write-utf8` to `string-to-memory`. --- lib/interface-types/src/decoders/binary.rs | 12 +- lib/interface-types/src/decoders/wat.rs | 16 +- lib/interface-types/src/encoders/binary.rs | 10 +- lib/interface-types/src/encoders/wat.rs | 10 +- .../src/interpreter/instruction.rs | 8 +- .../src/interpreter/instructions/mod.rs | 30 ++-- .../instructions/string_to_memory.rs | 169 ++++++++++++++++++ .../interpreter/instructions/write_utf8.rs | 169 ------------------ lib/interface-types/src/interpreter/mod.rs | 4 +- 9 files changed, 213 insertions(+), 215 deletions(-) create mode 100644 lib/interface-types/src/interpreter/instructions/string_to_memory.rs delete mode 100644 lib/interface-types/src/interpreter/instructions/write_utf8.rs diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 6412cbaa5..befb3dd7c 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -187,11 +187,11 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x03 => (input, Instruction::MemoryToString), 0x04 => { - consume!((input, argument_0) = string(input)?); + consume!((input, argument_0) = uleb(input)?); ( input, - Instruction::WriteUtf8 { - allocator_name: argument_0, + Instruction::StringToMemory { + allocator_index: argument_0 as u32, }, ) } @@ -642,7 +642,7 @@ mod tests { 0x01, 0x01, // Call { function_index: 1 } 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } 0x03, // MemoryToString - 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } + 0x04, 0x01, // StringToMemory { allocator_index: 1 } 0x07, // I32ToS8 0x08, // I32ToS8X 0x09, // I32ToU8 @@ -691,9 +691,7 @@ mod tests { Instruction::Call { function_index: 1 }, Instruction::CallExport { export_name: "abc" }, Instruction::MemoryToString, - Instruction::WriteUtf8 { - allocator_name: "abc", - }, + Instruction::StringToMemory { allocator_index: 1 }, Instruction::I32ToS8, Instruction::I32ToS8X, Instruction::I32ToU8, diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 8a25064f2..3cdf5c1fc 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -30,7 +30,7 @@ mod keyword { custom_keyword!(call); custom_keyword!(call_export = "call-export"); custom_keyword!(memory_to_string = "memory-to-string"); - custom_keyword!(write_utf8 = "write-utf8"); + custom_keyword!(string_to_memory = "string-to-memory"); custom_keyword!(i32_to_s8 = "i32-to-s8"); custom_keyword!(i32_to_s8x = "i32-to-s8x"); custom_keyword!(i32_to_u8 = "i32-to-u8"); @@ -165,11 +165,11 @@ impl<'a> Parse<'a> for Instruction<'a> { parser.parse::()?; Ok(Instruction::MemoryToString) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; - Ok(Instruction::WriteUtf8 { - allocator_name: parser.parse()?, + Ok(Instruction::StringToMemory { + allocator_index: parser.parse()?, }) } else if lookahead.peek::() { parser.parse::()?; @@ -676,7 +676,7 @@ mod tests { "call 7", r#"call-export "foo""#, "memory-to-string", - r#"write-utf8 "foo""#, + "string-to-memory 42", "i32-to-s8", "i32-to-s8x", "i32-to-u8", @@ -722,8 +722,8 @@ mod tests { Instruction::Call { function_index: 7 }, Instruction::CallExport { export_name: "foo" }, Instruction::MemoryToString, - Instruction::WriteUtf8 { - allocator_name: "foo", + Instruction::StringToMemory { + allocator_index: 42, }, Instruction::I32ToS8, Instruction::I32ToS8X, diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 1d6762553..4c0fbe2ca 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -267,9 +267,9 @@ where Instruction::MemoryToString => 0x03_u8.to_bytes(writer)?, - Instruction::WriteUtf8 { allocator_name } => { + Instruction::StringToMemory { allocator_index } => { 0x04_u8.to_bytes(writer)?; - allocator_name.to_bytes(writer)?; + (*allocator_index as u64).to_bytes(writer)?; } Instruction::I32ToS8 => 0x07_u8.to_bytes(writer)?, @@ -557,9 +557,7 @@ mod tests { Instruction::Call { function_index: 1 }, Instruction::CallExport { export_name: "abc" }, Instruction::MemoryToString, - Instruction::WriteUtf8 { - allocator_name: "abc", - }, + Instruction::StringToMemory { allocator_index: 1 }, Instruction::I32ToS8, Instruction::I32ToS8X, Instruction::I32ToU8, @@ -606,7 +604,7 @@ mod tests { 0x01, 0x01, // Call { function_index: 1 } 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } 0x03, // MemoryToString - 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } + 0x04, 0x01, // StringToMemory { allocator_index: 1 } 0x07, // I32ToS8 0x08, // I32ToS8X 0x09, // I32ToU8 diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index c0cff2299..4f0c43411 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -87,8 +87,8 @@ impl<'input> ToString for &Instruction<'input> { Instruction::Call { function_index } => format!("call {}", function_index), Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name), Instruction::MemoryToString => "memory-to-string".into(), - Instruction::WriteUtf8 { allocator_name } => { - format!(r#"write-utf8 "{}""#, allocator_name) + Instruction::StringToMemory { allocator_index } => { + format!(r#"string-to-memory {}"#, allocator_index) } Instruction::I32ToS8 => "i32-to-s8".into(), Instruction::I32ToS8X => "i32-to-s8x".into(), @@ -364,8 +364,8 @@ mod tests { (&Instruction::Call { function_index: 7 }).to_string(), (&Instruction::CallExport { export_name: "foo" }).to_string(), (&Instruction::MemoryToString).to_string(), - (&Instruction::WriteUtf8 { - allocator_name: "foo", + (&Instruction::StringToMemory { + allocator_index: 42, }) .to_string(), (&Instruction::I32ToS8).to_string(), @@ -413,7 +413,7 @@ mod tests { "call 7", r#"call-export "foo""#, "memory-to-string", - r#"write-utf8 "foo""#, + "string-to-memory 42", "i32-to-s8", "i32-to-s8x", "i32-to-u8", diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index 9cccacd05..70818e6c4 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -24,10 +24,10 @@ pub enum Instruction<'input> { /// The `memory-to-string` instruction. MemoryToString, - /// The `write-utf8` instruction. - WriteUtf8 { - /// The allocator function name. - allocator_name: &'input str, + /// The `string-to-memory` instruction. + StringToMemory { + /// The allocator function index. + allocator_index: u32, }, /// The `i32-to-s8,` instruction. diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 3bf7ed4a8..e8912a517 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -3,14 +3,14 @@ mod call; mod call_export; mod lowering_lifting; mod memory_to_string; -mod write_utf8; +mod string_to_memory; pub(crate) use argument_get::argument_get; pub(crate) use call::call; pub(crate) use call_export::call_export; pub(crate) use lowering_lifting::*; pub(crate) use memory_to_string::memory_to_string; -pub(crate) use write_utf8::write_utf8; +pub(crate) use string_to_memory::string_to_memory; #[cfg(test)] pub(crate) mod tests { @@ -133,23 +133,12 @@ pub(crate) mod tests { }, }, ); - hashmap.insert( - "alloc".into(), - Export { - inputs: vec![InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |arguments: &[InterfaceValue]| { - let _size: i32 = (&arguments[0]).try_into().unwrap(); - - Ok(vec![InterfaceValue::I32(0)]) - }, - }, - ); hashmap }, locals_or_imports: { let mut hashmap = HashMap::new(); + // sum hashmap.insert( 42, LocalImport { @@ -163,6 +152,19 @@ pub(crate) mod tests { }, }, ); + // string allocator + hashmap.insert( + 43, + LocalImport { + inputs: vec![InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { + let _size: i32 = (&arguments[0]).try_into().unwrap(); + + Ok(vec![InterfaceValue::I32(0)]) + }, + }, + ); hashmap }, diff --git a/lib/interface-types/src/interpreter/instructions/string_to_memory.rs b/lib/interface-types/src/interpreter/instructions/string_to_memory.rs new file mode 100644 index 000000000..57414d03f --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/string_to_memory.rs @@ -0,0 +1,169 @@ +use crate::interpreter::wasm::{ + structures::{FunctionIndex, TypedIndex}, + values::{InterfaceType, InterfaceValue}, +}; +use std::convert::TryInto; + +executable_instruction!( + string_to_memory(allocator_index: u32, instruction_name: String) -> _ { + move |runtime| -> _ { + let instance = &mut runtime.wasm_instance; + let index = FunctionIndex::new(allocator_index as usize); + + let allocator = instance.local_or_import(index).ok_or_else(|| { + format!( + "`{}` failed because the function `{}` (the allocator) doesn't exist.", + instruction_name, + allocator_index + ) + })?; + + if allocator.inputs() != [InterfaceType::I32] || allocator.outputs() != [InterfaceType::I32] { + return Err(format!( + "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", + instruction_name, + allocator_index, + )); + } + + let string = runtime.stack.pop1().ok_or_else(|| { + format!( + "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", + instruction_name, + allocator_index, + 1 + ) + })?; + + let string: String = (&string).try_into()?; + let string_bytes = string.as_bytes(); + let string_length = (string_bytes.len() as i32) + .try_into() + .map_err(|error| format!("{}", error))?; + + let outputs = allocator.call(&[InterfaceValue::I32(string_length)]).map_err(|_| format!( + "`{}` failed when calling the allocator `{}`.", + instruction_name, + allocator_index, + ))?; + + let string_pointer: i32 = (&outputs[0]).try_into()?; + + let memory_view = instance + .memory(0) + .ok_or_else(|| { + format!( + "`{}` failed because there is no memory to write into.", + instruction_name + ) + })? + .view(); + + for (nth, byte) in string_bytes.iter().enumerate() { + memory_view[string_pointer as usize + nth].set(*byte); + } + + runtime.stack.push(InterfaceValue::I32(string_pointer)); + runtime.stack.push(InterfaceValue::I32(string_length)); + + Ok(()) + } + } +); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_string_to_memory = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::StringToMemory { allocator_index: 43 }, + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + stack: [ + InterfaceValue::I32(0), + // ^^^^^^ pointer + InterfaceValue::I32(13), + // ^^^^^^^ length + ] + ); + + test_executable_instruction!( + test_string_to_memory__roundtrip_with_memory_to_string = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::StringToMemory { allocator_index: 43 }, + Instruction::MemoryToString, + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + stack: [InterfaceValue::String("Hello, World!".into())], + ); + + test_executable_instruction!( + test_string_to_memory__allocator_does_not_exist = + instructions: [Instruction::StringToMemory { allocator_index: 43 }], + invocation_inputs: [], + instance: Instance { ..Default::default() }, + error: r#"`string-to-memory 43` failed because the function `43` (the allocator) doesn't exist."#, + ); + + test_executable_instruction!( + test_string_to_memory__stack_is_too_small = + instructions: [ + Instruction::StringToMemory { allocator_index: 43 } + // ^^ `43` expects 1 value on the stack, none is present + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + error: r#"`string-to-memory 43` cannot call the allocator `43` because there is not enough data on the stack for the arguments (needs 1)."#, + ); + + test_executable_instruction!( + test_string_to_memory__failure_when_calling_the_allocator = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::StringToMemory { allocator_index: 153 } + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: { + let mut instance = Instance::new(); + instance.locals_or_imports.insert( + 153, + LocalImport { + inputs: vec![InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + instance + }, + error: r#"`string-to-memory 153` failed when calling the allocator `153`."#, + ); + + test_executable_instruction!( + test_string_to_memory__invalid_allocator_signature = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::StringToMemory { allocator_index: 153 } + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: { + let mut instance = Instance::new(); + instance.locals_or_imports.insert( + 153, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![], + function: |_| Err(()), + }, + ); + + instance + }, + error: r#"`string-to-memory 153` failed because the allocator `153` has an invalid signature (expects [I32] -> [I32])."#, + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/write_utf8.rs b/lib/interface-types/src/interpreter/instructions/write_utf8.rs deleted file mode 100644 index a3f53d550..000000000 --- a/lib/interface-types/src/interpreter/instructions/write_utf8.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::interpreter::wasm::values::{InterfaceType, InterfaceValue}; -use std::convert::TryInto; - -executable_instruction!( - write_utf8(allocator_name: String, instruction_name: String) -> _ { - move |runtime| -> _ { - let instance = &mut runtime.wasm_instance; - - match instance.export(&allocator_name) { - Some(allocator) => { - if allocator.inputs() != [InterfaceType::I32] || - allocator.outputs() != [InterfaceType::I32] { - return Err(format!( - "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", - instruction_name, - allocator_name, - )) - } - - match instance.memory(0) { - Some(memory) => match runtime.stack.pop1() { - Some(string) => { - let memory_view = memory.view(); - - let string: String = (&string).try_into()?; - let string_bytes = string.as_bytes(); - let string_length = (string_bytes.len() as i32) - .try_into() - .map_err(|error| format!("{}", error))?; - - match allocator.call(&[InterfaceValue::I32(string_length)]) { - Ok(outputs) => { - let string_pointer: i32 = (&outputs[0]).try_into()?; - - for (nth, byte) in string_bytes.iter().enumerate() { - memory_view[string_pointer as usize + nth].set(*byte); - } - - runtime.stack.push(InterfaceValue::I32(string_pointer)); - runtime.stack.push(InterfaceValue::I32(string_length)); - - Ok(()) - } - Err(_) => Err(format!( - "`{}` failed when calling the allocator `{}`.", - instruction_name, - allocator_name, - )) - } - } - None => Err(format!( - "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - allocator_name, - 1 - )) - } - None => Err(format!( - "`{}` failed because there is no memory to write into.", - instruction_name - )) - } - } - None => Err(format!( - "`{}` failed because the exported function `{}` (the allocator) doesn't exist.", - instruction_name, - allocator_name - )) - } - } - } -); - -#[cfg(test)] -mod tests { - test_executable_instruction!( - test_write_utf8 = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc" }, - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: Instance::new(), - stack: [ - InterfaceValue::I32(0), - // ^^^^^^ pointer - InterfaceValue::I32(13), - // ^^^^^^^ length - ] - ); - - test_executable_instruction!( - test_write_utf8__roundtrip_with_memory_to_string = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc" }, - Instruction::MemoryToString, - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: Instance::new(), - stack: [InterfaceValue::String("Hello, World!".into())], - ); - - test_executable_instruction!( - test_write_utf8__allocator_does_not_exist = - instructions: [Instruction::WriteUtf8 { allocator_name: "alloc" }], - invocation_inputs: [], - instance: Instance { ..Default::default() }, - error: r#"`write-utf8 "alloc"` failed because the exported function `alloc` (the allocator) doesn't exist."#, - ); - - test_executable_instruction!( - test_write_utf8__stack_is_too_small = - instructions: [ - Instruction::WriteUtf8 { allocator_name: "alloc" } - // ^^^^^ `alloc` expects 1 value on the stack, none is present - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: Instance::new(), - error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is not enough data on the stack for the arguments (needs 1)."#, - ); - - test_executable_instruction!( - test_write_utf8__failure_when_calling_the_allocator = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc-fail" } - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: { - let mut instance = Instance::new(); - instance.exports.insert( - "alloc-fail".into(), - Export { - inputs: vec![InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - instance - }, - error: r#"`write-utf8 "alloc-fail"` failed when calling the allocator `alloc-fail`."#, - ); - - test_executable_instruction!( - test_write_utf8__invalid_allocator_signature = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc-fail" } - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: { - let mut instance = Instance::new(); - instance.exports.insert( - "alloc-fail".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![], - function: |_| Err(()), - }, - ); - - instance - }, - error: r#"`write-utf8 "alloc-fail"` failed because the allocator `alloc-fail` has an invalid signature (expects [I32] -> [I32])."#, - ); -} diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 1dfd5af7d..29e1194a1 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -203,8 +203,8 @@ where instructions::call_export((*export_name).to_owned(), instruction_name) } Instruction::MemoryToString => instructions::memory_to_string(instruction_name), - Instruction::WriteUtf8 { allocator_name } => { - instructions::write_utf8((*allocator_name).to_owned(), instruction_name) + Instruction::StringToMemory { allocator_index } => { + instructions::string_to_memory(*allocator_index, instruction_name) } Instruction::I32ToS8 => instructions::i32_to_s8(), From e9de8f999976dc359fdf5017abac2e7ae8b96ad1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 10:27:17 +0100 Subject: [PATCH 53/79] feat(interface-types) `write-utf8` is renamed `string-to-memory`. --- lib/runtime-core/src/typed_func.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 0b29d54ea..6ae883b1a 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -323,12 +323,17 @@ impl<'a> DynamicFunc<'a> { .iter() .enumerate() .map(|(i, t)| { - let i = i + 1; // skip vmctx + let i = i as isize + 1; // skip vmctx + match *t { - Type::I32 => Value::I32(*args.offset(i as _) as i32), - Type::I64 => Value::I64(*args.offset(i as _) as i64), - Type::F32 => Value::F32(f32::from_bits(*args.offset(i as _) as u32)), - Type::F64 => Value::F64(f64::from_bits(*args.offset(i as _) as u64)), + Type::I32 => Value::I32(*args.offset(i) as i32), + Type::I64 => Value::I64(*args.offset(i) as i64), + Type::F32 => { + eprintln!("{:?}", 3.0f32.to_le_bytes()); + eprintln!("{:#064x}", *args.offset(i)); + Value::F32(f32::from_bits(*args.offset(i) as u32)) + } + Type::F64 => Value::F64(f64::from_bits(*args.offset(i) as u64)), Type::V128 => { todo!("enter_host_polymorphic: 128-bit types are not supported") } @@ -336,6 +341,7 @@ impl<'a> DynamicFunc<'a> { }) .collect(); let rets = (ctx.func)(vmctx, &args); + if rets.len() == 0 { 0 } else if rets.len() == 1 { From 52312ff0adb6e97624121d41295fc600fff5caf3 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 10:37:09 +0100 Subject: [PATCH 54/79] feat(interface-types) Remove the `'input` lifetime on `Instruction`. --- lib/interface-types/src/ast.rs | 6 +++--- lib/interface-types/src/decoders/wat.rs | 6 +++--- lib/interface-types/src/encoders/binary.rs | 4 ++-- lib/interface-types/src/encoders/wat.rs | 4 ++-- lib/interface-types/src/interpreter/instruction.rs | 2 +- lib/interface-types/src/interpreter/mod.rs | 3 +-- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 7b86c64ed..21b76020d 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -89,12 +89,12 @@ pub struct Export<'input> { /// Represents an adapter. #[derive(PartialEq, Debug)] -pub struct Adapter<'input> { +pub struct Adapter { /// The adapter function type. pub function_type: u32, /// The instructions. - pub instructions: Vec>, + pub instructions: Vec, } /// Represents an implementation. @@ -137,7 +137,7 @@ pub struct Interfaces<'input> { pub imports: Vec>, /// All the adapters. - pub adapters: Vec>, + pub adapters: Vec, /// All the exported functions. pub exports: Vec>, diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 069db8dc5..809b5e5a6 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -137,7 +137,7 @@ impl Parse<'_> for InterfaceType { } } -impl<'a> Parse<'a> for Instruction<'a> { +impl<'a> Parse<'a> for Instruction { #[allow(clippy::cognitive_complexity)] fn parse(parser: Parser<'a>) -> Result { let mut lookahead = parser.lookahead1(); @@ -392,7 +392,7 @@ impl Parse<'_> for FunctionType { enum Interface<'a> { Type(Type), Import(Import<'a>), - Adapter(Adapter<'a>), + Adapter(Adapter), Export(Export<'a>), Implementation(Implementation), } @@ -520,7 +520,7 @@ impl<'a> Parse<'a> for Implementation { } } -impl<'a> Parse<'a> for Adapter<'a> { +impl<'a> Parse<'a> for Adapter { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index b73d527be..cd322232d 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -162,7 +162,7 @@ where /// Encode an `Adapter` into bytes. /// /// Decoder is in `decoders::binary::adapters`. -impl ToBytes for Adapter<'_> +impl ToBytes for Adapter where W: Write, { @@ -244,7 +244,7 @@ where /// Encode an `Instruction` into bytes. /// /// Decoder is `decoders::binary::instruction`. -impl ToBytes for Instruction<'_> +impl ToBytes for Instruction where W: Write, { diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index ef5a09192..74a9d1a15 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -80,7 +80,7 @@ impl ToString for &InterfaceType { } /// Encode an `Instruction` into a string. -impl<'input> ToString for &Instruction<'input> { +impl ToString for &Instruction { fn to_string(&self) -> String { match self { Instruction::ArgumentGet { index } => format!("arg.get {}", index), @@ -194,7 +194,7 @@ impl<'input> ToString for &Import<'input> { } /// Encode an `Adapter` into a string. -impl<'input> ToString for &Adapter<'input> { +impl ToString for &Adapter { fn to_string(&self) -> String { format!( r#"(@interface func (type {function_type}){instructions})"#, diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index 9f7cc399e..109f269b8 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -2,7 +2,7 @@ /// Represents all the possible WIT instructions. #[derive(PartialEq, Debug)] -pub enum Instruction<'input> { +pub enum Instruction { /// The `arg.get` instruction. ArgumentGet { /// The argument index. diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 2cf6b21dd..bdc939fe2 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -174,8 +174,7 @@ where } /// Transforms a `Vec` into an `Interpreter`. -impl<'binary_input, Instance, Export, LocalImport, Memory, MemoryView> - TryFrom<&Vec>> +impl TryFrom<&Vec> for Interpreter where Export: wasm::structures::Export, From c1e40f165e57d41e64dfdd6ee5bc843cc891f1a2 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 15:58:27 +0100 Subject: [PATCH 55/79] fix: Revert a file that wasn't a commit candidate. --- lib/runtime-core/src/typed_func.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 6ae883b1a..0b29d54ea 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -323,17 +323,12 @@ impl<'a> DynamicFunc<'a> { .iter() .enumerate() .map(|(i, t)| { - let i = i as isize + 1; // skip vmctx - + let i = i + 1; // skip vmctx match *t { - Type::I32 => Value::I32(*args.offset(i) as i32), - Type::I64 => Value::I64(*args.offset(i) as i64), - Type::F32 => { - eprintln!("{:?}", 3.0f32.to_le_bytes()); - eprintln!("{:#064x}", *args.offset(i)); - Value::F32(f32::from_bits(*args.offset(i) as u32)) - } - Type::F64 => Value::F64(f64::from_bits(*args.offset(i) as u64)), + Type::I32 => Value::I32(*args.offset(i as _) as i32), + Type::I64 => Value::I64(*args.offset(i as _) as i64), + Type::F32 => Value::F32(f32::from_bits(*args.offset(i as _) as u32)), + Type::F64 => Value::F64(f64::from_bits(*args.offset(i as _) as u64)), Type::V128 => { todo!("enter_host_polymorphic: 128-bit types are not supported") } @@ -341,7 +336,6 @@ impl<'a> DynamicFunc<'a> { }) .collect(); let rets = (ctx.func)(vmctx, &args); - if rets.len() == 0 { 0 } else if rets.len() == 1 { From 58c3b3489a2d2b995a0b601e9cbb7affd0c6c3a1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 16:00:05 +0100 Subject: [PATCH 56/79] doc(changelog) Add #1284. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0856e49dc..f90c87997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` ## 0.15.0 - 2020-03-04 From f0c97a1b81fbd948faf5d499fc6a0126d9db3767 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 15:41:49 +0100 Subject: [PATCH 57/79] feat(interface-types) Use better errors. The new `errors` module contains structure to represent errors, instead of using basic strings. The first usage is in the interpreter itself. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/errors.rs | 219 ++++++++++++++++++ .../src/interpreter/instruction.rs | 2 +- .../interpreter/instructions/argument_get.rs | 15 +- .../src/interpreter/instructions/call_core.rs | 70 +++--- .../instructions/lowering_lifting.rs | 54 +++-- .../instructions/memory_to_string.rs | 101 ++++---- .../src/interpreter/instructions/mod.rs | 21 ++ .../instructions/string_to_memory.rs | 82 ++++--- lib/interface-types/src/interpreter/mod.rs | 89 +++---- .../src/interpreter/wasm/values.rs | 49 ++-- lib/interface-types/src/lib.rs | 1 + lib/interface-types/src/macros.rs | 2 +- 13 files changed, 500 insertions(+), 207 deletions(-) create mode 100644 lib/interface-types/src/errors.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 21b76020d..9828043db 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -5,7 +5,7 @@ use crate::interpreter::Instruction; use std::str; /// Represents the types supported by WIT. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub enum InterfaceType { /// A 8-bits signed integer. S8, diff --git a/lib/interface-types/src/errors.rs b/lib/interface-types/src/errors.rs new file mode 100644 index 000000000..24ca5566a --- /dev/null +++ b/lib/interface-types/src/errors.rs @@ -0,0 +1,219 @@ +//! The error module contains all the data structures that represent +//! an error. + +use crate::{ast::InterfaceType, interpreter::Instruction}; +use std::{ + fmt::{self, Display, Formatter}, + result::Result, + string::{self, ToString}, +}; + +/// A type alias for instruction's results. +pub type InstructionResult = Result; + +/// A type alias for the interpreter result. +pub type InterpreterResult = Result; + +/// Structure to represent errors when casting from an `InterfaceType` +/// to a native value. +#[derive(Debug)] +pub struct WasmValueNativeCastError { + /// The initial type. + pub from: InterfaceType, + + /// The targeted type. + /// + /// `InterfaceType` is used to represent the native type by + /// associativity. + pub to: InterfaceType, +} + +/// Structure to represent the errors for instructions. +#[derive(Debug)] +pub struct InstructionError { + /// The instruction that raises the error. + pub instruction: Instruction, + + /// The error kind. + pub error_kind: InstructionErrorKind, +} + +impl InstructionError { + pub(crate) fn new(instruction: Instruction, error_kind: InstructionErrorKind) -> Self { + Self { + instruction, + error_kind, + } + } +} + +impl Display for InstructionError { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + write!( + formatter, + "`{}` {}", + (&self.instruction).to_string(), + self.error_kind + ) + } +} + +/// The kind of instruction errors. +#[derive(Debug)] +pub enum InstructionErrorKind { + /// The instruction needs to read an invocation input at index `index`, but it's missing. + InvocationInputIsMissing { + /// The invocation input index. + index: u32, + }, + + /// Failed to cast from a WIT value to a native value. + ToNative(WasmValueNativeCastError), + + /// Failed to cast from `from` to `to`. + LoweringLifting { + /// The initial type. + from: InterfaceType, + + /// The targeted type. + to: InterfaceType, + }, + + /// Read a value from the stack, but it doesn't have the expected + /// type. + InvalidValueOnTheStack { + /// The expected type. + expected_type: InterfaceType, + + /// The received type. + received_type: InterfaceType, + }, + + /// Need to read some values from the stack, but it doesn't + /// contain enough data. + StackIsTooSmall { + /// The number of values that were needed. + needed: usize, + }, + + /// The local or import function doesn't exist. + LocalOrImportIsMissing { + /// The local or import function index. + function_index: u32, + }, + + /// Values given to a local or import function doesn't match the + /// function signature. + LocalOrImportSignatureMismatch { + /// The local or import function index. + function_index: u32, + + /// The expected signature. + expected: (Vec, Vec), + + /// The received signature. + received: (Vec, Vec), + }, + + /// Failed to call a local or import function. + LocalOrImportCall { + /// The local or import function index that has been called. + function_index: u32, + }, + + /// The memory doesn't exist. + MemoryIsMissing { + /// The memory indeX. + memory_index: u32, + }, + + /// Tried to read out of bounds of the memory. + MemoryOutOfBoundsAccess { + /// The access index. + index: usize, + + /// The memory length. + length: usize, + }, + + /// The string contains invalid UTF-8 encoding. + String(string::FromUtf8Error), +} + +impl Display for InstructionErrorKind { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + match self { + Self::InvocationInputIsMissing { index } => write!( + formatter, + "cannot access invocation inputs #{} because it doesn't exist", + index + ), + + Self::ToNative(WasmValueNativeCastError { from, .. }) => write!( + formatter, + "failed to cast the WIT value `{:?}` to its native type", + from, + ), + + Self::LoweringLifting { from, to } => { + write!(formatter, "failed to cast `{:?}` to `{:?}`", from, to) + } + + Self::InvalidValueOnTheStack { + expected_type, + received_type, + } => write!( + formatter, + "read a value of type `{:?}` from the stack, but the type `{:?}` was expected", + received_type, expected_type, + ), + + Self::StackIsTooSmall { needed } => write!( + formatter, + "needed to read `{}` value(s) from the stack, but it doesn't contain enough data", + needed + ), + + Self::LocalOrImportIsMissing { function_index } => write!( + formatter, + "the local or import function `{}` doesn't exist", + function_index + ), + + Self::LocalOrImportSignatureMismatch { function_index, expected, received } => write!( + formatter, + "the local or import function `{}` has the signature `{:?} -> {:?}` but it received values of kind `{:?} -> {:?}`", + function_index, + expected.0, + expected.1, + received.0, + received.1, + ), + + Self::LocalOrImportCall { function_index } => write!( + formatter, + "failed while calling the local or import function `{}`", + function_index + ), + + Self::MemoryIsMissing { memory_index } => write!( + formatter, + "memory `{}` does not exist", + memory_index, + ), + + Self::MemoryOutOfBoundsAccess { index, length } => write!( + formatter, + "read out of the memory bounds (index {} > memory length {})", + index, + length, + ), + + Self::String(error) => write!( + formatter, + "{}", + error + ), + } + } +} diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index 109f269b8..425cdad5c 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -1,7 +1,7 @@ //use crate::ast::InterfaceType; /// Represents all the possible WIT instructions. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone, Copy)] pub enum Instruction { /// The `arg.get` instruction. ArgumentGet { diff --git a/lib/interface-types/src/interpreter/instructions/argument_get.rs b/lib/interface-types/src/interpreter/instructions/argument_get.rs index e530d880a..dd161e158 100644 --- a/lib/interface-types/src/interpreter/instructions/argument_get.rs +++ b/lib/interface-types/src/interpreter/instructions/argument_get.rs @@ -1,12 +1,17 @@ +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::Instruction, +}; + executable_instruction!( - argument_get(index: u32, instruction_name: String) -> _ { + argument_get(index: u32, instruction: Instruction) -> _ { move |runtime| -> _ { let invocation_inputs = runtime.invocation_inputs; if index >= (invocation_inputs.len() as u32) { - return Err(format!( - "`{}` cannot access argument #{} because it doesn't exist.", - instruction_name, index + return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvocationInputIsMissing { index }, )); } @@ -49,6 +54,6 @@ mod tests { instructions: [Instruction::ArgumentGet { index: 1 }], invocation_inputs: [InterfaceValue::I32(42)], instance: Instance::new(), - error: "`arg.get 1` cannot access argument #1 because it doesn't exist." + error: "`arg.get 1` cannot access invocation inputs #1 because it doesn't exist" ); } diff --git a/lib/interface-types/src/interpreter/instructions/call_core.rs b/lib/interface-types/src/interpreter/instructions/call_core.rs index 3179c9080..dfaf520e4 100644 --- a/lib/interface-types/src/interpreter/instructions/call_core.rs +++ b/lib/interface-types/src/interpreter/instructions/call_core.rs @@ -1,10 +1,14 @@ -use crate::interpreter::wasm::{ - structures::{FunctionIndex, TypedIndex}, - values::InterfaceType, +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::wasm::{ + structures::{FunctionIndex, TypedIndex}, + values::InterfaceType, + }, + interpreter::Instruction, }; executable_instruction!( - call_core(function_index: usize, instruction_name: String) -> _ { + call_core(function_index: usize, instruction: Instruction) -> _ { move |runtime| -> _ { let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(function_index); @@ -21,12 +25,16 @@ executable_instruction!( .collect::>(); if input_types != local_or_import.inputs() { - return Err(format!( - "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", - instruction_name, - function_index, - local_or_import.inputs(), - )) + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: function_index as u32, + expected: (local_or_import.inputs().to_vec(), vec![]), + received: (input_types, vec![]), + } + ) + ) } match local_or_import.call(&inputs) { @@ -37,26 +45,28 @@ executable_instruction!( Ok(()) } - Err(_) => Err(format!( - "`{}` failed when calling the local or imported function `{}`.", - instruction_name, - function_index - )) + Err(_) => Err( + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { function_index: function_index as u32, }, + ) + ) } } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - function_index, - inputs_cardinality, - )) + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: inputs_cardinality }, + ) + ) } } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because it doesn't exist.", - instruction_name, - function_index, - )) + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportIsMissing { function_index: function_index as u32, }, + ) + ) } } } @@ -89,7 +99,7 @@ mod tests { InterfaceValue::I32(4), ], instance: Default::default(), - error: r#"`call-core 42` cannot call the local or imported function `42` because it doesn't exist."#, + error: r#"`call-core 42` the local or import function `42` doesn't exist"#, ); test_executable_instruction!( @@ -104,7 +114,7 @@ mod tests { InterfaceValue::I32(4), ], instance: Instance::new(), - error: r#"`call-core 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, + error: r#"`call-core 42` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#, ); test_executable_instruction!( @@ -120,7 +130,7 @@ mod tests { // ^^^ mismatch with `42` signature ], instance: Instance::new(), - error: r#"`call-core 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + error: r#"`call-core 42` the local or import function `42` has the signature `[I32, I32] -> []` but it received values of kind `[I32, I64] -> []`"#, ); test_executable_instruction!( @@ -151,7 +161,7 @@ mod tests { }, ..Default::default() }, - error: r#"`call-core 42` failed when calling the local or imported function `42`."#, + error: r#"`call-core 42` failed while calling the local or import function `42`"#, ); test_executable_instruction!( diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index c24fd992b..f18c24385 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -1,10 +1,14 @@ -use crate::interpreter::wasm::values::InterfaceValue; +use crate::{ + ast::InterfaceType, + errors::{InstructionError, InstructionErrorKind}, + interpreter::{wasm::values::InterfaceValue, Instruction}, +}; use std::convert::TryInto; macro_rules! lowering_lifting { ($instruction_function_name:ident, $instruction_name:expr, $from_variant:ident, $to_variant:ident) => { executable_instruction!( - $instruction_function_name() -> _ { + $instruction_function_name(instruction: Instruction) -> _ { move |runtime| -> _ { match runtime.stack.pop1() { Some(InterfaceValue::$from_variant(value)) => { @@ -12,39 +16,33 @@ macro_rules! lowering_lifting { .stack .push(InterfaceValue::$to_variant(value.try_into().map_err( |_| { - concat!( - "Failed to cast `", - stringify!($from_variant), - "` to `", - stringify!($to_variant), - "`." - ).to_string() + InstructionError::new( + instruction, + InstructionErrorKind::LoweringLifting { from: InterfaceType::$from_variant, to: InterfaceType::$to_variant }, + ) }, )?)) } Some(wrong_value) => { - return Err(format!( - concat!( - "Instruction `", - $instruction_name, - "` expects a `", - stringify!($from_variant), - "` value on the stack, got `{:?}`.", - ), - wrong_value - + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::$from_variant, + received_type: (&wrong_value).into(), + } + ) ) - .to_string()) }, None => { - return Err(concat!( - "Instruction `", - $instruction_name, - "` needs one value on the stack." + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + ) ) - .to_string()) } } @@ -103,7 +101,7 @@ mod tests { instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], invocation_inputs: [InterfaceValue::I32(128)], instance: Instance::new(), - error: "Failed to cast `I32` to `S8`." + error: "`i32-to-s8` failed to cast `I32` to `S8`" ); test_executable_instruction!( @@ -111,7 +109,7 @@ mod tests { instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], invocation_inputs: [InterfaceValue::I64(42)], instance: Instance::new(), - error: "Instruction `i32-to-s8` expects a `I32` value on the stack, got `I64(42)`." + error: "`i32-to-s8` read a value of type `I64` from the stack, but the type `I32` was expected" ); test_executable_instruction!( @@ -119,7 +117,7 @@ mod tests { instructions: [Instruction::I32ToS8], invocation_inputs: [InterfaceValue::I32(42)], instance: Instance::new(), - error: "Instruction `i32-to-s8` needs one value on the stack." + error: "`i32-to-s8` needed to read `1` value(s) from the stack, but it doesn't contain enough data" ); test_executable_instruction!( diff --git a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs index 678d08d74..b1b923035 100644 --- a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -1,52 +1,69 @@ -use crate::interpreter::wasm::values::InterfaceValue; -use std::{cell::Cell, convert::TryFrom}; +use super::to_native; +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::{wasm::values::InterfaceValue, Instruction}, +}; +use std::cell::Cell; executable_instruction!( - memory_to_string(instruction_name: String) -> _ { + memory_to_string(instruction: Instruction) -> _ { move |runtime| -> _ { match runtime.stack.pop(2) { - Some(inputs) => match runtime.wasm_instance.memory(0) { - Some(memory) => { - let length = i32::try_from(&inputs[0])? as usize; - let pointer = i32::try_from(&inputs[1])? as usize; - let memory_view = memory.view(); + Some(inputs) => { + let memory_index: u32 = 0; - if memory_view.len() < pointer + length { - return Err(format!( - "`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).", - instruction_name, - pointer + length, - memory_view.len() - )); - } + match runtime.wasm_instance.memory(memory_index as usize) { + Some(memory) => { + let length = to_native::(&inputs[0], instruction)? as usize; + let pointer = to_native::(&inputs[1], instruction)? as usize; + let memory_view = memory.view(); - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); - - match String::from_utf8(data) { - Ok(string) => { - runtime.stack.push(InterfaceValue::String(string)); - - Ok(()) + if memory_view.len() < pointer + length { + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::MemoryOutOfBoundsAccess { + index: pointer + length, + length: memory_view.len(), + } + ), + ) + } + + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); + + match String::from_utf8(data) { + Ok(string) => { + runtime.stack.push(InterfaceValue::String(string)); + + Ok(()) + } + Err(utf8_error) => Err( + InstructionError::new( + instruction, + InstructionErrorKind::String(utf8_error) + ), + ) } - Err(utf8_error) => Err(format!( - "`{}` failed because the read string isn't UTF-8 valid ({}).", - instruction_name, - utf8_error, - )) } + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index } + ), + ) } - None => Err(format!( - "`{}` failed because there is no memory to read.", - instruction_name - )) } - None => Err(format!( - "`{}` failed because there is not enough data on the stack (needs 2).", - instruction_name, - )) + + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 } + ), + ) } } } @@ -91,7 +108,7 @@ mod tests { memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), ..Default::default() }, - error: r#"`memory-to-string` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, + error: r#"`memory-to-string` read out of the memory bounds (index 13 > memory length 6)"#, ); test_executable_instruction!( @@ -111,7 +128,7 @@ mod tests { memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::>>()), ..Default::default() }, - error: r#"`memory-to-string` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, + error: r#"`memory-to-string` invalid utf-8 sequence of 1 bytes from index 1"#, ); test_executable_instruction!( @@ -126,6 +143,6 @@ mod tests { InterfaceValue::I32(0), ], instance: Instance::new(), - error: r#"`memory-to-string` failed because there is not enough data on the stack (needs 2)."#, + error: r#"`memory-to-string` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#, ); } diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 8466b9938..4400cf002 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -4,12 +4,33 @@ mod lowering_lifting; mod memory_to_string; mod string_to_memory; +use crate::{ + errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError}, + interpreter::{ + wasm::values::{InterfaceValue, NativeType}, + Instruction, + }, +}; pub(crate) use argument_get::argument_get; pub(crate) use call_core::call_core; pub(crate) use lowering_lifting::*; pub(crate) use memory_to_string::memory_to_string; +use std::convert::TryFrom; pub(crate) use string_to_memory::string_to_memory; +/// Just a short helper to map the error of a cast from an +/// `InterfaceValue` to a native value. +pub(crate) fn to_native<'a, T>( + wit_value: &'a InterfaceValue, + instruction: Instruction, +) -> InstructionResult +where + T: NativeType + TryFrom<&'a InterfaceValue, Error = WasmValueNativeCastError>, +{ + T::try_from(wit_value) + .map_err(|error| InstructionError::new(instruction, InstructionErrorKind::ToNative(error))) +} + #[cfg(test)] pub(crate) mod tests { use crate::interpreter::wasm::{ diff --git a/lib/interface-types/src/interpreter/instructions/string_to_memory.rs b/lib/interface-types/src/interpreter/instructions/string_to_memory.rs index 57414d03f..a4341a7c3 100644 --- a/lib/interface-types/src/interpreter/instructions/string_to_memory.rs +++ b/lib/interface-types/src/interpreter/instructions/string_to_memory.rs @@ -1,60 +1,66 @@ -use crate::interpreter::wasm::{ - structures::{FunctionIndex, TypedIndex}, - values::{InterfaceType, InterfaceValue}, +use super::to_native; +use crate::{ + ast::InterfaceType, + errors::{InstructionError, InstructionErrorKind}, + interpreter::{ + wasm::{ + structures::{FunctionIndex, TypedIndex}, + values::InterfaceValue, + }, + Instruction, + }, }; -use std::convert::TryInto; executable_instruction!( - string_to_memory(allocator_index: u32, instruction_name: String) -> _ { + string_to_memory(allocator_index: u32, instruction: Instruction) -> _ { move |runtime| -> _ { let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(allocator_index as usize); let allocator = instance.local_or_import(index).ok_or_else(|| { - format!( - "`{}` failed because the function `{}` (the allocator) doesn't exist.", - instruction_name, - allocator_index + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportIsMissing { function_index: allocator_index }, ) })?; if allocator.inputs() != [InterfaceType::I32] || allocator.outputs() != [InterfaceType::I32] { - return Err(format!( - "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", - instruction_name, - allocator_index, - )); + return Err(InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: allocator_index, + expected: (vec![InterfaceType::I32], vec![InterfaceType::I32]), + received: (allocator.inputs().to_vec(), allocator.outputs().to_vec()) + } + )) } let string = runtime.stack.pop1().ok_or_else(|| { - format!( - "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - allocator_index, - 1 + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 } ) })?; - let string: String = (&string).try_into()?; + let string: String = to_native(&string, instruction)?; let string_bytes = string.as_bytes(); - let string_length = (string_bytes.len() as i32) - .try_into() - .map_err(|error| format!("{}", error))?; + let string_length = string_bytes.len() as i32; - let outputs = allocator.call(&[InterfaceValue::I32(string_length)]).map_err(|_| format!( - "`{}` failed when calling the allocator `{}`.", - instruction_name, - allocator_index, - ))?; - - let string_pointer: i32 = (&outputs[0]).try_into()?; + let outputs = allocator.call(&[InterfaceValue::I32(string_length)]).map_err(|_| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { function_index: allocator_index }, + ) + })?; + let string_pointer: i32 = to_native(&outputs[0], instruction)?; + let memory_index: u32 = 0; let memory_view = instance - .memory(0) + .memory(memory_index as usize) .ok_or_else(|| { - format!( - "`{}` failed because there is no memory to write into.", - instruction_name + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index } ) })? .view(); @@ -106,7 +112,7 @@ mod tests { instructions: [Instruction::StringToMemory { allocator_index: 43 }], invocation_inputs: [], instance: Instance { ..Default::default() }, - error: r#"`string-to-memory 43` failed because the function `43` (the allocator) doesn't exist."#, + error: r#"`string-to-memory 43` the local or import function `43` doesn't exist"#, ); test_executable_instruction!( @@ -117,7 +123,7 @@ mod tests { ], invocation_inputs: [InterfaceValue::String("Hello, World!".into())], instance: Instance::new(), - error: r#"`string-to-memory 43` cannot call the allocator `43` because there is not enough data on the stack for the arguments (needs 1)."#, + error: r#"`string-to-memory 43` needed to read `1` value(s) from the stack, but it doesn't contain enough data"#, ); test_executable_instruction!( @@ -141,7 +147,7 @@ mod tests { instance }, - error: r#"`string-to-memory 153` failed when calling the allocator `153`."#, + error: r#"`string-to-memory 153` failed while calling the local or import function `153`"#, ); test_executable_instruction!( @@ -164,6 +170,6 @@ mod tests { instance }, - error: r#"`string-to-memory 153` failed because the allocator `153` has an invalid signature (expects [I32] -> [I32])."#, + error: r#"`string-to-memory 153` the local or import function `153` has the signature `[I32] -> [I32]` but it received values of kind `[I32, I32] -> []`"#, ); } diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index bdc939fe2..122c482d2 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -5,6 +5,7 @@ mod instructions; pub mod stack; pub mod wasm; +use crate::errors::{InstructionResult, InterpreterResult}; pub use instruction::Instruction; use stack::Stack; use std::{convert::TryFrom, marker::PhantomData}; @@ -38,7 +39,9 @@ where /// Type alias for an executable instruction. It's an implementation /// details, but an instruction is a boxed closure instance. pub(crate) type ExecutableInstruction = Box< - dyn Fn(&mut Runtime) -> Result<(), String>, + dyn Fn( + &mut Runtime, + ) -> InstructionResult<()>, >; /// An interpreter is the central piece of this crate. It is a set of @@ -154,7 +157,7 @@ where &self, invocation_inputs: &[InterfaceValue], wasm_instance: &mut Instance, - ) -> Result, String> { + ) -> InterpreterResult> { let mut runtime = Runtime { invocation_inputs, stack: Stack::new(), @@ -165,7 +168,7 @@ where for executable_instruction in self.iter() { match executable_instruction(&mut runtime) { Ok(_) => continue, - Err(message) => return Err(message), + Err(error) => return Err(error), } } @@ -183,62 +186,64 @@ where MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance, { - type Error = String; + type Error = (); fn try_from(instructions: &Vec) -> Result { let executable_instructions = instructions .iter() .map(|instruction| { - let instruction_name = instruction.to_string(); - match instruction { Instruction::ArgumentGet { index } => { - instructions::argument_get(*index, instruction_name) + instructions::argument_get(*index, *instruction) } Instruction::CallCore { function_index } => { - instructions::call_core(*function_index, instruction_name) + instructions::call_core(*function_index, *instruction) } - Instruction::MemoryToString => instructions::memory_to_string(instruction_name), + Instruction::MemoryToString => instructions::memory_to_string(*instruction), Instruction::StringToMemory { allocator_index } => { - instructions::string_to_memory(*allocator_index, instruction_name) + instructions::string_to_memory(*allocator_index, *instruction) } - Instruction::I32ToS8 => instructions::i32_to_s8(), + Instruction::I32ToS8 => instructions::i32_to_s8(*instruction), //Instruction::I32ToS8X - Instruction::I32ToU8 => instructions::i32_to_u8(), - Instruction::I32ToS16 => instructions::i32_to_s16(), + Instruction::I32ToU8 => instructions::i32_to_u8(*instruction), + Instruction::I32ToS16 => instructions::i32_to_s16(*instruction), //Instruction::I32ToS16X - Instruction::I32ToU16 => instructions::i32_to_u16(), - Instruction::I32ToS32 => instructions::i32_to_s32(), - Instruction::I32ToU32 => instructions::i32_to_u32(), - Instruction::I32ToS64 => instructions::i32_to_s64(), - Instruction::I32ToU64 => instructions::i32_to_u64(), - Instruction::I64ToS8 => instructions::i64_to_s8(), + Instruction::I32ToU16 => instructions::i32_to_u16(*instruction), + Instruction::I32ToS32 => instructions::i32_to_s32(*instruction), + Instruction::I32ToU32 => instructions::i32_to_u32(*instruction), + Instruction::I32ToS64 => instructions::i32_to_s64(*instruction), + Instruction::I32ToU64 => instructions::i32_to_u64(*instruction), + Instruction::I64ToS8 => instructions::i64_to_s8(*instruction), //Instruction::I64ToS8X - Instruction::I64ToU8 => instructions::i64_to_u8(), - Instruction::I64ToS16 => instructions::i64_to_s16(), + Instruction::I64ToU8 => instructions::i64_to_u8(*instruction), + Instruction::I64ToS16 => instructions::i64_to_s16(*instruction), //Instruction::I64ToS16X - Instruction::I64ToU16 => instructions::i64_to_u16(), - Instruction::I64ToS32 => instructions::i64_to_s32(), - Instruction::I64ToU32 => instructions::i64_to_u32(), - Instruction::I64ToS64 => instructions::i64_to_s64(), - Instruction::I64ToU64 => instructions::i64_to_u64(), - Instruction::S8ToI32 => instructions::s8_to_i32(), - Instruction::U8ToI32 => instructions::u8_to_i32(), - Instruction::S16ToI32 => instructions::s16_to_i32(), - Instruction::U16ToI32 => instructions::u16_to_i32(), - Instruction::S32ToI32 => instructions::s32_to_i32(), - Instruction::U32ToI32 => instructions::u32_to_i32(), - Instruction::S64ToI32 | Instruction::S64ToI32X => instructions::s64_to_i32(), - Instruction::U64ToI32 | Instruction::U64ToI32X => instructions::u64_to_i32(), - Instruction::S8ToI64 => instructions::s8_to_i64(), - Instruction::U8ToI64 => instructions::u8_to_i64(), - Instruction::S16ToI64 => instructions::s16_to_i64(), - Instruction::U16ToI64 => instructions::u16_to_i64(), - Instruction::S32ToI64 => instructions::s32_to_i64(), - Instruction::U32ToI64 => instructions::u32_to_i64(), - Instruction::S64ToI64 => instructions::s64_to_i64(), - Instruction::U64ToI64 => instructions::u64_to_i64(), + Instruction::I64ToU16 => instructions::i64_to_u16(*instruction), + Instruction::I64ToS32 => instructions::i64_to_s32(*instruction), + Instruction::I64ToU32 => instructions::i64_to_u32(*instruction), + Instruction::I64ToS64 => instructions::i64_to_s64(*instruction), + Instruction::I64ToU64 => instructions::i64_to_u64(*instruction), + Instruction::S8ToI32 => instructions::s8_to_i32(*instruction), + Instruction::U8ToI32 => instructions::u8_to_i32(*instruction), + Instruction::S16ToI32 => instructions::s16_to_i32(*instruction), + Instruction::U16ToI32 => instructions::u16_to_i32(*instruction), + Instruction::S32ToI32 => instructions::s32_to_i32(*instruction), + Instruction::U32ToI32 => instructions::u32_to_i32(*instruction), + Instruction::S64ToI32 | Instruction::S64ToI32X => { + instructions::s64_to_i32(*instruction) + } + Instruction::U64ToI32 | Instruction::U64ToI32X => { + instructions::u64_to_i32(*instruction) + } + Instruction::S8ToI64 => instructions::s8_to_i64(*instruction), + Instruction::U8ToI64 => instructions::u8_to_i64(*instruction), + Instruction::S16ToI64 => instructions::s16_to_i64(*instruction), + Instruction::U16ToI64 => instructions::u16_to_i64(*instruction), + Instruction::S32ToI64 => instructions::s32_to_i64(*instruction), + Instruction::U32ToI64 => instructions::u32_to_i64(*instruction), + Instruction::S64ToI64 => instructions::s64_to_i64(*instruction), + Instruction::U64ToI64 => instructions::u64_to_i64(*instruction), _ => unimplemented!(), } }) diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 1e0976cfe..484fa1dae 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -1,8 +1,8 @@ #![allow(missing_docs)] -use std::convert::TryFrom; - pub use crate::ast::InterfaceType; +use crate::errors::WasmValueNativeCastError; +use std::convert::TryFrom; #[derive(Debug, Clone, PartialEq)] pub enum InterfaceValue { @@ -49,35 +49,46 @@ impl Default for InterfaceValue { } } -macro_rules! from_x_for_interface_value { - ($native_type:ty, $value_variant:ident) => { +pub trait NativeType { + const INTERFACE_TYPE: InterfaceType; +} + +macro_rules! native { + ($native_type:ty, $variant:ident) => { + impl NativeType for $native_type { + const INTERFACE_TYPE: InterfaceType = InterfaceType::$variant; + } + impl From<$native_type> for InterfaceValue { fn from(n: $native_type) -> Self { - Self::$value_variant(n) + Self::$variant(n) } } impl TryFrom<&InterfaceValue> for $native_type { - type Error = &'static str; + type Error = WasmValueNativeCastError; fn try_from(w: &InterfaceValue) -> Result { match w { - InterfaceValue::$value_variant(n) => Ok(n.clone()), - _ => Err("Invalid cast."), + InterfaceValue::$variant(n) => Ok(n.clone()), + _ => Err(WasmValueNativeCastError { + from: w.into(), + to: <$native_type>::INTERFACE_TYPE, + }), } } } }; } -from_x_for_interface_value!(i8, S8); -from_x_for_interface_value!(i16, S16); -from_x_for_interface_value!(u8, U8); -from_x_for_interface_value!(u16, U16); -from_x_for_interface_value!(u32, U32); -from_x_for_interface_value!(u64, U64); -from_x_for_interface_value!(f32, F32); -from_x_for_interface_value!(f64, F64); -from_x_for_interface_value!(String, String); -from_x_for_interface_value!(i32, I32); -from_x_for_interface_value!(i64, I64); +native!(i8, S8); +native!(i16, S16); +native!(u8, U8); +native!(u16, U16); +native!(u32, U32); +native!(u64, U64); +native!(f32, F32); +native!(f64, F64); +native!(String, String); +native!(i32, I32); +native!(i64, I64); diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 955459438..9e88cac28 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -55,4 +55,5 @@ pub mod ast; mod macros; pub mod decoders; pub mod encoders; +pub mod errors; pub mod interpreter; diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index 5e2217e29..b2e2cfa80 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -122,7 +122,7 @@ macro_rules! test_executable_instruction { assert!(run.is_err()); - let error = run.unwrap_err(); + let error = run.unwrap_err().to_string(); assert_eq!(error, String::from($error)); } From 7d6bc577b724a2055a5b4b07df96dfd7f5be813f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 15:53:46 +0100 Subject: [PATCH 58/79] feat(interface-types) Reformat the instructions. --- .../src/interpreter/instructions/call_core.rs | 99 +++++++++---------- .../instructions/lowering_lifting.rs | 31 +++--- .../instructions/memory_to_string.rs | 91 ++++++++--------- 3 files changed, 100 insertions(+), 121 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/call_core.rs b/lib/interface-types/src/interpreter/instructions/call_core.rs index dfaf520e4..77486498f 100644 --- a/lib/interface-types/src/interpreter/instructions/call_core.rs +++ b/lib/interface-types/src/interpreter/instructions/call_core.rs @@ -13,61 +13,54 @@ executable_instruction!( let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(function_index); - match instance.local_or_import(index) { - Some(local_or_import) => { - let inputs_cardinality = local_or_import.inputs_cardinality(); - - match runtime.stack.pop(inputs_cardinality) { - Some(inputs) => { - let input_types = inputs - .iter() - .map(Into::into) - .collect::>(); - - if input_types != local_or_import.inputs() { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportSignatureMismatch { - function_index: function_index as u32, - expected: (local_or_import.inputs().to_vec(), vec![]), - received: (input_types, vec![]), - } - ) - ) - } - - match local_or_import.call(&inputs) { - Ok(outputs) => { - for output in outputs.iter() { - runtime.stack.push(output.clone()); - } - - Ok(()) - } - Err(_) => Err( - InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportCall { function_index: function_index as u32, }, - ) - ) - } - } - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::StackIsTooSmall { needed: inputs_cardinality }, - ) - ) - } - } - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportIsMissing { function_index: function_index as u32, }, - ) + let local_or_import = instance.local_or_import(index).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportIsMissing { + function_index: function_index as u32, + }, ) + })?; + let inputs_cardinality = local_or_import.inputs_cardinality(); + + let inputs = runtime.stack.pop(inputs_cardinality).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { + needed: inputs_cardinality, + }, + ) + })?; + let input_types = inputs + .iter() + .map(Into::into) + .collect::>(); + + if input_types != local_or_import.inputs() { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: function_index as u32, + expected: (local_or_import.inputs().to_vec(), vec![]), + received: (input_types, vec![]), + }, + )); } + + let outputs = local_or_import.call(&inputs).map_err(|_| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { + function_index: function_index as u32, + }, + ) + })?; + + for output in outputs.iter() { + runtime.stack.push(output.clone()); + } + + Ok(()) } } ); diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index f18c24385..d3903e660 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -18,31 +18,30 @@ macro_rules! lowering_lifting { |_| { InstructionError::new( instruction, - InstructionErrorKind::LoweringLifting { from: InterfaceType::$from_variant, to: InterfaceType::$to_variant }, + InstructionErrorKind::LoweringLifting { + from: InterfaceType::$from_variant, + to: InterfaceType::$to_variant + }, ) }, )?)) } Some(wrong_value) => { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::InvalidValueOnTheStack { - expected_type: InterfaceType::$from_variant, - received_type: (&wrong_value).into(), - } - ) - ) + return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::$from_variant, + received_type: (&wrong_value).into(), + } + )) }, None => { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::StackIsTooSmall { needed: 1 }, - ) - ) + return Err(InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + )) } } diff --git a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs index b1b923035..f96c47f22 100644 --- a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -8,63 +8,50 @@ use std::cell::Cell; executable_instruction!( memory_to_string(instruction: Instruction) -> _ { move |runtime| -> _ { - match runtime.stack.pop(2) { - Some(inputs) => { - let memory_index: u32 = 0; + let inputs = runtime.stack.pop(2).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, + ) + })?; - match runtime.wasm_instance.memory(memory_index as usize) { - Some(memory) => { - let length = to_native::(&inputs[0], instruction)? as usize; - let pointer = to_native::(&inputs[1], instruction)? as usize; - let memory_view = memory.view(); + let memory_index: u32 = 0; - if memory_view.len() < pointer + length { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::MemoryOutOfBoundsAccess { - index: pointer + length, - length: memory_view.len(), - } - ), - ) - } - - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); - - match String::from_utf8(data) { - Ok(string) => { - runtime.stack.push(InterfaceValue::String(string)); - - Ok(()) - } - Err(utf8_error) => Err( - InstructionError::new( - instruction, - InstructionErrorKind::String(utf8_error) - ), - ) - } - } - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::MemoryIsMissing { memory_index } - ), - ) - } - } - - None => Err( + let memory = runtime + .wasm_instance + .memory(memory_index as usize) + .ok_or_else(|| { InstructionError::new( instruction, - InstructionErrorKind::StackIsTooSmall { needed: 2 } - ), - ) + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })?; + + let length = to_native::(&inputs[0], instruction)? as usize; + let pointer = to_native::(&inputs[1], instruction)? as usize; + let memory_view = memory.view(); + + if memory_view.len() < pointer + length { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::MemoryOutOfBoundsAccess { + index: pointer + length, + length: memory_view.len(), + }, + )); } + + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); + + let string = String::from_utf8(data) + .map_err(|error| InstructionError::new(instruction, InstructionErrorKind::String(error)))?; + + runtime.stack.push(InterfaceValue::String(string)); + + Ok(()) } } ); From 4ffb158f1435bb21a058a3c10c21a815dec3c4c1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 16:07:07 +0100 Subject: [PATCH 59/79] doc(changelog) Add #1285. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f90c87997..4a8de2687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` - [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` From a1cdeede40fbc8879a99682c5f5f8a4f577fe6aa Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 10 Mar 2020 10:52:53 -0700 Subject: [PATCH 60/79] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e9c8b18..780754629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +- [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax + ## 0.15.0 - 2020-03-04 - [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages. From f2c5f88b21b26618bb94ef94c90a9dde3623f733 Mon Sep 17 00:00:00 2001 From: Syrus Date: Tue, 10 Mar 2020 18:23:59 -0700 Subject: [PATCH 61/79] Added wax symlink --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c8f755538..01cc7e05d 100644 --- a/Makefile +++ b/Makefile @@ -287,6 +287,8 @@ build-install-package: mkdir -p ./install/bin cp ./wapm-cli/target/release/wapm ./install/bin/ cp ./target/release/wasmer ./install/bin/ + # Create the wax binary as symlink to wapm + cd ./install/bin/ && ln -s wapm wax tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer UNAME_S := $(shell uname -s) @@ -315,7 +317,7 @@ endif cp lib/runtime-c-api/doc/index.md ./capi/README.md tar -C ./capi -zcvf wasmer-c-api.tar.gz lib include README.md LICENSE -WAPM_VERSION = 0.4.3 +WAPM_VERSION = 0.5.0 build-wapm: git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" From a4a90dfcd83902a8898f6698df978b6b5a471b25 Mon Sep 17 00:00:00 2001 From: Syrus Date: Tue, 10 Mar 2020 21:38:17 -0700 Subject: [PATCH 62/79] Fixed WAPM version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 01cc7e05d..601421c91 100644 --- a/Makefile +++ b/Makefile @@ -317,7 +317,7 @@ endif cp lib/runtime-c-api/doc/index.md ./capi/README.md tar -C ./capi -zcvf wasmer-c-api.tar.gz lib include README.md LICENSE -WAPM_VERSION = 0.5.0 +WAPM_VERSION = v0.5.0 build-wapm: git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" From d7d5f5b8945a0871dc2862b6065e5f446527e3e2 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 11 Mar 2020 11:15:07 -0700 Subject: [PATCH 63/79] Prepare for 0.16.0 release --- CHANGELOG.md | 2 ++ Cargo.lock | 38 ++++++++++----------- Cargo.toml | 2 +- lib/clif-backend/Cargo.toml | 6 ++-- lib/dev-utils/Cargo.toml | 2 +- lib/emscripten-tests/Cargo.toml | 14 ++++---- lib/emscripten/Cargo.toml | 4 +-- lib/interface-types/Cargo.toml | 2 +- lib/llvm-backend-tests/Cargo.toml | 6 ++-- lib/llvm-backend/Cargo.toml | 4 +-- lib/middleware-common-tests/Cargo.toml | 12 +++---- lib/middleware-common/Cargo.toml | 4 +-- lib/runtime-c-api/Cargo.toml | 10 +++--- lib/runtime-core-tests/Cargo.toml | 10 +++--- lib/runtime-core/Cargo.toml | 2 +- lib/runtime/Cargo.toml | 8 ++--- lib/singlepass-backend/Cargo.toml | 4 +-- lib/spectests/Cargo.toml | 10 +++--- lib/wasi-experimental-io-devices/Cargo.toml | 6 ++-- lib/wasi-tests/Cargo.toml | 14 ++++---- lib/wasi/Cargo.toml | 4 +-- lib/win-exception-handler/Cargo.toml | 4 +-- scripts/update_version_numbers.sh | 4 +-- src/installer/wasmer.iss | 2 +- 24 files changed, 88 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4983253d7..c1d93420b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +## 0.16.0 - 2020-03-11 + - [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax - [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` diff --git a/Cargo.lock b/Cargo.lock index ee781d901..57aba6cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1820,7 +1820,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "0.15.0" +version = "0.16.0" dependencies = [ "atty", "byteorder", @@ -1851,7 +1851,7 @@ dependencies = [ [[package]] name = "wasmer-clif-backend" -version = "0.15.0" +version = "0.16.0" dependencies = [ "byteorder", "cranelift-codegen", @@ -1902,14 +1902,14 @@ dependencies = [ [[package]] name = "wasmer-dev-utils" -version = "0.15.0" +version = "0.16.0" dependencies = [ "libc", ] [[package]] name = "wasmer-emscripten" -version = "0.15.0" +version = "0.16.0" dependencies = [ "byteorder", "getrandom", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "wasmer-emscripten-tests" -version = "0.15.0" +version = "0.16.0" dependencies = [ "glob 0.3.0", "wabt", @@ -1936,7 +1936,7 @@ dependencies = [ [[package]] name = "wasmer-interface-types" -version = "0.15.0" +version = "0.16.0" dependencies = [ "nom", "wast", @@ -1952,7 +1952,7 @@ dependencies = [ [[package]] name = "wasmer-llvm-backend" -version = "0.15.0" +version = "0.16.0" dependencies = [ "byteorder", "cc", @@ -1983,14 +1983,14 @@ dependencies = [ [[package]] name = "wasmer-middleware-common" -version = "0.15.0" +version = "0.16.0" dependencies = [ "wasmer-runtime-core", ] [[package]] name = "wasmer-middleware-common-tests" -version = "0.15.0" +version = "0.16.0" dependencies = [ "criterion", "wabt", @@ -2003,7 +2003,7 @@ dependencies = [ [[package]] name = "wasmer-runtime" -version = "0.15.0" +version = "0.16.0" dependencies = [ "criterion", "lazy_static", @@ -2020,7 +2020,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-c-api" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cbindgen", "libc", @@ -2032,7 +2032,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core" -version = "0.15.0" +version = "0.16.0" dependencies = [ "bincode", "blake3", @@ -2060,7 +2060,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core-tests" -version = "0.15.0" +version = "0.16.0" dependencies = [ "wabt", "wasmer-clif-backend", @@ -2071,7 +2071,7 @@ dependencies = [ [[package]] name = "wasmer-singlepass-backend" -version = "0.15.0" +version = "0.16.0" dependencies = [ "bincode", "byteorder", @@ -2088,7 +2088,7 @@ dependencies = [ [[package]] name = "wasmer-spectests" -version = "0.15.0" +version = "0.16.0" dependencies = [ "glob 0.3.0", "wabt", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wasmer-wasi" -version = "0.15.0" +version = "0.16.0" dependencies = [ "bincode", "byteorder", @@ -2117,7 +2117,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-experimental-io-devices" -version = "0.15.0" +version = "0.16.0" dependencies = [ "log", "minifb", @@ -2130,7 +2130,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-tests" -version = "0.15.0" +version = "0.16.0" dependencies = [ "glob 0.3.0", "wasmer-clif-backend", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "wasmer-win-exception-handler" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cmake", "libc", diff --git a/Cargo.toml b/Cargo.toml index b3f532d57..9a360270f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.15.0" +version = "0.16.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 282f4e2d1..9a2fc85ec 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } cranelift-native = "0.59.0" cranelift-codegen = "0.59.0" cranelift-entity = "0.59.0" @@ -38,7 +38,7 @@ version = "0.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } -wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.15.0" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.0" } [features] generate-debug-information = ["wasm-debug"] diff --git a/lib/dev-utils/Cargo.toml b/lib/dev-utils/Cargo.toml index 1c06313ba..404efd77a 100644 --- a/lib/dev-utils/Cargo.toml +++ b/lib/dev-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-dev-utils" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/emscripten-tests/Cargo.toml b/lib/emscripten-tests/Cargo.toml index 2e6e68a6f..4df41605d 100644 --- a/lib/emscripten-tests/Cargo.toml +++ b/lib/emscripten-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten-tests" -version = "0.15.0" +version = "0.16.0" description = "Tests for our Emscripten implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,15 +9,15 @@ publish = false build = "build/mod.rs" [dependencies] -wasmer-emscripten = { path = "../emscripten", version = "0.15.0" } -wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false } -wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", optional = true, features = ["test"] } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } +wasmer-emscripten = { path = "../emscripten", version = "0.16.0" } +wasmer-runtime = { path = "../runtime", version = "0.16.0", default-features = false } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", optional = true, features = ["test"] } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } [dev-dependencies] wabt = "0.9.1" -wasmer-dev-utils = { path = "../dev-utils", version = "0.15.0"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.16.0"} [build-dependencies] glob = "0.3" diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index 25091e89b..cb946a491 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -15,7 +15,7 @@ lazy_static = "1.4" libc = "0.2.60" log = "0.4" time = "0.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } [target.'cfg(windows)'.dependencies] getrandom = "0.1" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 44758c1fe..6915304af 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types" -version = "0.15.0" +version = "0.16.0" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/llvm-backend-tests/Cargo.toml b/lib/llvm-backend-tests/Cargo.toml index 4a5e7e80c..6bb843be5 100644 --- a/lib/llvm-backend-tests/Cargo.toml +++ b/lib/llvm-backend-tests/Cargo.toml @@ -9,8 +9,8 @@ edition = "2018" [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } -wasmer-runtime = { path = "../runtime", version = "0.15.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime = { path = "../runtime", version = "0.16.0" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"] } [features] diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index cfe2834a8..fa3d05b22 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-llvm-backend" -version = "0.15.0" +version = "0.16.0" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" @@ -10,7 +10,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0", features = ["generate-debug-information-no-export-symbols"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0", features = ["generate-debug-information-no-export-symbols"] } wasmparser = "0.51.3" smallvec = "0.6" goblin = "0.0.24" diff --git a/lib/middleware-common-tests/Cargo.toml b/lib/middleware-common-tests/Cargo.toml index f83da0256..943da23a0 100644 --- a/lib/middleware-common-tests/Cargo.toml +++ b/lib/middleware-common-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common-tests" -version = "0.15.0" +version = "0.16.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -8,11 +8,11 @@ license = "MIT" publish = false [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } -wasmer-middleware-common = { path = "../middleware-common", version = "0.15.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-middleware-common = { path = "../middleware-common", version = "0.16.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } [features] clif = ["wasmer-clif-backend"] diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index c44fba176..ea39f5958 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common" -version = "0.15.0" +version = "0.16.0" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime common middlewares" license = "MIT" @@ -10,4 +10,4 @@ categories = ["wasm"] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 38c099375..fb933aa2f 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.15.0" +version = "0.16.0" description = "Wasmer C API library" documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/" license = "MIT" @@ -20,22 +20,22 @@ libc = "0.2.60" [dependencies.wasmer-runtime] default-features = false path = "../runtime" -version = "0.15.0" +version = "0.16.0" [dependencies.wasmer-runtime-core] default-features = false path = "../runtime-core" -version = "0.15.0" +version = "0.16.0" [dependencies.wasmer-wasi] default-features = false path = "../wasi" -version = "0.15.0" +version = "0.16.0" optional = true [dependencies.wasmer-emscripten] path = "../emscripten" -version = "0.15.0" +version = "0.16.0" optional = true [features] diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml index a97b44b08..1e5b58f27 100644 --- a/lib/runtime-core-tests/Cargo.toml +++ b/lib/runtime-core-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core-tests" -version = "0.15.0" +version = "0.16.0" description = "Tests for the Wasmer runtime core crate" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ publish = false [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } [features] default = ["backend-cranelift"] diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index b309a552d..03904ed7c 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index f836c9693..bc9b1fe3a 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,17 +11,17 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } lazy_static = "1.4" memmap = "0.7" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.15.0" +version = "0.16.0" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.15.0" +version = "0.16.0" optional = true # Dependencies for caching. diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index b1964dc3d..2f3937f3b 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-singlepass-backend" -version = "0.15.0" +version = "0.16.0" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime single pass compiler backend" license = "MIT" @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } dynasm = "0.5" dynasmrt = "0.5" lazy_static = "1.4" diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index c5497c476..aed07d65c 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.15.0" +version = "0.16.0" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ edition = "2018" [dependencies] glob = "0.3" -wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false} -wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } +wasmer-runtime = { path = "../runtime", version = "0.16.0", default-features = false} +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } [build-dependencies] wabt = "0.9.1" diff --git a/lib/wasi-experimental-io-devices/Cargo.toml b/lib/wasi-experimental-io-devices/Cargo.toml index 32c8e266b..a64b3046a 100644 --- a/lib/wasi-experimental-io-devices/Cargo.toml +++ b/lib/wasi-experimental-io-devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-experimental-io-devices" -version = "0.15.0" +version = "0.16.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -14,8 +14,8 @@ maintenance = { status = "experimental" } [dependencies] log = "0.4" minifb = "0.13" -wasmer-wasi = { version = "0.15.0", path = "../wasi" } -wasmer-runtime-core = { version = "0.15.0", path = "../runtime-core" } +wasmer-wasi = { version = "0.16.0", path = "../wasi" } +wasmer-runtime-core = { version = "0.16.0", path = "../runtime-core" } ref_thread_local = "0.0" serde = "1" typetag = "0.1" diff --git a/lib/wasi-tests/Cargo.toml b/lib/wasi-tests/Cargo.toml index c7208be5d..19b6c1e88 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-tests" -version = "0.15.0" +version = "0.16.0" description = "Tests for our WASI implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,18 +10,18 @@ build = "build/mod.rs" [dependencies] # We set default features to false to be able to use the singlepass backend properly -wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false } -wasmer-wasi = { path = "../wasi", version = "0.15.0" } +wasmer-runtime = { path = "../runtime", version = "0.16.0", default-features = false } +wasmer-wasi = { path = "../wasi", version = "0.16.0" } # hack to get tests to work -wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true} -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true} +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } [build-dependencies] glob = "0.3" [dev-dependencies] -wasmer-dev-utils = { path = "../dev-utils", version = "0.15.0"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.16.0"} [features] clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 35886c948..13495fd12 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime WASI implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -19,7 +19,7 @@ getrandom = "0.1" time = "0.1" typetag = "0.1" serde = { version = "1", features = ["derive"] } -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index 0a92ae94b..7c5faacac 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.15.0" +version = "0.16.0" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.60" diff --git a/scripts/update_version_numbers.sh b/scripts/update_version_numbers.sh index 77392a5a3..ed93c6bde 100755 --- a/scripts/update_version_numbers.sh +++ b/scripts/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.14.1' -NEXT_VERSION='0.15.0' +PREVIOUS_VERSION='0.15.0' +NEXT_VERSION='0.16.0' # quick hack fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 299d638a4..89826ab5e 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=0.15.0 +AppVersion=0.16.0 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 From adabfa02b02348adf989549fb152efedf9b8bbd5 Mon Sep 17 00:00:00 2001 From: losfair Date: Thu, 12 Mar 2020 02:51:22 +0800 Subject: [PATCH 64/79] Move feed_* into closures. --- lib/runtime-core/src/parse.rs | 88 ++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index e65dfae8d..fffb7e4a9 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -6,8 +6,8 @@ use crate::{ backend::{CompilerConfig, RunnableModule}, error::CompileError, module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, + DataInitializer, ExportIndex, ImportName, ModuleInfo, NameIndex, NamespaceIndex, + StringTable, StringTableBuilder, TableInitializer, }, structures::{Map, TypedIndex}, types::{ @@ -110,46 +110,36 @@ pub fn read_module< let mut namespace_builder = Some(StringTableBuilder::new()); let mut name_builder = Some(StringTableBuilder::new()); let mut func_count: usize = 0; - let mut mcg_signatures_fed = false; - let mut mcg_info_fed = false; + + let mut feed_mcg_signatures: Option<_> = Some(|mcg: &mut MCG| -> Result<(), LoadError> { + let info_read = info.read().unwrap(); + mcg.feed_signatures(info_read.signatures.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + Ok(()) + }); + let mut feed_mcg_info: Option<_> = Some( + |mcg: &mut MCG, + ns_builder: StringTableBuilder, + name_builder: StringTableBuilder| + -> Result<(), LoadError> { + { + let mut info_write = info.write().unwrap(); + info_write.namespace_table = ns_builder.finish(); + info_write.name_table = name_builder.finish(); + } + let info_read = info.read().unwrap(); + mcg.feed_function_signatures(info_read.func_assoc.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + mcg.check_precondition(&info_read) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + Ok(()) + }, + ); loop { use wasmparser::ParserState; let state = parser.read(); - // Feed signature and namespace information as early as possible. - match *state { - ParserState::BeginFunctionBody { .. } - | ParserState::ImportSectionEntry { .. } - | ParserState::EndWasm => { - if !mcg_signatures_fed { - mcg_signatures_fed = true; - let info_read = info.read().unwrap(); - mcg.feed_signatures(info_read.signatures.clone()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - } - _ => {} - } - match *state { - ParserState::BeginFunctionBody { .. } | ParserState::EndWasm => { - if !mcg_info_fed { - mcg_info_fed = true; - { - let mut info_write = info.write().unwrap(); - info_write.namespace_table = namespace_builder.take().unwrap().finish(); - info_write.name_table = name_builder.take().unwrap().finish(); - } - let info_read = info.read().unwrap(); - mcg.feed_function_signatures(info_read.func_assoc.clone()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.check_precondition(&info_read) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - } - _ => {} - } - match *state { ParserState::Error(ref err) => return Err(err.clone().into()), ParserState::TypeSectionEntry(ref ty) => { @@ -159,6 +149,10 @@ pub fn read_module< .push(func_type_to_func_sig(ty)?); } ParserState::ImportSectionEntry { module, field, ty } => { + if let Some(f) = feed_mcg_signatures.take() { + f(mcg)?; + } + let namespace_index = namespace_builder.as_mut().unwrap().register(module); let name_index = name_builder.as_mut().unwrap().register(field); let import_name = ImportName { @@ -252,6 +246,16 @@ pub fn read_module< info.write().unwrap().start_func = Some(FuncIndex::new(start_index as usize)); } ParserState::BeginFunctionBody { range } => { + if let Some(f) = feed_mcg_signatures.take() { + f(mcg)?; + } + if let Some(f) = feed_mcg_info.take() { + f( + mcg, + namespace_builder.take().unwrap(), + name_builder.take().unwrap(), + )?; + } let id = func_count; let fcg = mcg .next_function( @@ -451,6 +455,16 @@ pub fn read_module< info.write().unwrap().globals.push(global_init); } ParserState::EndWasm => { + if let Some(f) = feed_mcg_signatures.take() { + f(mcg)?; + } + if let Some(f) = feed_mcg_info.take() { + f( + mcg, + namespace_builder.take().unwrap(), + name_builder.take().unwrap(), + )?; + } break; } _ => {} From f93561dafc62f9dadcd9f02c55fac7811114a0bb Mon Sep 17 00:00:00 2001 From: losfair Date: Thu, 12 Mar 2020 02:52:48 +0800 Subject: [PATCH 65/79] Add #1283 into changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d93420b..573b03523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s. + ## 0.16.0 - 2020-03-11 - [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax From 5edd1b5ab73868e60d8cdb28fbf6a133065733cb Mon Sep 17 00:00:00 2001 From: losfair Date: Thu, 12 Mar 2020 02:58:36 +0800 Subject: [PATCH 66/79] Enable `DynamicFunc` for closures with environment. --- lib/runtime-core/src/typed_func.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 0b29d54ea..b1d1399e9 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -349,11 +349,6 @@ impl<'a> DynamicFunc<'a> { } } - // Disable "fat" closures for possible future changes. - if mem::size_of::() != 0 { - unimplemented!("DynamicFunc with captured environment is not yet supported"); - } - let mut builder = TrampolineBufferBuilder::new(); let ctx: Box = Box::new(PolymorphicContext { arg_types: signature.params().to_vec(), From 6c7f49a223bc750eef91abf46eee5791785febe5 Mon Sep 17 00:00:00 2001 From: losfair Date: Thu, 12 Mar 2020 03:14:57 +0800 Subject: [PATCH 67/79] Put fat `DynamicFunc`s behind a feature flag. --- lib/runtime-core/Cargo.toml | 2 ++ lib/runtime-core/src/typed_func.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 03904ed7c..5242a2bc1 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -59,3 +59,5 @@ generate-debug-information = ["wasm-debug"] # don't export symbols related to the GDB JIT interafce, LLVM or some other native # code will be providing them generate-debug-information-no-export-symbols = [] +# enable DynamicFunc's for closures with captured environment. +dynamicfunc-fat-closures = [] diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index b1d1399e9..61def512d 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -349,6 +349,10 @@ impl<'a> DynamicFunc<'a> { } } + if cfg!(not(feature = "dynamicfunc-fat-closures")) && mem::size_of::() != 0 { + unimplemented!("DynamicFunc with captured environment is disabled"); + } + let mut builder = TrampolineBufferBuilder::new(); let ctx: Box = Box::new(PolymorphicContext { arg_types: signature.params().to_vec(), From 9d6681006daf74e25c92885bda2780e6d2f34adc Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 11 Mar 2020 15:53:51 -0700 Subject: [PATCH 68/79] Fix `wax` command packaging for install script --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 601421c91..7365279ab 100644 --- a/Makefile +++ b/Makefile @@ -288,8 +288,8 @@ build-install-package: cp ./wapm-cli/target/release/wapm ./install/bin/ cp ./target/release/wasmer ./install/bin/ # Create the wax binary as symlink to wapm - cd ./install/bin/ && ln -s wapm wax - tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer + cd ./install/bin/ && ln -sf wapm wax && chmod +x wax + tar -C ./install -zcvf wasmer.tar.gz bin UNAME_S := $(shell uname -s) From a0d0263e86e04b5451d5a70a2ae418fca45ad3a4 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Wed, 11 Mar 2020 15:57:26 -0700 Subject: [PATCH 69/79] Update azure-pipelines.yml --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bccae7ddd..07f720e86 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -300,6 +300,7 @@ jobs: isDraft: false isPreRelease: false assets: '$(Build.ArtifactStagingDirectory)/**' + assetUploadMode: 'replace' # Don't delete previously uploaded assets (default) - job: Publish_Docs dependsOn: From dc9cc3292c82589dcb0069b802a2e03ad82771a5 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 11 Mar 2020 16:00:21 -0700 Subject: [PATCH 70/79] Prepare for 0.16.1 release --- CHANGELOG.md | 4 +++ Cargo.lock | 38 ++++++++++----------- Cargo.toml | 2 +- lib/clif-backend/Cargo.toml | 6 ++-- lib/dev-utils/Cargo.toml | 2 +- lib/emscripten-tests/Cargo.toml | 14 ++++---- lib/emscripten/Cargo.toml | 4 +-- lib/interface-types/Cargo.toml | 2 +- lib/llvm-backend-tests/Cargo.toml | 6 ++-- lib/llvm-backend/Cargo.toml | 4 +-- lib/middleware-common-tests/Cargo.toml | 12 +++---- lib/middleware-common/Cargo.toml | 4 +-- lib/runtime-c-api/Cargo.toml | 10 +++--- lib/runtime-core-tests/Cargo.toml | 10 +++--- lib/runtime-core/Cargo.toml | 2 +- lib/runtime/Cargo.toml | 8 ++--- lib/singlepass-backend/Cargo.toml | 4 +-- lib/spectests/Cargo.toml | 10 +++--- lib/wasi-experimental-io-devices/Cargo.toml | 6 ++-- lib/wasi-tests/Cargo.toml | 14 ++++---- lib/wasi/Cargo.toml | 4 +-- lib/win-exception-handler/Cargo.toml | 4 +-- scripts/update_version_numbers.sh | 4 +-- src/installer/wasmer.iss | 2 +- 24 files changed, 90 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d93420b..22885af0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## 0.16.0 - 2020-03-11 +- [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command. + +## 0.16.0 - 2020-03-11 + - [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax - [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` diff --git a/Cargo.lock b/Cargo.lock index 57aba6cea..d1ce82547 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1820,7 +1820,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "0.16.0" +version = "0.16.1" dependencies = [ "atty", "byteorder", @@ -1851,7 +1851,7 @@ dependencies = [ [[package]] name = "wasmer-clif-backend" -version = "0.16.0" +version = "0.16.1" dependencies = [ "byteorder", "cranelift-codegen", @@ -1902,14 +1902,14 @@ dependencies = [ [[package]] name = "wasmer-dev-utils" -version = "0.16.0" +version = "0.16.1" dependencies = [ "libc", ] [[package]] name = "wasmer-emscripten" -version = "0.16.0" +version = "0.16.1" dependencies = [ "byteorder", "getrandom", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "wasmer-emscripten-tests" -version = "0.16.0" +version = "0.16.1" dependencies = [ "glob 0.3.0", "wabt", @@ -1936,7 +1936,7 @@ dependencies = [ [[package]] name = "wasmer-interface-types" -version = "0.16.0" +version = "0.16.1" dependencies = [ "nom", "wast", @@ -1952,7 +1952,7 @@ dependencies = [ [[package]] name = "wasmer-llvm-backend" -version = "0.16.0" +version = "0.16.1" dependencies = [ "byteorder", "cc", @@ -1983,14 +1983,14 @@ dependencies = [ [[package]] name = "wasmer-middleware-common" -version = "0.16.0" +version = "0.16.1" dependencies = [ "wasmer-runtime-core", ] [[package]] name = "wasmer-middleware-common-tests" -version = "0.16.0" +version = "0.16.1" dependencies = [ "criterion", "wabt", @@ -2003,7 +2003,7 @@ dependencies = [ [[package]] name = "wasmer-runtime" -version = "0.16.0" +version = "0.16.1" dependencies = [ "criterion", "lazy_static", @@ -2020,7 +2020,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-c-api" -version = "0.16.0" +version = "0.16.1" dependencies = [ "cbindgen", "libc", @@ -2032,7 +2032,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core" -version = "0.16.0" +version = "0.16.1" dependencies = [ "bincode", "blake3", @@ -2060,7 +2060,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core-tests" -version = "0.16.0" +version = "0.16.1" dependencies = [ "wabt", "wasmer-clif-backend", @@ -2071,7 +2071,7 @@ dependencies = [ [[package]] name = "wasmer-singlepass-backend" -version = "0.16.0" +version = "0.16.1" dependencies = [ "bincode", "byteorder", @@ -2088,7 +2088,7 @@ dependencies = [ [[package]] name = "wasmer-spectests" -version = "0.16.0" +version = "0.16.1" dependencies = [ "glob 0.3.0", "wabt", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wasmer-wasi" -version = "0.16.0" +version = "0.16.1" dependencies = [ "bincode", "byteorder", @@ -2117,7 +2117,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-experimental-io-devices" -version = "0.16.0" +version = "0.16.1" dependencies = [ "log", "minifb", @@ -2130,7 +2130,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-tests" -version = "0.16.0" +version = "0.16.1" dependencies = [ "glob 0.3.0", "wasmer-clif-backend", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "wasmer-win-exception-handler" -version = "0.16.0" +version = "0.16.1" dependencies = [ "cmake", "libc", diff --git a/Cargo.toml b/Cargo.toml index 9a360270f..05874f849 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.16.0" +version = "0.16.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 9a2fc85ec..0cda55de6 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } cranelift-native = "0.59.0" cranelift-codegen = "0.59.0" cranelift-entity = "0.59.0" @@ -38,7 +38,7 @@ version = "0.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } -wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.0" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.1" } [features] generate-debug-information = ["wasm-debug"] diff --git a/lib/dev-utils/Cargo.toml b/lib/dev-utils/Cargo.toml index 404efd77a..c97b4b16f 100644 --- a/lib/dev-utils/Cargo.toml +++ b/lib/dev-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-dev-utils" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/emscripten-tests/Cargo.toml b/lib/emscripten-tests/Cargo.toml index 4df41605d..750583a85 100644 --- a/lib/emscripten-tests/Cargo.toml +++ b/lib/emscripten-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten-tests" -version = "0.16.0" +version = "0.16.1" description = "Tests for our Emscripten implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,15 +9,15 @@ publish = false build = "build/mod.rs" [dependencies] -wasmer-emscripten = { path = "../emscripten", version = "0.16.0" } -wasmer-runtime = { path = "../runtime", version = "0.16.0", default-features = false } -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", optional = true, features = ["test"] } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } +wasmer-emscripten = { path = "../emscripten", version = "0.16.1" } +wasmer-runtime = { path = "../runtime", version = "0.16.1", default-features = false } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", optional = true, features = ["test"] } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } [dev-dependencies] wabt = "0.9.1" -wasmer-dev-utils = { path = "../dev-utils", version = "0.16.0"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.16.1"} [build-dependencies] glob = "0.3" diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index cb946a491..de3469bc7 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -15,7 +15,7 @@ lazy_static = "1.4" libc = "0.2.60" log = "0.4" time = "0.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } [target.'cfg(windows)'.dependencies] getrandom = "0.1" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 6915304af..859986f8f 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types" -version = "0.16.0" +version = "0.16.1" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/llvm-backend-tests/Cargo.toml b/lib/llvm-backend-tests/Cargo.toml index 6bb843be5..796655413 100644 --- a/lib/llvm-backend-tests/Cargo.toml +++ b/lib/llvm-backend-tests/Cargo.toml @@ -9,8 +9,8 @@ edition = "2018" [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } -wasmer-runtime = { path = "../runtime", version = "0.16.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime = { path = "../runtime", version = "0.16.1" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"] } [features] diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index fa3d05b22..94f786e00 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-llvm-backend" -version = "0.16.0" +version = "0.16.1" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" @@ -10,7 +10,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0", features = ["generate-debug-information-no-export-symbols"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1", features = ["generate-debug-information-no-export-symbols"] } wasmparser = "0.51.3" smallvec = "0.6" goblin = "0.0.24" diff --git a/lib/middleware-common-tests/Cargo.toml b/lib/middleware-common-tests/Cargo.toml index 943da23a0..117dc79f0 100644 --- a/lib/middleware-common-tests/Cargo.toml +++ b/lib/middleware-common-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common-tests" -version = "0.16.0" +version = "0.16.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -8,11 +8,11 @@ license = "MIT" publish = false [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } -wasmer-middleware-common = { path = "../middleware-common", version = "0.16.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-middleware-common = { path = "../middleware-common", version = "0.16.1" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } [features] clif = ["wasmer-clif-backend"] diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index ea39f5958..593fe5b8f 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common" -version = "0.16.0" +version = "0.16.1" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime common middlewares" license = "MIT" @@ -10,4 +10,4 @@ categories = ["wasm"] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index fb933aa2f..fbb3287b0 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.16.0" +version = "0.16.1" description = "Wasmer C API library" documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/" license = "MIT" @@ -20,22 +20,22 @@ libc = "0.2.60" [dependencies.wasmer-runtime] default-features = false path = "../runtime" -version = "0.16.0" +version = "0.16.1" [dependencies.wasmer-runtime-core] default-features = false path = "../runtime-core" -version = "0.16.0" +version = "0.16.1" [dependencies.wasmer-wasi] default-features = false path = "../wasi" -version = "0.16.0" +version = "0.16.1" optional = true [dependencies.wasmer-emscripten] path = "../emscripten" -version = "0.16.0" +version = "0.16.1" optional = true [features] diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml index 1e5b58f27..aea0fd11b 100644 --- a/lib/runtime-core-tests/Cargo.toml +++ b/lib/runtime-core-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core-tests" -version = "0.16.0" +version = "0.16.1" description = "Tests for the Wasmer runtime core crate" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ publish = false [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } [features] default = ["backend-cranelift"] diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 03904ed7c..22f7fc40f 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index bc9b1fe3a..6af22a3b8 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,17 +11,17 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } lazy_static = "1.4" memmap = "0.7" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.16.0" +version = "0.16.1" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.16.0" +version = "0.16.1" optional = true # Dependencies for caching. diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index 2f3937f3b..564a26809 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-singlepass-backend" -version = "0.16.0" +version = "0.16.1" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime single pass compiler backend" license = "MIT" @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } dynasm = "0.5" dynasmrt = "0.5" lazy_static = "1.4" diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index aed07d65c..26cd21d89 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.16.0" +version = "0.16.1" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ edition = "2018" [dependencies] glob = "0.3" -wasmer-runtime = { path = "../runtime", version = "0.16.0", default-features = false} -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } +wasmer-runtime = { path = "../runtime", version = "0.16.1", default-features = false} +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } [build-dependencies] wabt = "0.9.1" diff --git a/lib/wasi-experimental-io-devices/Cargo.toml b/lib/wasi-experimental-io-devices/Cargo.toml index a64b3046a..ebf6b4873 100644 --- a/lib/wasi-experimental-io-devices/Cargo.toml +++ b/lib/wasi-experimental-io-devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-experimental-io-devices" -version = "0.16.0" +version = "0.16.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -14,8 +14,8 @@ maintenance = { status = "experimental" } [dependencies] log = "0.4" minifb = "0.13" -wasmer-wasi = { version = "0.16.0", path = "../wasi" } -wasmer-runtime-core = { version = "0.16.0", path = "../runtime-core" } +wasmer-wasi = { version = "0.16.1", path = "../wasi" } +wasmer-runtime-core = { version = "0.16.1", path = "../runtime-core" } ref_thread_local = "0.0" serde = "1" typetag = "0.1" diff --git a/lib/wasi-tests/Cargo.toml b/lib/wasi-tests/Cargo.toml index 19b6c1e88..75a8488ef 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-tests" -version = "0.16.0" +version = "0.16.1" description = "Tests for our WASI implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,18 +10,18 @@ build = "build/mod.rs" [dependencies] # We set default features to false to be able to use the singlepass backend properly -wasmer-runtime = { path = "../runtime", version = "0.16.0", default-features = false } -wasmer-wasi = { path = "../wasi", version = "0.16.0" } +wasmer-runtime = { path = "../runtime", version = "0.16.1", default-features = false } +wasmer-wasi = { path = "../wasi", version = "0.16.1" } # hack to get tests to work -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.0", optional = true} -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.0", features = ["test"], optional = true } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true} +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } [build-dependencies] glob = "0.3" [dev-dependencies] -wasmer-dev-utils = { path = "../dev-utils", version = "0.16.0"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.16.1"} [features] clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 13495fd12..f539f82b0 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime WASI implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -19,7 +19,7 @@ getrandom = "0.1" time = "0.1" typetag = "0.1" serde = { version = "1", features = ["derive"] } -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index 7c5faacac..75079fc23 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.16.0" +version = "0.16.1" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.60" diff --git a/scripts/update_version_numbers.sh b/scripts/update_version_numbers.sh index ed93c6bde..84418905e 100755 --- a/scripts/update_version_numbers.sh +++ b/scripts/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.15.0' -NEXT_VERSION='0.16.0' +PREVIOUS_VERSION='0.16.0' +NEXT_VERSION='0.16.1' # quick hack fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 89826ab5e..20f4223c1 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=0.16.0 +AppVersion=0.16.1 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 From 418edef88fe22c39835e0f7f223af0469ba04503 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Wed, 11 Mar 2020 16:10:06 -0700 Subject: [PATCH 71/79] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22885af0a..afc95820a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## **[Unreleased]** -## 0.16.0 - 2020-03-11 +## 0.16.1 - 2020-03-11 - [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command. From c3865c919c5ff9333a967e102787ca477bff458e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 11 Mar 2020 18:59:09 -0700 Subject: [PATCH 72/79] Allow zero length arrays and check base offset for being out of bounds --- lib/runtime-core/src/memory/ptr.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index f9c798af1..9e991cb85 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -130,9 +130,10 @@ impl WasmPtr { // for any index, we will always result an aligned memory access let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); let slice_full_len = index as usize + length as usize; + let memory_size = memory.size().bytes().0; - if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0 - || length == 0 + if (self.offset as usize) + (item_size * slice_full_len) > memory_size + || self.offset as usize >= memory_size || mem::size_of::() == 0 { return None; @@ -167,9 +168,10 @@ impl WasmPtr { // for any index, we will always result an aligned memory access let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); let slice_full_len = index as usize + length as usize; + let memory_size = memory.size().bytes().0; if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0 - || length == 0 + || self.offset as usize >= memory_size || mem::size_of::() == 0 { return None; @@ -190,7 +192,11 @@ impl WasmPtr { /// underlying data can be mutated if the Wasm is allowed to execute or /// an aliasing `WasmPtr` is used to mutate memory. pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> { - if self.offset as usize + str_len as usize > memory.size().bytes().0 || str_len == 0 { + let memory_size = memory.size().bytes().0; + + if self.offset as usize + str_len as usize > memory.size().bytes().0 + || self.offset as usize >= memory_size + { return None; } let ptr = unsafe { memory.view::().as_ptr().add(self.offset as usize) as *const u8 }; @@ -271,15 +277,15 @@ mod test { memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap(); let memory = memory::Memory::new(memory_descriptor).unwrap(); - // test that basic access works and that len = 0 is caught correctly + // test that basic access works and that len = 0 works, but oob does not let start_wasm_ptr: WasmPtr = WasmPtr::new(0); let start_wasm_ptr_array: WasmPtr = WasmPtr::new(0); assert!(start_wasm_ptr.deref(&memory).is_some()); assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() }); - assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_none()); - assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_none()); - assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_none() }); + assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some()); + assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some()); + assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() }); assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some()); assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() }); @@ -293,7 +299,8 @@ mod test { assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some()); assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() }); - let invalid_idx_len_combos: [(u32, u32); 3] = [(0, 0), (0, 2), (1, 1)]; + let invalid_idx_len_combos: [(u32, u32); 3] = + [(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)]; for &(idx, len) in invalid_idx_len_combos.into_iter() { assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none()); assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() }); @@ -323,7 +330,8 @@ mod test { assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some()); assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() }); - let invalid_idx_len_combos: [(u32, u32); 4] = [(0, 0), (1, 0), (0, 2), (1, 1)]; + let invalid_idx_len_combos: [(u32, u32); 3] = + [(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)]; for &(idx, len) in invalid_idx_len_combos.into_iter() { assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none()); assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() }); @@ -339,8 +347,6 @@ mod test { for oob_end_array_ptr in end_wasm_ptr_array_oob_array.into_iter() { assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none()); assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() }); - assert!(oob_end_array_ptr.deref(&memory, 0, 0).is_none()); - assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 0).is_none() }); assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none()); assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() }); } From fffdba395d85357209460a99138b37ddac3df328 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 11 Mar 2020 19:15:31 -0700 Subject: [PATCH 73/79] Prepare for 0.16.2 release --- CHANGELOG.md | 4 +++ Cargo.lock | 38 ++++++++++----------- Cargo.toml | 2 +- lib/clif-backend/Cargo.toml | 6 ++-- lib/dev-utils/Cargo.toml | 2 +- lib/emscripten-tests/Cargo.toml | 14 ++++---- lib/emscripten/Cargo.toml | 4 +-- lib/interface-types/Cargo.toml | 2 +- lib/llvm-backend-tests/Cargo.toml | 6 ++-- lib/llvm-backend/Cargo.toml | 4 +-- lib/middleware-common-tests/Cargo.toml | 12 +++---- lib/middleware-common/Cargo.toml | 4 +-- lib/runtime-c-api/Cargo.toml | 10 +++--- lib/runtime-core-tests/Cargo.toml | 10 +++--- lib/runtime-core/Cargo.toml | 2 +- lib/runtime/Cargo.toml | 8 ++--- lib/singlepass-backend/Cargo.toml | 4 +-- lib/spectests/Cargo.toml | 10 +++--- lib/wasi-experimental-io-devices/Cargo.toml | 6 ++-- lib/wasi-tests/Cargo.toml | 14 ++++---- lib/wasi/Cargo.toml | 4 +-- lib/win-exception-handler/Cargo.toml | 4 +-- scripts/update_version_numbers.sh | 4 +-- src/installer/wasmer.iss | 2 +- 24 files changed, 90 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc95820a..0380b4f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## **[Unreleased]** +## 0.16.2 - 2020-03-11 + +- [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again. + ## 0.16.1 - 2020-03-11 - [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command. diff --git a/Cargo.lock b/Cargo.lock index d1ce82547..3cd09fa0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1820,7 +1820,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "0.16.1" +version = "0.16.2" dependencies = [ "atty", "byteorder", @@ -1851,7 +1851,7 @@ dependencies = [ [[package]] name = "wasmer-clif-backend" -version = "0.16.1" +version = "0.16.2" dependencies = [ "byteorder", "cranelift-codegen", @@ -1902,14 +1902,14 @@ dependencies = [ [[package]] name = "wasmer-dev-utils" -version = "0.16.1" +version = "0.16.2" dependencies = [ "libc", ] [[package]] name = "wasmer-emscripten" -version = "0.16.1" +version = "0.16.2" dependencies = [ "byteorder", "getrandom", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "wasmer-emscripten-tests" -version = "0.16.1" +version = "0.16.2" dependencies = [ "glob 0.3.0", "wabt", @@ -1936,7 +1936,7 @@ dependencies = [ [[package]] name = "wasmer-interface-types" -version = "0.16.1" +version = "0.16.2" dependencies = [ "nom", "wast", @@ -1952,7 +1952,7 @@ dependencies = [ [[package]] name = "wasmer-llvm-backend" -version = "0.16.1" +version = "0.16.2" dependencies = [ "byteorder", "cc", @@ -1983,14 +1983,14 @@ dependencies = [ [[package]] name = "wasmer-middleware-common" -version = "0.16.1" +version = "0.16.2" dependencies = [ "wasmer-runtime-core", ] [[package]] name = "wasmer-middleware-common-tests" -version = "0.16.1" +version = "0.16.2" dependencies = [ "criterion", "wabt", @@ -2003,7 +2003,7 @@ dependencies = [ [[package]] name = "wasmer-runtime" -version = "0.16.1" +version = "0.16.2" dependencies = [ "criterion", "lazy_static", @@ -2020,7 +2020,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-c-api" -version = "0.16.1" +version = "0.16.2" dependencies = [ "cbindgen", "libc", @@ -2032,7 +2032,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core" -version = "0.16.1" +version = "0.16.2" dependencies = [ "bincode", "blake3", @@ -2060,7 +2060,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core-tests" -version = "0.16.1" +version = "0.16.2" dependencies = [ "wabt", "wasmer-clif-backend", @@ -2071,7 +2071,7 @@ dependencies = [ [[package]] name = "wasmer-singlepass-backend" -version = "0.16.1" +version = "0.16.2" dependencies = [ "bincode", "byteorder", @@ -2088,7 +2088,7 @@ dependencies = [ [[package]] name = "wasmer-spectests" -version = "0.16.1" +version = "0.16.2" dependencies = [ "glob 0.3.0", "wabt", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wasmer-wasi" -version = "0.16.1" +version = "0.16.2" dependencies = [ "bincode", "byteorder", @@ -2117,7 +2117,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-experimental-io-devices" -version = "0.16.1" +version = "0.16.2" dependencies = [ "log", "minifb", @@ -2130,7 +2130,7 @@ dependencies = [ [[package]] name = "wasmer-wasi-tests" -version = "0.16.1" +version = "0.16.2" dependencies = [ "glob 0.3.0", "wasmer-clif-backend", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "wasmer-win-exception-handler" -version = "0.16.1" +version = "0.16.2" dependencies = [ "cmake", "libc", diff --git a/Cargo.toml b/Cargo.toml index 05874f849..0bb2e9a82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.16.1" +version = "0.16.2" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 0cda55de6..fa24d5ed4 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } cranelift-native = "0.59.0" cranelift-codegen = "0.59.0" cranelift-entity = "0.59.0" @@ -38,7 +38,7 @@ version = "0.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } -wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.1" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.2" } [features] generate-debug-information = ["wasm-debug"] diff --git a/lib/dev-utils/Cargo.toml b/lib/dev-utils/Cargo.toml index c97b4b16f..07800ed61 100644 --- a/lib/dev-utils/Cargo.toml +++ b/lib/dev-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-dev-utils" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/emscripten-tests/Cargo.toml b/lib/emscripten-tests/Cargo.toml index 750583a85..5065de08a 100644 --- a/lib/emscripten-tests/Cargo.toml +++ b/lib/emscripten-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten-tests" -version = "0.16.1" +version = "0.16.2" description = "Tests for our Emscripten implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,15 +9,15 @@ publish = false build = "build/mod.rs" [dependencies] -wasmer-emscripten = { path = "../emscripten", version = "0.16.1" } -wasmer-runtime = { path = "../runtime", version = "0.16.1", default-features = false } -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", optional = true, features = ["test"] } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } +wasmer-emscripten = { path = "../emscripten", version = "0.16.2" } +wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", optional = true, features = ["test"] } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true } [dev-dependencies] wabt = "0.9.1" -wasmer-dev-utils = { path = "../dev-utils", version = "0.16.1"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.16.2"} [build-dependencies] glob = "0.3" diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index de3469bc7..3fb904902 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -15,7 +15,7 @@ lazy_static = "1.4" libc = "0.2.60" log = "0.4" time = "0.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } [target.'cfg(windows)'.dependencies] getrandom = "0.1" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 859986f8f..3bcebd337 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types" -version = "0.16.1" +version = "0.16.2" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/llvm-backend-tests/Cargo.toml b/lib/llvm-backend-tests/Cargo.toml index 796655413..e916850aa 100644 --- a/lib/llvm-backend-tests/Cargo.toml +++ b/lib/llvm-backend-tests/Cargo.toml @@ -9,8 +9,8 @@ edition = "2018" [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } -wasmer-runtime = { path = "../runtime", version = "0.16.1" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } +wasmer-runtime = { path = "../runtime", version = "0.16.2" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"] } [features] diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 94f786e00..129d7e374 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-llvm-backend" -version = "0.16.1" +version = "0.16.2" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" @@ -10,7 +10,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1", features = ["generate-debug-information-no-export-symbols"] } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2", features = ["generate-debug-information-no-export-symbols"] } wasmparser = "0.51.3" smallvec = "0.6" goblin = "0.0.24" diff --git a/lib/middleware-common-tests/Cargo.toml b/lib/middleware-common-tests/Cargo.toml index 117dc79f0..22b3d723e 100644 --- a/lib/middleware-common-tests/Cargo.toml +++ b/lib/middleware-common-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common-tests" -version = "0.16.1" +version = "0.16.2" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -8,11 +8,11 @@ license = "MIT" publish = false [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } -wasmer-middleware-common = { path = "../middleware-common", version = "0.16.1" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } +wasmer-middleware-common = { path = "../middleware-common", version = "0.16.2" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true } [features] clif = ["wasmer-clif-backend"] diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index 593fe5b8f..345fff897 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common" -version = "0.16.1" +version = "0.16.2" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime common middlewares" license = "MIT" @@ -10,4 +10,4 @@ categories = ["wasm"] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index fbb3287b0..618ba71ba 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.16.1" +version = "0.16.2" description = "Wasmer C API library" documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/" license = "MIT" @@ -20,22 +20,22 @@ libc = "0.2.60" [dependencies.wasmer-runtime] default-features = false path = "../runtime" -version = "0.16.1" +version = "0.16.2" [dependencies.wasmer-runtime-core] default-features = false path = "../runtime-core" -version = "0.16.1" +version = "0.16.2" [dependencies.wasmer-wasi] default-features = false path = "../wasi" -version = "0.16.1" +version = "0.16.2" optional = true [dependencies.wasmer-emscripten] path = "../emscripten" -version = "0.16.1" +version = "0.16.2" optional = true [features] diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml index aea0fd11b..7ea842d8c 100644 --- a/lib/runtime-core-tests/Cargo.toml +++ b/lib/runtime-core-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core-tests" -version = "0.16.1" +version = "0.16.2" description = "Tests for the Wasmer runtime core crate" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ publish = false [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true } [features] default = ["backend-cranelift"] diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 22f7fc40f..967dbe32e 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 6af22a3b8..cb3a1d700 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -11,17 +11,17 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true } lazy_static = "1.4" memmap = "0.7" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.16.1" +version = "0.16.2" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.16.1" +version = "0.16.2" optional = true # Dependencies for caching. diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index 564a26809..889492287 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-singlepass-backend" -version = "0.16.1" +version = "0.16.2" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime single pass compiler backend" license = "MIT" @@ -11,7 +11,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } dynasm = "0.5" dynasmrt = "0.5" lazy_static = "1.4" diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index 26cd21d89..a4b6576ce 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.16.1" +version = "0.16.2" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ edition = "2018" [dependencies] glob = "0.3" -wasmer-runtime = { path = "../runtime", version = "0.16.1", default-features = false} -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true} -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } +wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false} +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true } [build-dependencies] wabt = "0.9.1" diff --git a/lib/wasi-experimental-io-devices/Cargo.toml b/lib/wasi-experimental-io-devices/Cargo.toml index ebf6b4873..06421d396 100644 --- a/lib/wasi-experimental-io-devices/Cargo.toml +++ b/lib/wasi-experimental-io-devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-experimental-io-devices" -version = "0.16.1" +version = "0.16.2" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -14,8 +14,8 @@ maintenance = { status = "experimental" } [dependencies] log = "0.4" minifb = "0.13" -wasmer-wasi = { version = "0.16.1", path = "../wasi" } -wasmer-runtime-core = { version = "0.16.1", path = "../runtime-core" } +wasmer-wasi = { version = "0.16.2", path = "../wasi" } +wasmer-runtime-core = { version = "0.16.2", path = "../runtime-core" } ref_thread_local = "0.0" serde = "1" typetag = "0.1" diff --git a/lib/wasi-tests/Cargo.toml b/lib/wasi-tests/Cargo.toml index 75a8488ef..3a542ab7a 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-tests" -version = "0.16.1" +version = "0.16.2" description = "Tests for our WASI implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,18 +10,18 @@ build = "build/mod.rs" [dependencies] # We set default features to false to be able to use the singlepass backend properly -wasmer-runtime = { path = "../runtime", version = "0.16.1", default-features = false } -wasmer-wasi = { path = "../wasi", version = "0.16.1" } +wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false } +wasmer-wasi = { path = "../wasi", version = "0.16.2" } # hack to get tests to work -wasmer-clif-backend = { path = "../clif-backend", version = "0.16.1", optional = true} -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.1", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.1", features = ["test"], optional = true } +wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true} +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true } [build-dependencies] glob = "0.3" [dev-dependencies] -wasmer-dev-utils = { path = "../dev-utils", version = "0.16.1"} +wasmer-dev-utils = { path = "../dev-utils", version = "0.16.2"} [features] clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index f539f82b0..e26bf73e3 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime WASI implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -19,7 +19,7 @@ getrandom = "0.1" time = "0.1" typetag = "0.1" serde = { version = "1", features = ["derive"] } -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index 75079fc23..02d5c54b4 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.16.1" +version = "0.16.2" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.16.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" } winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.60" diff --git a/scripts/update_version_numbers.sh b/scripts/update_version_numbers.sh index 84418905e..c436e38b7 100755 --- a/scripts/update_version_numbers.sh +++ b/scripts/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.16.0' -NEXT_VERSION='0.16.1' +PREVIOUS_VERSION='0.16.1' +NEXT_VERSION='0.16.2' # quick hack fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 20f4223c1..767409584 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=0.16.1 +AppVersion=0.16.2 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 From 20085e84a688ca18e8426dec9ec663f34aa35369 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 13:57:57 +0100 Subject: [PATCH 74/79] doc(interface-types) Update the state of supported instructions. --- lib/interface-types/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/README.md b/lib/interface-types/README.md index f8f349013..3e7ccdf76 100644 --- a/lib/interface-types/README.md +++ b/lib/interface-types/README.md @@ -52,8 +52,8 @@ Here is the instructions that are implemented: |-|-|-|-|-|-| | `arg.get` | ✅ | ✅ | ✅ | ✅ | ✅ | | `call-core` | ✅ | ✅ | ✅ | ✅ | ✅ | -| `memory-to-string` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `string-to-memory` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `memory-to-string` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `string-to-memory` | ✅ | ✅ | ✅ | ✅ | ✅ | | `call-adapter` | ❌ | ❌ | ❌ | ❌ | ❌ | | `defer-call-core` | ❌ | ❌ | ❌ | ❌ | ❌ | | `i32-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ | From 64729aa8f4e920a96256566ec2658d8fe0f2db61 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 14:39:15 +0100 Subject: [PATCH 75/79] feat(interface-types) Use include ranges to read the memory. --- .../instructions/memory_to_string.rs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs index f96c47f22..e30b70ce0 100644 --- a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -31,7 +31,13 @@ executable_instruction!( let pointer = to_native::(&inputs[1], instruction)? as usize; let memory_view = memory.view(); - if memory_view.len() < pointer + length { + if length == 0 { + runtime.stack.push(InterfaceValue::String("".into())); + + return Ok(()) + } + + if memory_view.len() <= pointer + length - 1 { return Err(InstructionError::new( instruction, InstructionErrorKind::MemoryOutOfBoundsAccess { @@ -41,7 +47,7 @@ executable_instruction!( )); } - let data: Vec = (&memory_view[pointer..pointer + length]) + let data: Vec = (&memory_view[pointer..=pointer + length - 1]) .iter() .map(Cell::get) .collect(); @@ -78,6 +84,24 @@ mod tests { stack: [InterfaceValue::String("Hello, World!".into())], ); + test_executable_instruction!( + test_memory_to_string__empty_string = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::MemoryToString, + ], + invocation_inputs: [ + InterfaceValue::I32(0), + InterfaceValue::I32(0), + ], + instance: Instance { + memory: Memory::new(vec![]), + ..Default::default() + }, + stack: [InterfaceValue::String("".into())], + ); + test_executable_instruction!( test_memory_to_string__read_out_of_memory = instructions: [ From f46099c67b7ab3e531096154167f989e857d492d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 14:49:43 +0100 Subject: [PATCH 76/79] feat(interface-types) Implement `Error` on errors. --- lib/interface-types/src/errors.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/interface-types/src/errors.rs b/lib/interface-types/src/errors.rs index 24ca5566a..5df5c49e3 100644 --- a/lib/interface-types/src/errors.rs +++ b/lib/interface-types/src/errors.rs @@ -3,6 +3,7 @@ use crate::{ast::InterfaceType, interpreter::Instruction}; use std::{ + error::Error, fmt::{self, Display, Formatter}, result::Result, string::{self, ToString}, @@ -28,6 +29,14 @@ pub struct WasmValueNativeCastError { pub to: InterfaceType, } +impl Error for WasmValueNativeCastError {} + +impl Display for WasmValueNativeCastError { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "{:?}", self) + } +} + /// Structure to represent the errors for instructions. #[derive(Debug)] pub struct InstructionError { @@ -47,6 +56,8 @@ impl InstructionError { } } +impl Error for InstructionError {} + impl Display for InstructionError { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { write!( @@ -140,6 +151,8 @@ pub enum InstructionErrorKind { String(string::FromUtf8Error), } +impl Error for InstructionErrorKind {} + impl Display for InstructionErrorKind { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { match self { From b78a6f47abac055e32a00b54e8ba427062c66e9b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 14:51:18 +0100 Subject: [PATCH 77/79] chore(interface-types) Simplify code. --- lib/interface-types/src/interpreter/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 122c482d2..d5f01056b 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -166,10 +166,7 @@ where }; for executable_instruction in self.iter() { - match executable_instruction(&mut runtime) { - Ok(_) => continue, - Err(error) => return Err(error), - } + executable_instruction(&mut runtime)?; } Ok(runtime.stack) From 57d4a076cb7c779b7b01eabf7a17a58f0f160b48 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 13 Mar 2020 16:30:50 -0700 Subject: [PATCH 78/79] Update Rust version in CI to 1.41.1 --- CHANGELOG.md | 1 + azure-pipelines.yml | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38fab56b2..fcfbd3eab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1. - [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` - [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 07f720e86..8665c1dba 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,7 +20,7 @@ jobs: - script: cargo fmt --all -- --check displayName: Lint variables: - rust_toolchain: '1.40.0' + rust_toolchain: '1.41.1' - job: clippy_lint pool: @@ -55,7 +55,7 @@ jobs: CARGO_HTTP_CHECK_REVOKE: false windows: imageName: "vs2017-win2016" - rust_toolchain: '1.40.0' + rust_toolchain: '1.41.1' pool: vmImage: $(imageName) condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying') @@ -114,7 +114,7 @@ jobs: MACOSX_DEPLOYMENT_TARGET: 10.10 windows: imageName: "vs2017-win2016" - rust_toolchain: '1.40.0' + rust_toolchain: '1.41.1' # RUSTFLAGS: -Ctarget-feature=+crt-static pool: vmImage: $(imageName) @@ -183,7 +183,7 @@ jobs: MACOSX_DEPLOYMENT_TARGET: 10.10 windows: imageName: "vs2017-win2016" - rust_toolchain: '1.40.0' + rust_toolchain: '1.41.1' # RUSTFLAGS: -Ctarget-feature=+crt-static pool: vmImage: $(imageName) From 5e2488c1eacd8dc6235ddc4b5f763eec74a224fa Mon Sep 17 00:00:00 2001 From: Syrus Date: Sat, 14 Mar 2020 15:12:18 -0700 Subject: [PATCH 79/79] Fixed lint --- lib/emscripten/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index c4981b305..b19f751d3 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -1058,10 +1058,13 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject pub fn nullfunc(ctx: &mut Ctx, _x: u32) { use crate::process::abort_with_message; debug!("emscripten::nullfunc_i {}", _x); - abort_with_message(ctx, "Invalid function pointer. Perhaps this is an invalid value \ + abort_with_message( + ctx, + "Invalid function pointer. Perhaps this is an invalid value \ (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an \ incorrect type, which will fail? (it is worth building your source files with -Werror (\ - warnings are errors), as warnings can indicate undefined behavior which can cause this)"); + warnings are errors), as warnings can indicate undefined behavior which can cause this)", + ); } /// The current version of this crate