use crate::parser::Interface;
use crate::types::Function;
use thiserror::Error;
use wasmtime::{Instance, Store};
#[derive(Error, Debug)]
pub enum InterfaceError {
#[error("Missing function '{name}' required by interface")]
MissingFunction { name: String },
#[error("Function '{name}' has wrong signature: expected {expected}, got {actual}")]
SignatureMismatch {
name: String,
expected: String,
actual: String,
},
#[error("Missing memory export 'memory'")]
MissingMemory,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExpectedSignature {
GraphAbi,
NoArgsNoResults,
}
impl ExpectedSignature {
fn description(&self) -> &'static str {
match self {
ExpectedSignature::GraphAbi => "(i32, i32) -> i64",
ExpectedSignature::NoArgsNoResults => "() -> ()",
}
}
}
fn expected_signature_for(func: &Function) -> ExpectedSignature {
if func.params.is_empty() && func.results.is_empty() {
ExpectedSignature::NoArgsNoResults
} else {
ExpectedSignature::GraphAbi
}
}
pub fn validate_instance_implements_interface<T>(
store: &mut Store<T>,
instance: &Instance,
interface: &Interface,
) -> Result<(), InterfaceError> {
if instance.get_memory(&mut *store, "memory").is_none() {
return Err(InterfaceError::MissingMemory);
}
let mut required_functions: Vec<&Function> = Vec::new();
required_functions.extend(interface.functions.iter());
for export_block in &interface.exports {
required_functions.extend(export_block.functions.iter());
}
for func in required_functions {
check_function_export(&mut *store, instance, func)?;
}
Ok(())
}
fn check_function_export<T>(
store: &mut Store<T>,
instance: &Instance,
func: &Function,
) -> Result<(), InterfaceError> {
let expected_sig = expected_signature_for(func);
match expected_sig {
ExpectedSignature::GraphAbi => {
match instance.get_typed_func::<(i32, i32), i64>(&mut *store, &func.name) {
Ok(_) => Ok(()),
Err(_) => {
if instance.get_export(&mut *store, &func.name).is_some() {
Err(InterfaceError::SignatureMismatch {
name: func.name.clone(),
expected: expected_sig.description().to_string(),
actual: "different signature".to_string(),
})
} else {
Err(InterfaceError::MissingFunction {
name: func.name.clone(),
})
}
}
}
}
ExpectedSignature::NoArgsNoResults => {
match instance.get_typed_func::<(), ()>(&mut *store, &func.name) {
Ok(_) => Ok(()),
Err(_) => {
if instance.get_export(&mut *store, &func.name).is_some() {
Err(InterfaceError::SignatureMismatch {
name: func.name.clone(),
expected: expected_sig.description().to_string(),
actual: "different signature".to_string(),
})
} else {
Err(InterfaceError::MissingFunction {
name: func.name.clone(),
})
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Param, Type};
#[test]
fn expected_signature_graph_abi_for_params() {
let func =
Function::with_signature("process", vec![Param::new("x", Type::S64)], vec![Type::S64]);
assert_eq!(expected_signature_for(&func), ExpectedSignature::GraphAbi);
}
#[test]
fn expected_signature_no_args_for_empty() {
let func = Function::new("init");
assert_eq!(
expected_signature_for(&func),
ExpectedSignature::NoArgsNoResults
);
}
}