use crate::action::{get, inspect_memory, invoke}; use crate::{ instantiate, ActionError, ActionOutcome, Compiler, InstanceHandle, Namespace, RuntimeValue, SetupError, }; use cranelift_codegen::isa::TargetIsa; use std::borrow::ToOwned; use std::boxed::Box; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::string::{String, ToString}; use std::{fmt, str}; use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig}; /// Indicates an unknown instance was specified. #[derive(Fail, Debug)] pub struct UnknownInstance { instance_name: String, } impl fmt::Display for UnknownInstance { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "no instance {} present", self.instance_name) } } /// Error message used by `WastContext`. #[derive(Fail, Debug)] pub enum ContextError { /// An unknown instance name was used. Instance(UnknownInstance), /// An error occured while performing an action. Action(ActionError), } impl fmt::Display for ContextError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { ContextError::Instance(ref error) => error.fmt(f), ContextError::Action(ref error) => error.fmt(f), } } } /// A convenient context for compiling and executing WebAssembly instances. pub struct Context { namespace: Namespace, compiler: Box, global_exports: Rc>>>, debug_info: bool, } impl Context { /// Construct a new instance of `Context`. pub fn new(compiler: Box) -> Self { Self { namespace: Namespace::new(), compiler, global_exports: Rc::new(RefCell::new(HashMap::new())), debug_info: false, } } /// Get debug_info settings. pub fn debug_info(&self) -> bool { self.debug_info } /// Set debug_info settings. pub fn set_debug_info(&mut self, value: bool) { self.debug_info = value; } /// Construct a new instance of `Context` with the given target. pub fn with_isa(isa: Box) -> Self { Self::new(Box::new(Compiler::new(isa))) } fn validate(&mut self, data: &[u8]) -> Result<(), String> { let config = ValidatingParserConfig { operator_config: OperatorValidatorConfig { enable_threads: false, enable_reference_types: false, enable_bulk_memory: false, enable_simd: false, }, mutable_global_imports: true, }; // TODO: Fix Cranelift to be able to perform validation itself, rather // than calling into wasmparser ourselves here. if validate(data, Some(config)) { Ok(()) } else { // TODO: Work with wasmparser to get better error messages. Err("module did not validate".to_owned()) } } fn instantiate(&mut self, data: &[u8]) -> Result { self.validate(&data).map_err(SetupError::Validate)?; let debug_info = self.debug_info(); instantiate( &mut *self.compiler, &data, &mut self.namespace, Rc::clone(&self.global_exports), debug_info, ) } /// Return the instance associated with the given name. pub fn get_instance( &mut self, instance_name: &str, ) -> Result<&mut InstanceHandle, UnknownInstance> { self.namespace .get_instance(instance_name) .ok_or_else(|| UnknownInstance { instance_name: instance_name.to_string(), }) } /// Instantiate a module instance and register the instance. pub fn instantiate_module( &mut self, instance_name: Option, data: &[u8], ) -> Result { let instance = self.instantiate(data).map_err(ActionError::Setup)?; self.optionally_name_instance(instance_name, instance.clone()); Ok(instance) } /// If `name` isn't None, register it for the given instance. pub fn optionally_name_instance(&mut self, name: Option, instance: InstanceHandle) { if let Some(name) = name { self.namespace.name_instance(name, instance); } } /// Register a name for the given instance. pub fn name_instance(&mut self, name: String, instance: InstanceHandle) { self.namespace.name_instance(name, instance); } /// Register an additional name for an existing registered instance. pub fn alias(&mut self, name: &str, as_name: String) -> Result<(), UnknownInstance> { let instance = self.get_instance(&name)?.clone(); self.name_instance(as_name, instance); Ok(()) } /// Invoke an exported function from a named instance. pub fn invoke_named( &mut self, instance_name: &str, field: &str, args: &[RuntimeValue], ) -> Result { let mut instance = self .get_instance(&instance_name) .map_err(ContextError::Instance)? .clone(); self.invoke(&mut instance, field, args) .map_err(ContextError::Action) } /// Invoke an exported function from an instance. pub fn invoke( &mut self, instance: &mut InstanceHandle, field: &str, args: &[RuntimeValue], ) -> Result { invoke(&mut *self.compiler, instance, field, &args) } /// Get the value of an exported global variable from an instance. pub fn get_named( &mut self, instance_name: &str, field: &str, ) -> Result { let instance = self .get_instance(&instance_name) .map_err(ContextError::Instance)? .clone(); self.get(&instance, field).map_err(ContextError::Action) } /// Get the value of an exported global variable from an instance. pub fn get( &mut self, instance: &InstanceHandle, field: &str, ) -> Result { get(instance, field).map(|value| ActionOutcome::Returned { values: vec![value], }) } /// Get a slice of memory from an instance. pub fn inspect_memory<'instance>( &self, instance: &'instance InstanceHandle, field_name: &str, start: usize, len: usize, ) -> Result<&'instance [u8], ActionError> { inspect_memory(instance, field_name, start, len) } /// Return a handle to the global_exports mapping, needed by some modules /// for instantiation. pub fn get_global_exports( &mut self, ) -> Rc>>> { Rc::clone(&mut self.global_exports) } }