diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index ce86bdf59..fb073765f 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,6 +1,6 @@ use crate::instructions::{ stack::{Stack, Stackable}, - wasm::{self, Value}, + wasm::{self, Type, Value}, Instruction, }; use std::{convert::TryFrom, marker::PhantomData}; @@ -105,6 +105,20 @@ where match runtime.stack.pop(inputs_cardinality) { Some(inputs) => { + let input_types = inputs + .iter() + .map(|input| input.into()) + .collect::>(); + + if input_types != export.inputs() { + return Err(format!( + "`{}` cannot call the exported function `{}` because the value types in the stack mismatch the function signature (expects {:?}).", + instruction_name, + export_name, + export.inputs(), + )) + } + match export.call(&inputs) { Ok(outputs) => { for output in outputs.iter() { @@ -117,7 +131,7 @@ where } } None => Err(format!( - "`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (need {}).", + "`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (needs {}).", instruction_name, export_name, inputs_cardinality, @@ -339,4 +353,53 @@ mod tests { String::from(r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#) ); } + + #[test] + fn test_interpreter_call_export_too_small_stack() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + // ^^^ `sum` expects 2 values in the stack, only one is present + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enought data in the stack for the arguments (needs 2)."#) + ); + } + + #[test] + fn test_interpreter_call_export_invalid_types_in_the_stack() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![Value::I32(3), Value::I64(4)]; + // ^^^ mismatch with `sum` signature + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types in the stack mismatch the function signature (expects [I32, I32])."#) + ); + } } diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index ab5f0e6f8..8857b6db5 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -1,5 +1,6 @@ use std::convert::TryFrom; +#[derive(Debug, PartialEq)] pub enum Type { I32, I64, @@ -17,6 +18,18 @@ pub enum Value { V128(u128), } +impl From<&Value> for Type { + fn from(value: &Value) -> Self { + match value { + Value::I32(_) => Type::I32, + Value::I64(_) => Type::I64, + Value::F32(_) => Type::F32, + Value::F64(_) => Type::F64, + Value::V128(_) => Type::V128, + } + } +} + impl Default for Value { fn default() -> Self { Self::I32(0)