diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 47ed4592a..0aceb914d 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -5,6 +5,7 @@ pub type CompileResult = std::result::Result>; pub type LinkResult = std::result::Result>; pub type RuntimeResult = std::result::Result>; pub type CallResult = std::result::Result>; +pub type ResolveResult = std::result::Result>; /// This is returned when the chosen compiler is unable to /// successfully compile the provided webassembly module into @@ -93,6 +94,23 @@ impl PartialEq for RuntimeError { } } +/// This error type is produced by resolving a wasm function +/// given its name. +/// +/// Comparing two `ResolveError`s always evaluates to false. +#[derive(Debug, Clone)] +pub enum ResolveError { + Signature { expected: FuncSig, found: Vec }, + NoSuchExport { name: String }, + ExportWrongType { name: String }, +} + +impl PartialEq for ResolveError { + fn eq(&self, _other: &ResolveError) -> bool { + false + } +} + /// This error type is produced by calling a wasm function /// exported from a module. /// @@ -102,9 +120,7 @@ impl PartialEq for RuntimeError { /// Comparing two `CallError`s always evaluates to false. #[derive(Debug, Clone)] pub enum CallError { - Signature { expected: FuncSig, found: Vec }, - NoSuchExport { name: String }, - ExportNotFunc { name: String }, + Resolve(ResolveError), Runtime(RuntimeError), } @@ -162,3 +178,33 @@ impl From> for Box { Box::new(CallError::Runtime(*runtime_err)) } } + +impl From for Box { + fn from(compile_err: CompileError) -> Self { + Box::new(Error::CompileError(compile_err)) + } +} + +impl From for Box { + fn from(runtime_err: RuntimeError) -> Self { + Box::new(Error::RuntimeError(runtime_err)) + } +} + +impl From for Box { + fn from(call_err: CallError) -> Self { + Box::new(Error::CallError(call_err)) + } +} + +impl From for Box { + fn from(runtime_err: RuntimeError) -> Self { + Box::new(CallError::Runtime(runtime_err)) + } +} + +impl From for Box { + fn from(resolve_err: ResolveError) -> Self { + Box::new(CallError::Resolve(resolve_err)) + } +} diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 7717ff39b..6af540f7d 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,7 +1,7 @@ use crate::{ backend::Token, backing::{ImportBacking, LocalBacking}, - error::{CallError, CallResult, Result}, + error::{CallError, CallResult, ResolveError, ResolveResult, Result}, export::{ Context, Export, ExportIter, FuncPointer, GlobalPointer, MemoryPointer, TablePointer, }, @@ -13,8 +13,7 @@ use crate::{ }, vm, }; -use std::mem; -use std::rc::Rc; +use std::{mem, rc::Rc}; pub(crate) struct InstanceInner { #[allow(dead_code)] @@ -72,6 +71,50 @@ impl Instance { Ok(instance) } + /// This returns the representation of a function that can be called + /// safely. + /// + /// # Usage: + /// ``` + /// # use wasmer_runtime_core::Instance; + /// # fn call_foo(instance: &mut Instance) -> Option<()> { + /// instance + /// .func("foo")? + /// .call(&[])?; + /// # Some(()) + /// # } + /// ``` + pub fn func(&mut self, name: &str) -> ResolveResult { + let export_index = + self.module + .exports + .get(name) + .ok_or_else(|| ResolveError::NoSuchExport { + name: name.to_string(), + })?; + + if let ExportIndex::Func(func_index) = export_index { + let sig_index = *self + .module + .func_assoc + .get(*func_index) + .expect("broken invariant, incorrect func index"); + let signature = self.module.sig_registry.lookup_func_sig(sig_index); + + Ok(Function { + signature, + module: &self.module, + instance_inner: &mut self.inner, + func_index: *func_index, + }) + } else { + Err(ResolveError::ExportWrongType { + name: name.to_string(), + } + .into()) + } + } + /// Call an exported webassembly function given the export name. /// Pass arguments by wrapping each one in the [`Value`] enum. /// The returned values are also each wrapped in a [`Value`]. @@ -99,16 +142,16 @@ impl Instance { self.module .exports .get(name) - .ok_or_else(|| CallError::NoSuchExport { + .ok_or_else(|| ResolveError::NoSuchExport { name: name.to_string(), })?; let func_index = if let ExportIndex::Func(func_index) = export_index { *func_index } else { - return Err(CallError::ExportNotFunc { + return Err(CallError::Resolve(ResolveError::ExportWrongType { name: name.to_string(), - } + }) .into()); }; @@ -134,7 +177,7 @@ impl Instance { let signature = self.module.sig_registry.lookup_func_sig(sig_index); if !signature.check_sig(args) { - Err(CallError::Signature { + Err(ResolveError::Signature { expected: signature.clone(), found: args.iter().map(|val| val.ty()).collect(), })? @@ -351,6 +394,84 @@ impl LikeNamespace for Instance { } } +/// A representation of an exported WebAssembly function. +pub struct Function<'a> { + signature: &'a FuncSig, + module: &'a ModuleInner, + instance_inner: &'a mut InstanceInner, + func_index: FuncIndex, +} + +impl<'a> Function<'a> { + /// Call an exported webassembly function safely. + /// + /// Pass arguments by wrapping each one in the [`Value`] enum. + /// The returned values are also each wrapped in a [`Value`]. + /// + /// [`Value`]: enum.Value.html + /// + /// # Note: + /// This returns `CallResult>` in order to support + /// the future multi-value returns webassembly feature. + /// + /// # Usage: + /// ``` + /// # use wasmer_runtime_core::Instance; + /// # fn call_foo(instance: &mut Instance) -> Option<()> { + /// instance + /// .func("foo")? + /// .call(&[])?; + /// # Some(()) + /// # } + /// ``` + pub fn call(&mut self, params: &[Value]) -> CallResult> { + if !self.signature.check_sig(params) { + Err(ResolveError::Signature { + expected: self.signature.clone(), + found: params.iter().map(|val| val.ty()).collect(), + })? + } + + let vmctx = match self.func_index.local_or_import(self.module) { + LocalOrImport::Local(_) => &mut *self.instance_inner.vmctx, + LocalOrImport::Import(imported_func_index) => { + self.instance_inner.import_backing.functions[imported_func_index].vmctx + } + }; + + let token = Token::generate(); + + let returns = self.module.protected_caller.call( + &self.module, + self.func_index, + params, + &self.instance_inner.import_backing, + vmctx, + token, + )?; + + Ok(returns) + } + + pub fn signature(&self) -> &FuncSig { + self.signature + } + + pub fn raw(&self) -> *const vm::Func { + match self.func_index.local_or_import(self.module) { + LocalOrImport::Local(local_func_index) => self + .module + .func_resolver + .get(self.module, local_func_index) + .unwrap() + .as_ptr(), + LocalOrImport::Import(import_func_index) => { + self.instance_inner.import_backing.functions[import_func_index].func + } + } + } +} + #[doc(hidden)] impl Instance { pub fn memory_offset_addr(&self, _: u32, _: usize) -> *const u8 { diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 92401dfc4..5ba999c6a 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -170,10 +170,11 @@ impl Ctx { } } +enum InnerFunc {} /// Used to provide type safety (ish) for passing around function pointers. /// The typesystem ensures this cannot be dereferenced since an /// empty enum cannot actually exist. -pub enum Func {} +pub struct Func(InnerFunc); /// An imported function, which contains the vmctx that owns this function. #[derive(Debug, Clone)] diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 55865b698..fd40a8b4a 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -1,5 +1,5 @@ pub use wasmer_runtime_core::import::ImportObject; -pub use wasmer_runtime_core::instance::Instance; +pub use wasmer_runtime_core::instance::{Function, Instance}; pub use wasmer_runtime_core::module::Module; pub use wasmer_runtime_core::types::Value; pub use wasmer_runtime_core::vm::Ctx; @@ -9,8 +9,9 @@ pub use wasmer_runtime_core::{compile_with, validate}; pub use wasmer_runtime_core::error; pub use wasmer_runtime_core::imports; -pub mod value { - pub use wasmer_runtime_core::types::{Type, Value}; +pub mod wasm { + pub use wasmer_runtime_core::instance::Function; + pub use wasmer_runtime_core::types::{FuncSig, Type, Value}; } /// Compile WebAssembly binary code into a [`Module`].