diff --git a/crates/wasm_interp/src/instance.rs b/crates/wasm_interp/src/instance.rs index 1e03165a5e..01226b6f23 100644 --- a/crates/wasm_interp/src/instance.rs +++ b/crates/wasm_interp/src/instance.rs @@ -10,7 +10,7 @@ use roc_wasm_module::{Value, ValueType}; use crate::call_stack::CallStack; use crate::value_stack::ValueStack; -use crate::ImportDispatcher; +use crate::{Error, ImportDispatcher}; pub enum Action { Continue, @@ -218,7 +218,26 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { &mut self.program_counter, ); - while let Action::Continue = self.execute_next_instruction(module) {} + loop { + match self.execute_next_instruction(module) { + Ok(Action::Continue) => {} + Ok(Action::Break) => { + break; + } + Err(Error::ValueStackType(expected, actual)) => { + return Err(format!( + "I found a type mismatch in the Value Stack at file offset {:#x}. Expected {:?}, but found {:?}.", + self.program_counter + module.code.section_offset as usize, expected, actual + )); + } + Err(Error::ValueStackEmpty) => { + return Err(format!( + "I tried to pop a value from the Value Stack at file offset {:#x}, but it was empty.", + self.program_counter + module.code.section_offset as usize + )); + } + } + } let return_value = if !self.value_stack.is_empty() { Some(self.value_stack.pop()) @@ -253,26 +272,26 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } } - fn get_load_address(&mut self, module: &WasmModule<'a>) -> u32 { + fn get_load_address(&mut self, module: &WasmModule<'a>) -> Result { // Alignment is not used in the execution steps from the spec! Maybe it's just an optimization hint? // https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions // Also note: in the text format we can specify the useless `align=` but not the useful `offset=`! let _alignment = self.fetch_immediate_u32(module); let offset = self.fetch_immediate_u32(module); - let base_addr = self.value_stack.pop_u32(); - base_addr + offset + let base_addr = self.value_stack.pop_u32()?; + Ok(base_addr + offset) } - fn get_store_addr_value(&mut self, module: &WasmModule<'a>) -> (usize, Value) { + fn get_store_addr_value(&mut self, module: &WasmModule<'a>) -> Result<(usize, Value), Error> { // Alignment is not used in the execution steps from the spec! Maybe it's just an optimization hint? // https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions // Also note: in the text format we can specify the useless `align=` but not the useful `offset=`! let _alignment = self.fetch_immediate_u32(module); let offset = self.fetch_immediate_u32(module); let value = self.value_stack.pop(); - let base_addr = self.value_stack.pop_u32(); + let base_addr = self.value_stack.pop_u32()?; let addr = (base_addr + offset) as usize; - (addr, value) + Ok((addr, value)) } fn write_debug(&mut self, value: T) { @@ -394,7 +413,10 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } } - pub fn execute_next_instruction(&mut self, module: &WasmModule<'a>) -> Action { + pub(crate) fn execute_next_instruction( + &mut self, + module: &WasmModule<'a>, + ) -> Result { use OpCode::*; let file_offset = self.program_counter as u32 + module.code.section_offset; @@ -428,7 +450,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } IF => { self.fetch_immediate_u32(module); // blocktype (ignored) - let condition = self.value_stack.pop_i32(); + let condition = self.value_stack.pop_i32()?; self.block_loop_addrs.push(None); if condition == 0 { let mut depth = self.block_loop_addrs.len(); @@ -473,13 +495,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } BRIF => { let relative_blocks_outward = self.fetch_immediate_u32(module); - let condition = self.value_stack.pop_i32(); + let condition = self.value_stack.pop_i32()?; if condition != 0 { self.do_break(relative_blocks_outward, module); } } BRTABLE => { - let selector = self.value_stack.pop_u32(); + let selector = self.value_stack.pop_u32()?; let nondefault_condition_count = self.fetch_immediate_u32(module); let mut selected = None; for i in 0..nondefault_condition_count { @@ -502,7 +524,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { CALLINDIRECT => { let table_index = self.fetch_immediate_u32(module); let expected_signature = self.fetch_immediate_u32(module); - let element_index = self.value_stack.pop_u32(); + let element_index = self.value_stack.pop_u32()?; // So far, all compilers seem to be emitting MVP-compatible code. (Rust, Zig, Roc...) assert_eq!( @@ -525,10 +547,14 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.value_stack.pop(); } SELECT => { - let c = self.value_stack.pop_i32(); + let c = self.value_stack.pop_i32()?; let val2 = self.value_stack.pop(); let val1 = self.value_stack.pop(); - assert_eq!(ValueType::from(val1), ValueType::from(val2)); + let actual = ValueType::from(val2); + let expected = ValueType::from(val1); + if actual != expected { + return Err(Error::ValueStackType(expected, actual)); + } let result = if c != 0 { val1 } else { val2 }; self.value_stack.push(result); } @@ -556,149 +582,149 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.globals[index as usize] = self.value_stack.pop(); } I32LOAD => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 4]; bytes.copy_from_slice(&self.memory[addr..][..4]); let value = i32::from_le_bytes(bytes); self.value_stack.push(Value::I32(value)); } I64LOAD => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 8]; bytes.copy_from_slice(&self.memory[addr..][..8]); let value = i64::from_le_bytes(bytes); self.value_stack.push(Value::I64(value)); } F32LOAD => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 4]; bytes.copy_from_slice(&self.memory[addr..][..4]); let value = f32::from_le_bytes(bytes); self.value_stack.push(Value::F32(value)); } F64LOAD => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 8]; bytes.copy_from_slice(&self.memory[addr..][..8]); let value = f64::from_le_bytes(bytes); self.value_stack.push(Value::F64(value)); } I32LOAD8S => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 1]; bytes.copy_from_slice(&self.memory[addr..][..1]); let value = i8::from_le_bytes(bytes); self.value_stack.push(Value::I32(value as i32)); } I32LOAD8U => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let value = self.memory[addr]; self.value_stack.push(Value::I32(value as i32)); } I32LOAD16S => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 2]; bytes.copy_from_slice(&self.memory[addr..][..2]); let value = i16::from_le_bytes(bytes); self.value_stack.push(Value::I32(value as i32)); } I32LOAD16U => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 2]; bytes.copy_from_slice(&self.memory[addr..][..2]); let value = u16::from_le_bytes(bytes); self.value_stack.push(Value::I32(value as i32)); } I64LOAD8S => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 1]; bytes.copy_from_slice(&self.memory[addr..][..1]); let value = i8::from_le_bytes(bytes); self.value_stack.push(Value::I64(value as i64)); } I64LOAD8U => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let value = self.memory[addr]; self.value_stack.push(Value::I64(value as i64)); } I64LOAD16S => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 2]; bytes.copy_from_slice(&self.memory[addr..][..2]); let value = i16::from_le_bytes(bytes); self.value_stack.push(Value::I64(value as i64)); } I64LOAD16U => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 2]; bytes.copy_from_slice(&self.memory[addr..][..2]); let value = u16::from_le_bytes(bytes); self.value_stack.push(Value::I64(value as i64)); } I64LOAD32S => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 4]; bytes.copy_from_slice(&self.memory[addr..][..4]); let value = i32::from_le_bytes(bytes); self.value_stack.push(Value::I64(value as i64)); } I64LOAD32U => { - let addr = self.get_load_address(module) as usize; + let addr = self.get_load_address(module)? as usize; let mut bytes = [0; 4]; bytes.copy_from_slice(&self.memory[addr..][..4]); let value = u32::from_le_bytes(bytes); self.value_stack.push(Value::I64(value as i64)); } I32STORE => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i32(); let target = &mut self.memory[addr..][..4]; target.copy_from_slice(&unwrapped.to_le_bytes()); } I64STORE => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i64(); let target = &mut self.memory[addr..][..8]; target.copy_from_slice(&unwrapped.to_le_bytes()); } F32STORE => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_f32(); let target = &mut self.memory[addr..][..4]; target.copy_from_slice(&unwrapped.to_le_bytes()); } F64STORE => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_f64(); let target = &mut self.memory[addr..][..8]; target.copy_from_slice(&unwrapped.to_le_bytes()); } I32STORE8 => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i32(); let target = &mut self.memory[addr..][..1]; target.copy_from_slice(&unwrapped.to_le_bytes()[..1]); } I32STORE16 => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i32(); let target = &mut self.memory[addr..][..2]; target.copy_from_slice(&unwrapped.to_le_bytes()[..2]); } I64STORE8 => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i64(); let target = &mut self.memory[addr..][..1]; target.copy_from_slice(&unwrapped.to_le_bytes()[..1]); } I64STORE16 => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i64(); let target = &mut self.memory[addr..][..2]; target.copy_from_slice(&unwrapped.to_le_bytes()[..2]); } I64STORE32 => { - let (addr, value) = self.get_store_addr_value(module); + let (addr, value) = self.get_store_addr_value(module)?; let unwrapped = value.unwrap_i64(); let target = &mut self.memory[addr..][..4]; target.copy_from_slice(&unwrapped.to_le_bytes()[..4]); @@ -710,7 +736,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { GROWMEMORY => { let old_bytes = self.memory.len() as u32; let old_pages = old_bytes / MemorySection::PAGE_SIZE as u32; - let grow_pages = self.value_stack.pop_u32(); + let grow_pages = self.value_stack.pop_u32()?; let grow_bytes = grow_pages * MemorySection::PAGE_SIZE; let new_bytes = old_bytes + grow_bytes; @@ -754,424 +780,424 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } I32EQZ => { - let arg = self.value_stack.pop_i32(); + let arg = self.value_stack.pop_i32()?; let result: bool = arg == 0; self.value_stack.push(Value::I32(result as i32)); } I32EQ => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let result: bool = arg1 == arg2; self.value_stack.push(Value::I32(result as i32)); } I32NE => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let result: bool = arg1 != arg2; self.value_stack.push(Value::I32(result as i32)); } I32LTS => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let result: bool = arg1 < arg2; self.value_stack.push(Value::I32(result as i32)); } I32LTU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let result: bool = arg1 < arg2; self.value_stack.push(Value::I32(result as i32)); } I32GTS => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let result: bool = arg1 > arg2; self.value_stack.push(Value::I32(result as i32)); } I32GTU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let result: bool = arg1 > arg2; self.value_stack.push(Value::I32(result as i32)); } I32LES => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let result: bool = arg1 <= arg2; self.value_stack.push(Value::I32(result as i32)); } I32LEU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let result: bool = arg1 <= arg2; self.value_stack.push(Value::I32(result as i32)); } I32GES => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let result: bool = arg1 >= arg2; self.value_stack.push(Value::I32(result as i32)); } I32GEU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let result: bool = arg1 >= arg2; self.value_stack.push(Value::I32(result as i32)); } I64EQZ => { - let arg = self.value_stack.pop_i64(); + let arg = self.value_stack.pop_i64()?; let result: bool = arg == 0; self.value_stack.push(Value::I32(result as i32)); } I64EQ => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let result: bool = arg1 == arg2; self.value_stack.push(Value::I32(result as i32)); } I64NE => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let result: bool = arg1 != arg2; self.value_stack.push(Value::I32(result as i32)); } I64LTS => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let result: bool = arg1 < arg2; self.value_stack.push(Value::I32(result as i32)); } I64LTU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let result: bool = arg1 < arg2; self.value_stack.push(Value::I32(result as i32)); } I64GTS => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let result: bool = arg1 > arg2; self.value_stack.push(Value::I32(result as i32)); } I64GTU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let result: bool = arg1 > arg2; self.value_stack.push(Value::I32(result as i32)); } I64LES => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let result: bool = arg1 <= arg2; self.value_stack.push(Value::I32(result as i32)); } I64LEU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let result: bool = arg1 <= arg2; self.value_stack.push(Value::I32(result as i32)); } I64GES => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let result: bool = arg1 >= arg2; self.value_stack.push(Value::I32(result as i32)); } I64GEU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let result: bool = arg1 >= arg2; self.value_stack.push(Value::I32(result as i32)); } F32EQ => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result: bool = arg1 == arg2; self.value_stack.push(Value::I32(result as i32)); } F32NE => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result: bool = arg1 != arg2; self.value_stack.push(Value::I32(result as i32)); } F32LT => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result: bool = arg1 < arg2; self.value_stack.push(Value::I32(result as i32)); } F32GT => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result: bool = arg1 > arg2; self.value_stack.push(Value::I32(result as i32)); } F32LE => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result: bool = arg1 <= arg2; self.value_stack.push(Value::I32(result as i32)); } F32GE => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result: bool = arg1 >= arg2; self.value_stack.push(Value::I32(result as i32)); } F64EQ => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result: bool = arg1 == arg2; self.value_stack.push(Value::I32(result as i32)); } F64NE => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result: bool = arg1 != arg2; self.value_stack.push(Value::I32(result as i32)); } F64LT => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result: bool = arg1 < arg2; self.value_stack.push(Value::I32(result as i32)); } F64GT => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result: bool = arg1 > arg2; self.value_stack.push(Value::I32(result as i32)); } F64LE => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result: bool = arg1 <= arg2; self.value_stack.push(Value::I32(result as i32)); } F64GE => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result: bool = arg1 >= arg2; self.value_stack.push(Value::I32(result as i32)); } I32CLZ => { - let arg = self.value_stack.pop_u32(); + let arg = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg.leading_zeros())); } I32CTZ => { - let arg = self.value_stack.pop_u32(); + let arg = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg.trailing_zeros())); } I32POPCNT => { - let arg = self.value_stack.pop_u32(); + let arg = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg.count_ones())); } I32ADD => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; self.value_stack.push(Value::from(arg1.wrapping_add(arg2))); } I32SUB => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; self.value_stack.push(Value::from(arg1.wrapping_sub(arg2))); } I32MUL => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; self.value_stack.push(Value::from(arg1.wrapping_mul(arg2))); } I32DIVS => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; self.value_stack.push(Value::from(arg1.wrapping_div(arg2))); } I32DIVU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg1.wrapping_div(arg2))); } I32REMS => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; self.value_stack.push(Value::from(arg1.wrapping_rem(arg2))); } I32REMU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg1.wrapping_rem(arg2))); } I32AND => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg1 & arg2)); } I32OR => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg1 | arg2)); } I32XOR => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg1 ^ arg2)); } I32SHL => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; // Take modulo N as per the spec https://webassembly.github.io/spec/core/exec/numerics.html#op-ishl let k = arg2 % 32; self.value_stack.push(Value::from(arg1 << k)); } I32SHRS => { - let arg2 = self.value_stack.pop_i32(); - let arg1 = self.value_stack.pop_i32(); + let arg2 = self.value_stack.pop_i32()?; + let arg1 = self.value_stack.pop_i32()?; let k = arg2 % 32; self.value_stack.push(Value::from(arg1 >> k)); } I32SHRU => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let k = arg2 % 32; self.value_stack.push(Value::from(arg1 >> k)); } I32ROTL => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let k = arg2 % 32; self.value_stack.push(Value::from(arg1.rotate_left(k))); } I32ROTR => { - let arg2 = self.value_stack.pop_u32(); - let arg1 = self.value_stack.pop_u32(); + let arg2 = self.value_stack.pop_u32()?; + let arg1 = self.value_stack.pop_u32()?; let k = arg2 % 32; self.value_stack.push(Value::from(arg1.rotate_right(k))); } I64CLZ => { - let arg = self.value_stack.pop_u64(); + let arg = self.value_stack.pop_u64()?; self.value_stack .push(Value::from(arg.leading_zeros() as u64)); } I64CTZ => { - let arg = self.value_stack.pop_u64(); + let arg = self.value_stack.pop_u64()?; self.value_stack .push(Value::from(arg.trailing_zeros() as u64)); } I64POPCNT => { - let arg = self.value_stack.pop_u64(); + let arg = self.value_stack.pop_u64()?; self.value_stack.push(Value::from(arg.count_ones() as u64)); } I64ADD => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; self.value_stack.push(Value::from(arg1.wrapping_add(arg2))); } I64SUB => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; self.value_stack.push(Value::from(arg1.wrapping_sub(arg2))); } I64MUL => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; self.value_stack.push(Value::from(arg1.wrapping_mul(arg2))); } I64DIVS => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; self.value_stack.push(Value::from(arg1.wrapping_div(arg2))); } I64DIVU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; self.value_stack.push(Value::from(arg1.wrapping_div(arg2))); } I64REMS => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; self.value_stack.push(Value::from(arg1.wrapping_rem(arg2))); } I64REMU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; self.value_stack.push(Value::from(arg1.wrapping_rem(arg2))); } I64AND => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; self.value_stack.push(Value::from(arg1 & arg2)); } I64OR => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; self.value_stack.push(Value::from(arg1 | arg2)); } I64XOR => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; self.value_stack.push(Value::from(arg1 ^ arg2)); } I64SHL => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; // Take modulo N as per the spec https://webassembly.github.io/spec/core/exec/numerics.html#op-ishl let k = arg2 % 64; self.value_stack.push(Value::from(arg1 << k)); } I64SHRS => { - let arg2 = self.value_stack.pop_i64(); - let arg1 = self.value_stack.pop_i64(); + let arg2 = self.value_stack.pop_i64()?; + let arg1 = self.value_stack.pop_i64()?; let k = arg2 % 64; self.value_stack.push(Value::from(arg1 >> k)); } I64SHRU => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let k = arg2 % 64; self.value_stack.push(Value::from(arg1 >> k)); } I64ROTL => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let k = (arg2 % 64) as u32; self.value_stack.push(Value::from(arg1.rotate_left(k))); } I64ROTR => { - let arg2 = self.value_stack.pop_u64(); - let arg1 = self.value_stack.pop_u64(); + let arg2 = self.value_stack.pop_u64()?; + let arg1 = self.value_stack.pop_u64()?; let k = (arg2 % 64) as u32; self.value_stack.push(Value::from(arg1.rotate_right(k))); } F32ABS => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg.abs())); } F32NEG => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(-arg)); } F32CEIL => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg.ceil())); } F32FLOOR => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg.floor())); } F32TRUNC => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg.trunc())); } F32NEAREST => { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; let rounded = arg.round(); // "Rounds half-way cases away from 0.0" let frac = arg - rounded; let result = if frac == 0.5 || frac == -0.5 { @@ -1190,44 +1216,44 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.value_stack.push(Value::F32(result)); } F32SQRT => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg.sqrt())); } F32ADD => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg1 + arg2)); } F32SUB => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg1 - arg2)); } F32MUL => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg1 * arg2)); } F32DIV => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; self.value_stack.push(Value::F32(arg1 / arg2)); } F32MIN => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result = if arg1 < arg2 { arg1 } else { arg2 }; self.value_stack.push(Value::F32(result)); } F32MAX => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result = if arg1 > arg2 { arg1 } else { arg2 }; self.value_stack.push(Value::F32(result)); } F32COPYSIGN => { - let arg2 = self.value_stack.pop_f32(); - let arg1 = self.value_stack.pop_f32(); + let arg2 = self.value_stack.pop_f32()?; + let arg1 = self.value_stack.pop_f32()?; let result = if arg1.is_sign_negative() == arg2.is_sign_negative() { arg1 } else { @@ -1237,28 +1263,28 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } F64ABS => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg.abs())); } F64NEG => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(-arg)); } F64CEIL => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg.ceil())); } F64FLOOR => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg.floor())); } F64TRUNC => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg.trunc())); } F64NEAREST => { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; let rounded = arg.round(); // "Rounds half-way cases away from 0.0" let frac = arg - rounded; let result = if frac == 0.5 || frac == -0.5 { @@ -1277,44 +1303,44 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.value_stack.push(Value::F64(result)); } F64SQRT => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg.sqrt())); } F64ADD => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg1 + arg2)); } F64SUB => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg1 - arg2)); } F64MUL => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg1 * arg2)); } F64DIV => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; self.value_stack.push(Value::F64(arg1 / arg2)); } F64MIN => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result = if arg1 < arg2 { arg1 } else { arg2 }; self.value_stack.push(Value::F64(result)); } F64MAX => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result = if arg1 > arg2 { arg1 } else { arg2 }; self.value_stack.push(Value::F64(result)); } F64COPYSIGN => { - let arg2 = self.value_stack.pop_f64(); - let arg1 = self.value_stack.pop_f64(); + let arg2 = self.value_stack.pop_f64()?; + let arg1 = self.value_stack.pop_f64()?; let result = if arg1.is_sign_negative() == arg2.is_sign_negative() { arg1 } else { @@ -1324,132 +1350,132 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } I32WRAPI64 => { - let arg = self.value_stack.pop_u64(); + let arg = self.value_stack.pop_u64()?; let wrapped: u32 = (arg & 0xffff_ffff) as u32; self.value_stack.push(Value::from(wrapped)); } I32TRUNCSF32 => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; if arg < i32::MIN as f32 || arg > i32::MAX as f32 { panic!("Cannot truncate {} from F32 to I32", arg); } self.value_stack.push(Value::I32(arg as i32)); } I32TRUNCUF32 => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; if arg < u32::MIN as f32 || arg > u32::MAX as f32 { panic!("Cannot truncate {} from F32 to unsigned I32", arg); } self.value_stack.push(Value::from(arg as u32)); } I32TRUNCSF64 => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; if arg < i32::MIN as f64 || arg > i32::MAX as f64 { panic!("Cannot truncate {} from F64 to I32", arg); } self.value_stack.push(Value::I32(arg as i32)); } I32TRUNCUF64 => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; if arg < u32::MIN as f64 || arg > u32::MAX as f64 { panic!("Cannot truncate {} from F64 to unsigned I32", arg); } self.value_stack.push(Value::from(arg as u32)); } I64EXTENDSI32 => { - let arg = self.value_stack.pop_i32(); + let arg = self.value_stack.pop_i32()?; self.value_stack.push(Value::I64(arg as i64)); } I64EXTENDUI32 => { - let arg = self.value_stack.pop_u32(); + let arg = self.value_stack.pop_u32()?; self.value_stack.push(Value::from(arg as u64)); } I64TRUNCSF32 => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; if arg < i64::MIN as f32 || arg > i64::MAX as f32 { panic!("Cannot truncate {} from F32 to I64", arg); } self.value_stack.push(Value::I64(arg as i64)); } I64TRUNCUF32 => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; if arg < u64::MIN as f32 || arg > u64::MAX as f32 { panic!("Cannot truncate {} from F32 to unsigned I64", arg); } self.value_stack.push(Value::from(arg as u64)); } I64TRUNCSF64 => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; if arg < i64::MIN as f64 || arg > i64::MAX as f64 { panic!("Cannot truncate {} from F64 to I64", arg); } self.value_stack.push(Value::I64(arg as i64)); } I64TRUNCUF64 => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; if arg < u64::MIN as f64 || arg > u64::MAX as f64 { panic!("Cannot truncate {} from F64 to unsigned I64", arg); } self.value_stack.push(Value::from(arg as u64)); } F32CONVERTSI32 => { - let arg = self.value_stack.pop_i32(); + let arg = self.value_stack.pop_i32()?; self.value_stack.push(Value::F32(arg as f32)); } F32CONVERTUI32 => { - let arg = self.value_stack.pop_u32(); + let arg = self.value_stack.pop_u32()?; self.value_stack.push(Value::F32(arg as f32)); } F32CONVERTSI64 => { - let arg = self.value_stack.pop_i64(); + let arg = self.value_stack.pop_i64()?; self.value_stack.push(Value::F32(arg as f32)); } F32CONVERTUI64 => { - let arg = self.value_stack.pop_u64(); + let arg = self.value_stack.pop_u64()?; self.value_stack.push(Value::F32(arg as f32)); } F32DEMOTEF64 => { - let arg = self.value_stack.pop_f64(); + let arg = self.value_stack.pop_f64()?; self.value_stack.push(Value::F32(arg as f32)); } F64CONVERTSI32 => { - let arg = self.value_stack.pop_i32(); + let arg = self.value_stack.pop_i32()?; self.value_stack.push(Value::F64(arg as f64)); } F64CONVERTUI32 => { - let arg = self.value_stack.pop_u32(); + let arg = self.value_stack.pop_u32()?; self.value_stack.push(Value::F64(arg as f64)); } F64CONVERTSI64 => { - let arg = self.value_stack.pop_i64(); + let arg = self.value_stack.pop_i64()?; self.value_stack.push(Value::F64(arg as f64)); } F64CONVERTUI64 => { - let arg = self.value_stack.pop_u64(); + let arg = self.value_stack.pop_u64()?; self.value_stack.push(Value::F64(arg as f64)); } F64PROMOTEF32 => { - let arg = self.value_stack.pop_f32(); + let arg = self.value_stack.pop_f32()?; self.value_stack.push(Value::F64(arg as f64)); } I32REINTERPRETF32 => { - let x = self.value_stack.pop_f32(); + let x = self.value_stack.pop_f32()?; self.value_stack .push(Value::I32(i32::from_ne_bytes(x.to_ne_bytes()))); } I64REINTERPRETF64 => { - let x = self.value_stack.pop_f64(); + let x = self.value_stack.pop_f64()?; self.value_stack .push(Value::I64(i64::from_ne_bytes(x.to_ne_bytes()))); } F32REINTERPRETI32 => { - let x = self.value_stack.pop_i32(); + let x = self.value_stack.pop_i32()?; self.value_stack .push(Value::F32(f32::from_ne_bytes(x.to_ne_bytes()))); } F64REINTERPRETI64 => { - let x = self.value_stack.pop_i64(); + let x = self.value_stack.pop_i64()?; self.value_stack .push(Value::F64(f64::from_ne_bytes(x.to_ne_bytes()))); } @@ -1473,6 +1499,6 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } } - action + Ok(action) } } diff --git a/crates/wasm_interp/src/lib.rs b/crates/wasm_interp/src/lib.rs index b7841d8684..7b81559741 100644 --- a/crates/wasm_interp/src/lib.rs +++ b/crates/wasm_interp/src/lib.rs @@ -7,7 +7,7 @@ pub mod wasi; // Main external interface pub use instance::Instance; -use roc_wasm_module::Value; +use roc_wasm_module::{Value, ValueType}; use value_stack::ValueStack; use wasi::WasiDispatcher; @@ -56,3 +56,21 @@ impl<'a> ImportDispatcher for DefaultImportDispatcher<'a> { } } } + +#[derive(Debug, PartialEq)] +pub(crate) enum Error { + ValueStackType(ValueType, ValueType), + ValueStackEmpty, +} + +impl Error { + fn value_stack_type(expected: ValueType, is_float: bool, is_64: bool) -> Self { + let ty = match (is_float, is_64) { + (false, false) => ValueType::I32, + (false, true) => ValueType::I64, + (true, false) => ValueType::F32, + (true, true) => ValueType::F64, + }; + Error::ValueStackType(expected, ty) + } +} diff --git a/crates/wasm_interp/src/tests/test_basics.rs b/crates/wasm_interp/src/tests/test_basics.rs index 4c2fe64ed3..21673d31bc 100644 --- a/crates/wasm_interp/src/tests/test_basics.rs +++ b/crates/wasm_interp/src/tests/test_basics.rs @@ -93,9 +93,9 @@ fn test_loop_help(end: i32, expected: i32) { &mut state.program_counter, ); - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} - assert_eq!(state.value_stack.pop_i32(), expected); + assert_eq!(state.value_stack.pop_i32(), Ok(expected)); } #[test] @@ -161,9 +161,9 @@ fn test_if_else_help(condition: i32, expected: i32) { &mut state.program_counter, ); - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} - assert_eq!(state.value_stack.pop_i32(), expected); + assert_eq!(state.value_stack.pop_i32(), Ok(expected)); } #[test] @@ -250,7 +250,7 @@ fn test_br() { &mut state.program_counter, ); - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} assert_eq!(state.value_stack.pop(), Value::I32(111)) } @@ -348,7 +348,7 @@ fn test_br_if_help(condition: i32, expected: i32) { &mut state.program_counter, ); - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} assert_eq!(state.value_stack.pop(), Value::I32(expected)) } @@ -452,7 +452,7 @@ fn test_br_table_help(condition: i32, expected: i32) { &mut state.program_counter, ); - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} assert_eq!(state.value_stack.pop(), Value::I32(expected)) } @@ -664,7 +664,7 @@ fn test_call_return_with_args() { state.program_counter = func0_first_instruction as usize; - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} assert_eq!(state.value_stack.peek(), Value::I32(4)); } @@ -785,7 +785,7 @@ fn test_select_help(first: Value, second: Value, condition: i32, expected: Value &mut state.program_counter, ); - while let Action::Continue = state.execute_next_instruction(&module) {} + while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} assert_eq!(state.value_stack.pop(), expected); } @@ -818,9 +818,9 @@ fn test_set_get_local() { module.code.bytes.push(OpCode::GETLOCAL as u8); module.code.bytes.encode_u32(2); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.len(), 1); assert_eq!(state.value_stack.pop(), Value::I32(12345)); } @@ -853,9 +853,9 @@ fn test_tee_get_local() { module.code.bytes.push(OpCode::GETLOCAL as u8); module.code.bytes.encode_u32(2); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.len(), 2); assert_eq!(state.value_stack.pop(), Value::I32(12345)); assert_eq!(state.value_stack.pop(), Value::I32(12345)); @@ -879,10 +879,10 @@ fn test_global() { module.code.bytes.push(OpCode::GETGLOBAL as u8); module.code.bytes.encode_u32(1); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.len(), 2); assert_eq!(state.value_stack.pop(), Value::I32(555)); assert_eq!(state.value_stack.pop(), Value::I32(222)); @@ -897,7 +897,7 @@ fn test_i32const() { module.code.bytes.push(OpCode::I32CONST as u8); module.code.bytes.encode_i32(12345); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.pop(), Value::I32(12345)) } @@ -910,7 +910,7 @@ fn test_i64const() { module.code.bytes.push(OpCode::I64CONST as u8); module.code.bytes.encode_i64(1234567890); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.pop(), Value::I64(1234567890)) } @@ -923,7 +923,7 @@ fn test_f32const() { module.code.bytes.push(OpCode::F32CONST as u8); module.code.bytes.encode_f32(123.45); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.pop(), Value::F32(123.45)) } @@ -936,6 +936,6 @@ fn test_f64const() { module.code.bytes.push(OpCode::F64CONST as u8); module.code.bytes.encode_f64(12345.67890); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.pop(), Value::F64(12345.67890)) } diff --git a/crates/wasm_interp/src/tests/test_mem.rs b/crates/wasm_interp/src/tests/test_mem.rs index c00cea70b4..05d3b9ad00 100644 --- a/crates/wasm_interp/src/tests/test_mem.rs +++ b/crates/wasm_interp/src/tests/test_mem.rs @@ -18,7 +18,7 @@ fn test_currentmemory() { module.code.bytes.push(OpCode::CURRENTMEMORY as u8); let mut state = Instance::new(&arena, pages, pc, [], DEFAULT_IMPORTS); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.value_stack.pop(), Value::I32(3)) } @@ -36,8 +36,8 @@ fn test_growmemory() { module.code.bytes.push(OpCode::GROWMEMORY as u8); let mut state = Instance::new(&arena, existing_pages, pc, [], DEFAULT_IMPORTS); - state.execute_next_instruction(&module); - state.execute_next_instruction(&module); + state.execute_next_instruction(&module).unwrap(); + state.execute_next_instruction(&module).unwrap(); assert_eq!(state.memory.len(), 5 * MemorySection::PAGE_SIZE as usize); } diff --git a/crates/wasm_interp/src/value_stack.rs b/crates/wasm_interp/src/value_stack.rs index 246f1ffa31..cccd8522ab 100644 --- a/crates/wasm_interp/src/value_stack.rs +++ b/crates/wasm_interp/src/value_stack.rs @@ -3,6 +3,8 @@ use bumpalo::{collections::Vec, Bump}; use roc_wasm_module::{Value, ValueType}; use std::{fmt::Debug, mem::size_of}; +use crate::Error; + /// Memory-efficient Struct-of-Arrays storage for the value stack. /// Pack the values and their types as densely as possible, /// to get better cache usage, at the expense of some extra logic. @@ -15,16 +17,20 @@ pub struct ValueStack<'a> { macro_rules! pop_bytes { ($ty: ty, $bytes: expr) => {{ const SIZE: usize = size_of::<$ty>(); - let bytes_idx = $bytes.len() - SIZE; - let mut b = [0; SIZE]; - b.copy_from_slice(&$bytes[bytes_idx..][..SIZE]); - $bytes.truncate(bytes_idx); - <$ty>::from_ne_bytes(b) + if $bytes.len() < SIZE { + Err(Error::ValueStackEmpty) + } else { + let bytes_idx = $bytes.len() - SIZE; + let mut b = [0; SIZE]; + b.copy_from_slice(&$bytes[bytes_idx..][..SIZE]); + $bytes.truncate(bytes_idx); + Ok(<$ty>::from_ne_bytes(b)) + } }}; } impl<'a> ValueStack<'a> { - pub fn new(arena: &'a Bump) -> Self { + pub(crate) fn new(arena: &'a Bump) -> Self { ValueStack { bytes: Vec::with_capacity_in(1024, arena), is_float: BitVec::with_capacity(1024), @@ -32,15 +38,15 @@ impl<'a> ValueStack<'a> { } } - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.is_64.len() } - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.is_64.is_empty() } - pub fn push(&mut self, value: Value) { + pub(crate) fn push(&mut self, value: Value) { match value { Value::I32(x) => { self.bytes.extend_from_slice(&x.to_ne_bytes()); @@ -65,7 +71,7 @@ impl<'a> ValueStack<'a> { } } - pub fn pop(&mut self) -> Value { + pub(crate) fn pop(&mut self) -> Value { let is_64 = self.is_64.pop().unwrap(); let is_float = self.is_float.pop().unwrap(); let size = if is_64 { 8 } else { 4 }; @@ -75,7 +81,7 @@ impl<'a> ValueStack<'a> { value } - pub fn peek(&self) -> Value { + pub(crate) fn peek(&self) -> Value { let is_64 = *self.is_64.last().unwrap(); let is_float = *self.is_float.last().unwrap(); let size = if is_64 { 8 } else { 4 }; @@ -104,69 +110,63 @@ impl<'a> ValueStack<'a> { } /// Memory addresses etc - pub fn pop_u32(&mut self) -> u32 { + pub(crate) fn pop_u32(&mut self) -> Result { match (self.is_float.pop(), self.is_64.pop()) { (Some(false), Some(false)) => pop_bytes!(u32, self.bytes), - (Some(is_float), Some(is_64)) => panic!( - "Expected I32 but found {:?}", - type_from_flags(is_float, is_64) - ), - _ => panic!("Expected I32 but value stack was empty"), + (Some(is_float), Some(is_64)) => { + Err(Error::value_stack_type(ValueType::I32, is_float, is_64)) + } + _ => Err(Error::ValueStackEmpty), } } - pub fn pop_i32(&mut self) -> i32 { + pub(crate) fn pop_i32(&mut self) -> Result { match (self.is_float.pop(), self.is_64.pop()) { (Some(false), Some(false)) => pop_bytes!(i32, self.bytes), - (Some(is_float), Some(is_64)) => panic!( - "Expected I32 but found {:?}", - type_from_flags(is_float, is_64) - ), - _ => panic!("Expected I32 but value stack was empty"), + (Some(is_float), Some(is_64)) => { + Err(Error::value_stack_type(ValueType::I32, is_float, is_64)) + } + _ => Err(Error::ValueStackEmpty), } } - pub fn pop_u64(&mut self) -> u64 { + pub(crate) fn pop_u64(&mut self) -> Result { match (self.is_float.pop(), self.is_64.pop()) { (Some(false), Some(true)) => pop_bytes!(u64, self.bytes), - (Some(is_float), Some(is_64)) => panic!( - "Expected I64 but found {:?}", - type_from_flags(is_float, is_64) - ), - _ => panic!("Expected I64 but value stack was empty"), + (Some(is_float), Some(is_64)) => { + Err(Error::value_stack_type(ValueType::I64, is_float, is_64)) + } + _ => Err(Error::ValueStackEmpty), } } - pub fn pop_i64(&mut self) -> i64 { + pub(crate) fn pop_i64(&mut self) -> Result { match (self.is_float.pop(), self.is_64.pop()) { (Some(false), Some(true)) => pop_bytes!(i64, self.bytes), - (Some(is_float), Some(is_64)) => panic!( - "Expected I64 but found {:?}", - type_from_flags(is_float, is_64) - ), - _ => panic!("Expected I64 but value stack was empty"), + (Some(is_float), Some(is_64)) => { + Err(Error::value_stack_type(ValueType::I64, is_float, is_64)) + } + _ => Err(Error::ValueStackEmpty), } } - pub fn pop_f32(&mut self) -> f32 { + pub(crate) fn pop_f32(&mut self) -> Result { match (self.is_float.pop(), self.is_64.pop()) { (Some(true), Some(false)) => pop_bytes!(f32, self.bytes), - (Some(is_float), Some(is_64)) => panic!( - "Expected F32 but found {:?}", - type_from_flags(is_float, is_64) - ), - _ => panic!("Expected F32 but value stack was empty"), + (Some(is_float), Some(is_64)) => { + Err(Error::value_stack_type(ValueType::F32, is_float, is_64)) + } + _ => Err(Error::ValueStackEmpty), } } - pub fn pop_f64(&mut self) -> f64 { + pub(crate) fn pop_f64(&mut self) -> Result { match (self.is_float.pop(), self.is_64.pop()) { (Some(true), Some(true)) => pop_bytes!(f64, self.bytes), - (Some(is_float), Some(is_64)) => panic!( - "Expected F64 but found {:?}", - type_from_flags(is_float, is_64) - ), - _ => panic!("Expected F64 but value stack was empty"), + (Some(is_float), Some(is_64)) => { + Err(Error::value_stack_type(ValueType::F64, is_float, is_64)) + } + _ => Err(Error::ValueStackEmpty), } } @@ -196,20 +196,11 @@ impl<'a> ValueStack<'a> { write!(f, "]") } - pub fn get_slice<'b>(&'b self, index: usize) -> ValueStackSlice<'a, 'b> { + pub(crate) fn get_slice<'b>(&'b self, index: usize) -> ValueStackSlice<'a, 'b> { ValueStackSlice { stack: self, index } } } -fn type_from_flags(is_float: bool, is_64: bool) -> ValueType { - match (is_float, is_64) { - (false, false) => ValueType::I32, - (false, true) => ValueType::I64, - (true, false) => ValueType::F32, - (true, true) => ValueType::F64, - } -} - impl Debug for ValueStack<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_from_index(f, 0)