From 3e0d299227077598506955a0fea2551971444482 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 2 Mar 2020 13:41:23 -0800 Subject: [PATCH 01/13] 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 1c6424a754c30b61b2eb877c7ee116598df4b282 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 5 Mar 2020 13:23:50 -0800 Subject: [PATCH 02/13] 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 03/13] 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 04/13] 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 05/13] 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 637ff1e316d56e874a61cb9cdb2cbac91b36fdf4 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 9 Mar 2020 14:33:17 +0100 Subject: [PATCH 06/13] 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 07/13] 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 08/13] 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 1b0bda82dd0d07c073c9b839f76a4f35cfc4dfa0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 10:25:58 +0100 Subject: [PATCH 09/13] 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 10/13] 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 11/13] 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 12/13] 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 13/13] 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