mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-13 22:25:40 +00:00
836 lines
26 KiB
Rust
836 lines
26 KiB
Rust
//! The instance module contains the implementation data structures and helper functions used to
|
|
//! manipulate and access wasm instances.
|
|
use crate::{
|
|
backend::RunnableModule,
|
|
backing::{ImportBacking, LocalBacking},
|
|
error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError},
|
|
export::{Context, Export, ExportIter, FuncPointer},
|
|
global::Global,
|
|
import::{ImportObject, LikeNamespace},
|
|
loader::Loader,
|
|
memory::Memory,
|
|
module::{ExportIndex, Module, ModuleInfo, ModuleInner},
|
|
sig_registry::SigRegistry,
|
|
structures::TypedIndex,
|
|
table::Table,
|
|
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
|
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
|
vm::{self, InternalField},
|
|
};
|
|
use smallvec::{smallvec, SmallVec};
|
|
use std::{
|
|
mem,
|
|
pin::Pin,
|
|
ptr::NonNull,
|
|
sync::{Arc, Mutex},
|
|
};
|
|
|
|
pub(crate) struct InstanceInner {
|
|
#[allow(dead_code)]
|
|
pub(crate) backing: LocalBacking,
|
|
import_backing: ImportBacking,
|
|
pub(crate) vmctx: *mut vm::Ctx,
|
|
}
|
|
|
|
// manually implemented because InstanceInner contains a raw pointer to Ctx
|
|
unsafe impl Send for InstanceInner {}
|
|
|
|
impl Drop for InstanceInner {
|
|
fn drop(&mut self) {
|
|
// Drop the vmctx.
|
|
unsafe { Box::from_raw(self.vmctx) };
|
|
}
|
|
}
|
|
|
|
/// An instantiated WebAssembly module.
|
|
///
|
|
/// An `Instance` represents a WebAssembly module that
|
|
/// has been instantiated with an [`ImportObject`] and is
|
|
/// ready to be called.
|
|
///
|
|
/// [`ImportObject`]: struct.ImportObject.html
|
|
pub struct Instance {
|
|
/// Reference to the module used to instantiate this instance.
|
|
pub module: Arc<ModuleInner>,
|
|
inner: Pin<Box<InstanceInner>>,
|
|
#[allow(dead_code)]
|
|
import_object: ImportObject,
|
|
}
|
|
|
|
impl Instance {
|
|
pub(crate) fn new(module: Arc<ModuleInner>, imports: &ImportObject) -> Result<Instance> {
|
|
// We need the backing and import_backing to create a vm::Ctx, but we need
|
|
// a vm::Ctx to create a backing and an import_backing. The solution is to create an
|
|
// uninitialized vm::Ctx and then initialize it in-place.
|
|
let mut vmctx: Box<mem::MaybeUninit<vm::Ctx>> =
|
|
Box::new(mem::MaybeUninit::<vm::Ctx>::zeroed());
|
|
|
|
let import_backing = ImportBacking::new(&module, &imports, vmctx.as_mut_ptr())?;
|
|
let backing = LocalBacking::new(&module, &import_backing, vmctx.as_mut_ptr())?;
|
|
|
|
let mut inner = Box::pin(InstanceInner {
|
|
backing,
|
|
import_backing,
|
|
vmctx: vmctx.as_mut_ptr(),
|
|
});
|
|
|
|
// Initialize the vm::Ctx in-place after the backing
|
|
// has been boxed.
|
|
unsafe {
|
|
let backing = &mut *(&mut inner.backing as *mut _);
|
|
let import_backing = &mut *(&mut inner.import_backing as *mut _);
|
|
let real_ctx = match imports.call_state_creator() {
|
|
Some((data, dtor)) => {
|
|
vm::Ctx::new_with_data(backing, import_backing, &module, data, dtor)
|
|
}
|
|
None => vm::Ctx::new(backing, import_backing, &module),
|
|
};
|
|
vmctx.as_mut_ptr().write(real_ctx);
|
|
};
|
|
Box::leak(vmctx);
|
|
|
|
let instance = Instance {
|
|
module,
|
|
inner,
|
|
import_object: imports.clone_ref(),
|
|
};
|
|
|
|
if let Some(start_index) = instance.module.info.start_func {
|
|
// We know that the start function takes no arguments and returns no values.
|
|
// Therefore, we can call it without doing any signature checking, etc.
|
|
|
|
let func_ptr = match start_index.local_or_import(&instance.module.info) {
|
|
LocalOrImport::Local(local_func_index) => instance
|
|
.module
|
|
.runnable_module
|
|
.get_func(&instance.module.info, local_func_index)
|
|
.unwrap(),
|
|
LocalOrImport::Import(import_func_index) => NonNull::new(
|
|
instance.inner.import_backing.vm_functions[import_func_index].func as *mut _,
|
|
)
|
|
.unwrap(),
|
|
};
|
|
|
|
let ctx_ptr = match start_index.local_or_import(&instance.module.info) {
|
|
LocalOrImport::Local(_) => instance.inner.vmctx,
|
|
LocalOrImport::Import(imported_func_index) => unsafe {
|
|
instance.inner.import_backing.vm_functions[imported_func_index]
|
|
.func_ctx
|
|
.as_ref()
|
|
}
|
|
.vmctx
|
|
.as_ptr(),
|
|
};
|
|
|
|
let sig_index = *instance
|
|
.module
|
|
.info
|
|
.func_assoc
|
|
.get(start_index)
|
|
.expect("broken invariant, incorrect func index");
|
|
|
|
let wasm_trampoline = instance
|
|
.module
|
|
.runnable_module
|
|
.get_trampoline(&instance.module.info, sig_index)
|
|
.expect("wasm trampoline");
|
|
|
|
let start_func: Func<(), (), Wasm> =
|
|
unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, None, ctx_ptr) };
|
|
|
|
start_func.call()?;
|
|
}
|
|
|
|
Ok(instance)
|
|
}
|
|
|
|
/// Load an `Instance` using the given loader.
|
|
pub fn load<T: Loader>(&self, loader: T) -> ::std::result::Result<T::Instance, T::Error> {
|
|
loader.load(&*self.module.runnable_module, &self.module.info, unsafe {
|
|
&*self.inner.vmctx
|
|
})
|
|
}
|
|
|
|
/// Through generic magic and the awe-inspiring power of traits, we bring you...
|
|
///
|
|
/// # "Func"
|
|
///
|
|
/// A [`Func`] allows you to call functions exported from wasm with
|
|
/// near zero overhead.
|
|
///
|
|
/// [`Func`]: struct.Func.html
|
|
/// # Usage:
|
|
///
|
|
/// ```
|
|
/// # use wasmer_runtime_core::{Func, Instance, error::ResolveResult};
|
|
/// # fn typed_func(instance: Instance) -> ResolveResult<()> {
|
|
/// let func: Func<(i32, i32)> = instance.func("foo")?;
|
|
///
|
|
/// func.call(42, 43);
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
pub fn func<Args, Rets>(&self, name: &str) -> ResolveResult<Func<Args, Rets, Wasm>>
|
|
where
|
|
Args: WasmTypeList,
|
|
Rets: WasmTypeList,
|
|
{
|
|
let export_index =
|
|
self.module
|
|
.info
|
|
.exports
|
|
.get(name)
|
|
.ok_or_else(|| ResolveError::ExportNotFound {
|
|
name: name.to_string(),
|
|
})?;
|
|
|
|
if let ExportIndex::Func(func_index) = export_index {
|
|
let sig_index = *self
|
|
.module
|
|
.info
|
|
.func_assoc
|
|
.get(*func_index)
|
|
.expect("broken invariant, incorrect func index");
|
|
let signature =
|
|
SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]);
|
|
|
|
if signature.params() != Args::types() || signature.returns() != Rets::types() {
|
|
Err(ResolveError::Signature {
|
|
expected: (*signature).clone(),
|
|
found: Args::types().to_vec(),
|
|
})?;
|
|
}
|
|
|
|
let ctx = match func_index.local_or_import(&self.module.info) {
|
|
LocalOrImport::Local(_) => self.inner.vmctx,
|
|
LocalOrImport::Import(imported_func_index) => unsafe {
|
|
self.inner.import_backing.vm_functions[imported_func_index]
|
|
.func_ctx
|
|
.as_ref()
|
|
}
|
|
.vmctx
|
|
.as_ptr(),
|
|
};
|
|
|
|
let func_wasm_inner = self
|
|
.module
|
|
.runnable_module
|
|
.get_trampoline(&self.module.info, sig_index)
|
|
.unwrap();
|
|
|
|
let (func_ptr, func_env) = match func_index.local_or_import(&self.module.info) {
|
|
LocalOrImport::Local(local_func_index) => (
|
|
self.module
|
|
.runnable_module
|
|
.get_func(&self.module.info, local_func_index)
|
|
.unwrap(),
|
|
None,
|
|
),
|
|
LocalOrImport::Import(import_func_index) => {
|
|
let imported_func = &self.inner.import_backing.vm_functions[import_func_index];
|
|
|
|
(
|
|
NonNull::new(imported_func.func as *mut _).unwrap(),
|
|
unsafe { imported_func.func_ctx.as_ref() }.func_env,
|
|
)
|
|
}
|
|
};
|
|
|
|
let typed_func: Func<Args, Rets, Wasm> =
|
|
unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, func_env, ctx) };
|
|
|
|
Ok(typed_func)
|
|
} else {
|
|
Err(ResolveError::ExportWrongType {
|
|
name: name.to_string(),
|
|
}
|
|
.into())
|
|
}
|
|
}
|
|
|
|
/// Resolve a function by name.
|
|
pub fn resolve_func(&self, name: &str) -> ResolveResult<usize> {
|
|
let export_index =
|
|
self.module
|
|
.info
|
|
.exports
|
|
.get(name)
|
|
.ok_or_else(|| ResolveError::ExportNotFound {
|
|
name: name.to_string(),
|
|
})?;
|
|
|
|
if let ExportIndex::Func(func_index) = export_index {
|
|
Ok(func_index.index())
|
|
} else {
|
|
Err(ResolveError::ExportWrongType {
|
|
name: name.to_string(),
|
|
}
|
|
.into())
|
|
}
|
|
}
|
|
|
|
/// This returns the representation of a function that can be called
|
|
/// safely.
|
|
///
|
|
/// # Usage:
|
|
/// ```
|
|
/// # use wasmer_runtime_core::Instance;
|
|
/// # use wasmer_runtime_core::error::CallResult;
|
|
/// # fn call_foo(instance: &mut Instance) -> CallResult<()> {
|
|
/// instance
|
|
/// .dyn_func("foo")?
|
|
/// .call(&[])?;
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
pub fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc> {
|
|
let export_index =
|
|
self.module
|
|
.info
|
|
.exports
|
|
.get(name)
|
|
.ok_or_else(|| ResolveError::ExportNotFound {
|
|
name: name.to_string(),
|
|
})?;
|
|
|
|
if let ExportIndex::Func(func_index) = export_index {
|
|
let sig_index = *self
|
|
.module
|
|
.info
|
|
.func_assoc
|
|
.get(*func_index)
|
|
.expect("broken invariant, incorrect func index");
|
|
let signature =
|
|
SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]);
|
|
|
|
Ok(DynFunc {
|
|
signature,
|
|
module: &self.module,
|
|
instance_inner: &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`].
|
|
///
|
|
/// [`Value`]: enum.Value.html
|
|
///
|
|
/// # Note:
|
|
/// This returns `CallResult<Vec<Value>>` in order to support
|
|
/// the future multi-value returns WebAssembly feature.
|
|
///
|
|
/// # Usage:
|
|
/// ```
|
|
/// # use wasmer_runtime_core::types::Value;
|
|
/// # use wasmer_runtime_core::error::Result;
|
|
/// # use wasmer_runtime_core::Instance;
|
|
/// # fn call_foo(instance: &mut Instance) -> Result<()> {
|
|
/// // ...
|
|
/// let results = instance.call("foo", &[Value::I32(42)])?;
|
|
/// // ...
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
pub fn call(&self, name: &str, params: &[Value]) -> CallResult<Vec<Value>> {
|
|
let export_index =
|
|
self.module
|
|
.info
|
|
.exports
|
|
.get(name)
|
|
.ok_or_else(|| ResolveError::ExportNotFound {
|
|
name: name.to_string(),
|
|
})?;
|
|
|
|
let func_index = if let ExportIndex::Func(func_index) = export_index {
|
|
*func_index
|
|
} else {
|
|
return Err(CallError::Resolve(ResolveError::ExportWrongType {
|
|
name: name.to_string(),
|
|
})
|
|
.into());
|
|
};
|
|
|
|
let mut results = Vec::new();
|
|
|
|
call_func_with_index(
|
|
&self.module.info,
|
|
&*self.module.runnable_module,
|
|
&self.inner.import_backing,
|
|
self.inner.vmctx,
|
|
func_index,
|
|
params,
|
|
&mut results,
|
|
)?;
|
|
|
|
Ok(results)
|
|
}
|
|
|
|
/// Returns an immutable reference to the
|
|
/// [`Ctx`] used by this Instance.
|
|
///
|
|
/// [`Ctx`]: struct.Ctx.html
|
|
pub fn context(&self) -> &vm::Ctx {
|
|
unsafe { &*self.inner.vmctx }
|
|
}
|
|
|
|
/// Returns a mutable reference to the
|
|
/// [`Ctx`] used by this Instance.
|
|
///
|
|
/// [`Ctx`]: struct.Ctx.html
|
|
pub fn context_mut(&mut self) -> &mut vm::Ctx {
|
|
unsafe { &mut *self.inner.vmctx }
|
|
}
|
|
|
|
/// Returns an iterator over all of the items
|
|
/// exported from this instance.
|
|
pub fn exports(&self) -> ExportIter {
|
|
ExportIter::new(&self.module, &self.inner)
|
|
}
|
|
|
|
/// The module used to instantiate this Instance.
|
|
pub fn module(&self) -> Module {
|
|
Module::new(Arc::clone(&self.module))
|
|
}
|
|
|
|
/// Get the value of an internal field
|
|
pub fn get_internal(&self, field: &InternalField) -> u64 {
|
|
self.inner.backing.internals.0[field.index()]
|
|
}
|
|
|
|
/// Set the value of an internal field.
|
|
pub fn set_internal(&mut self, field: &InternalField, value: u64) {
|
|
self.inner.backing.internals.0[field.index()] = value;
|
|
}
|
|
}
|
|
|
|
impl InstanceInner {
|
|
pub(crate) fn get_export_from_index(
|
|
&self,
|
|
module: &ModuleInner,
|
|
export_index: &ExportIndex,
|
|
) -> Export {
|
|
match export_index {
|
|
ExportIndex::Func(func_index) => {
|
|
let (func, ctx, signature) = self.get_func_from_index(module, *func_index);
|
|
|
|
Export::Function {
|
|
func,
|
|
ctx: match ctx {
|
|
Context::Internal => Context::External(self.vmctx),
|
|
ctx @ Context::External(_) => ctx,
|
|
ctx @ Context::ExternalWithEnv(_, _) => ctx,
|
|
},
|
|
signature,
|
|
}
|
|
}
|
|
ExportIndex::Memory(memory_index) => {
|
|
let memory = self.get_memory_from_index(module, *memory_index);
|
|
Export::Memory(memory)
|
|
}
|
|
ExportIndex::Global(global_index) => {
|
|
let global = self.get_global_from_index(module, *global_index);
|
|
Export::Global(global)
|
|
}
|
|
ExportIndex::Table(table_index) => {
|
|
let table = self.get_table_from_index(module, *table_index);
|
|
Export::Table(table)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_func_from_index(
|
|
&self,
|
|
module: &ModuleInner,
|
|
func_index: FuncIndex,
|
|
) -> (FuncPointer, Context, Arc<FuncSig>) {
|
|
let sig_index = *module
|
|
.info
|
|
.func_assoc
|
|
.get(func_index)
|
|
.expect("broken invariant, incorrect func index");
|
|
|
|
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
|
|
LocalOrImport::Local(local_func_index) => (
|
|
module
|
|
.runnable_module
|
|
.get_func(&module.info, local_func_index)
|
|
.expect("broken invariant, func resolver not synced with module.exports")
|
|
.cast()
|
|
.as_ptr() as *const _,
|
|
Context::Internal,
|
|
),
|
|
LocalOrImport::Import(imported_func_index) => {
|
|
let imported_func = &self.import_backing.vm_functions[imported_func_index];
|
|
let func_ctx = unsafe { imported_func.func_ctx.as_ref() };
|
|
|
|
(
|
|
imported_func.func as *const _,
|
|
Context::ExternalWithEnv(func_ctx.vmctx.as_ptr(), func_ctx.func_env),
|
|
)
|
|
}
|
|
};
|
|
|
|
let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]);
|
|
|
|
(unsafe { FuncPointer::new(func_ptr) }, ctx, signature)
|
|
}
|
|
|
|
fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory {
|
|
match mem_index.local_or_import(&module.info) {
|
|
LocalOrImport::Local(local_mem_index) => self.backing.memories[local_mem_index].clone(),
|
|
LocalOrImport::Import(imported_mem_index) => {
|
|
self.import_backing.memories[imported_mem_index].clone()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_global_from_index(&self, module: &ModuleInner, global_index: GlobalIndex) -> Global {
|
|
match global_index.local_or_import(&module.info) {
|
|
LocalOrImport::Local(local_global_index) => {
|
|
self.backing.globals[local_global_index].clone()
|
|
}
|
|
LocalOrImport::Import(import_global_index) => {
|
|
self.import_backing.globals[import_global_index].clone()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_table_from_index(&self, module: &ModuleInner, table_index: TableIndex) -> Table {
|
|
match table_index.local_or_import(&module.info) {
|
|
LocalOrImport::Local(local_table_index) => {
|
|
self.backing.tables[local_table_index].clone()
|
|
}
|
|
LocalOrImport::Import(imported_table_index) => {
|
|
self.import_backing.tables[imported_table_index].clone()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LikeNamespace for Instance {
|
|
fn get_export(&self, name: &str) -> Option<Export> {
|
|
let export_index = self.module.info.exports.get(name)?;
|
|
|
|
Some(self.inner.get_export_from_index(&self.module, export_index))
|
|
}
|
|
|
|
fn get_exports(&self) -> Vec<(String, Export)> {
|
|
unimplemented!("Use the exports method instead");
|
|
}
|
|
|
|
fn maybe_insert(&mut self, _name: &str, _export: Export) -> Option<()> {
|
|
None
|
|
}
|
|
}
|
|
|
|
use std::rc::Rc;
|
|
impl LikeNamespace for Rc<Instance> {
|
|
fn get_export(&self, name: &str) -> Option<Export> {
|
|
let export_index = self.module.info.exports.get(name)?;
|
|
|
|
Some(self.inner.get_export_from_index(&self.module, export_index))
|
|
}
|
|
|
|
fn get_exports(&self) -> Vec<(String, Export)> {
|
|
unimplemented!("Use the exports method instead");
|
|
}
|
|
|
|
fn maybe_insert(&mut self, _name: &str, _export: Export) -> Option<()> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl LikeNamespace for Arc<Mutex<Instance>> {
|
|
fn get_export(&self, name: &str) -> Option<Export> {
|
|
let instance = self.lock().unwrap();
|
|
let export_index = instance.module.info.exports.get(name)?;
|
|
|
|
Some(
|
|
instance
|
|
.inner
|
|
.get_export_from_index(&instance.module, export_index),
|
|
)
|
|
}
|
|
|
|
fn get_exports(&self) -> Vec<(String, Export)> {
|
|
unimplemented!("Use the exports method instead");
|
|
}
|
|
|
|
fn maybe_insert(&mut self, _name: &str, _export: Export) -> Option<()> {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn call_func_with_index(
|
|
info: &ModuleInfo,
|
|
runnable: &dyn RunnableModule,
|
|
import_backing: &ImportBacking,
|
|
local_ctx: *mut vm::Ctx,
|
|
func_index: FuncIndex,
|
|
args: &[Value],
|
|
rets: &mut Vec<Value>,
|
|
) -> CallResult<()> {
|
|
let sig_index = *info
|
|
.func_assoc
|
|
.get(func_index)
|
|
.expect("broken invariant, incorrect func index");
|
|
|
|
let signature = &info.signatures[sig_index];
|
|
|
|
let func_ptr = match func_index.local_or_import(info) {
|
|
LocalOrImport::Local(local_func_index) => {
|
|
runnable.get_func(info, local_func_index).unwrap()
|
|
}
|
|
LocalOrImport::Import(import_func_index) => {
|
|
NonNull::new(import_backing.vm_functions[import_func_index].func as *mut _).unwrap()
|
|
}
|
|
};
|
|
|
|
let ctx_ptr = match func_index.local_or_import(info) {
|
|
LocalOrImport::Local(_) => local_ctx,
|
|
LocalOrImport::Import(imported_func_index) => unsafe {
|
|
import_backing.vm_functions[imported_func_index]
|
|
.func_ctx
|
|
.as_ref()
|
|
}
|
|
.vmctx
|
|
.as_ptr(),
|
|
};
|
|
|
|
let wasm = runnable
|
|
.get_trampoline(info, sig_index)
|
|
.expect("wasm trampoline");
|
|
|
|
call_func_with_index_inner(ctx_ptr, func_ptr, signature, wasm, args, rets)
|
|
}
|
|
|
|
pub(crate) fn call_func_with_index_inner(
|
|
ctx_ptr: *mut vm::Ctx,
|
|
func_ptr: NonNull<vm::Func>,
|
|
signature: &FuncSig,
|
|
wasm: Wasm,
|
|
args: &[Value],
|
|
rets: &mut Vec<Value>,
|
|
) -> CallResult<()> {
|
|
rets.clear();
|
|
|
|
let num_results = signature.returns().len();
|
|
let num_results = num_results
|
|
+ signature
|
|
.returns()
|
|
.iter()
|
|
.filter(|&&ty| ty == Type::V128)
|
|
.count();
|
|
rets.reserve(num_results);
|
|
|
|
if !signature.check_param_value_types(args) {
|
|
Err(ResolveError::Signature {
|
|
expected: signature.clone(),
|
|
found: args.iter().map(|val| val.ty()).collect(),
|
|
})?
|
|
}
|
|
|
|
let mut raw_args: SmallVec<[u64; 8]> = SmallVec::new();
|
|
for v in args {
|
|
match v {
|
|
Value::I32(i) => {
|
|
raw_args.push(*i as u64);
|
|
}
|
|
Value::I64(i) => {
|
|
raw_args.push(*i as u64);
|
|
}
|
|
Value::F32(f) => {
|
|
raw_args.push(f.to_bits() as u64);
|
|
}
|
|
Value::F64(f) => {
|
|
raw_args.push(f.to_bits() as u64);
|
|
}
|
|
Value::V128(v) => {
|
|
let bytes = v.to_le_bytes();
|
|
let mut lo = [0u8; 8];
|
|
lo.clone_from_slice(&bytes[0..8]);
|
|
raw_args.push(u64::from_le_bytes(lo));
|
|
let mut hi = [0u8; 8];
|
|
hi.clone_from_slice(&bytes[8..16]);
|
|
raw_args.push(u64::from_le_bytes(hi));
|
|
}
|
|
}
|
|
}
|
|
|
|
let Wasm {
|
|
trampoline,
|
|
invoke,
|
|
invoke_env,
|
|
} = wasm;
|
|
|
|
let run_wasm = |result_space: *mut u64| unsafe {
|
|
let mut trap_info = WasmTrapInfo::Unknown;
|
|
let mut user_error = None;
|
|
|
|
let success = invoke(
|
|
trampoline,
|
|
ctx_ptr,
|
|
func_ptr,
|
|
raw_args.as_ptr(),
|
|
result_space,
|
|
&mut trap_info,
|
|
&mut user_error,
|
|
invoke_env,
|
|
);
|
|
|
|
if success {
|
|
Ok(())
|
|
} else {
|
|
if let Some(data) = user_error {
|
|
Err(RuntimeError::Error { data })
|
|
} else {
|
|
Err(RuntimeError::Trap {
|
|
msg: trap_info.to_string().into(),
|
|
})
|
|
}
|
|
}
|
|
};
|
|
|
|
let raw_to_value = |raw, ty| match ty {
|
|
Type::I32 => Value::I32(raw as i32),
|
|
Type::I64 => Value::I64(raw as i64),
|
|
Type::F32 => Value::F32(f32::from_bits(raw as u32)),
|
|
Type::F64 => Value::F64(f64::from_bits(raw)),
|
|
Type::V128 => unreachable!("V128 does not map to any single value"),
|
|
};
|
|
|
|
match signature.returns() {
|
|
&[] => {
|
|
run_wasm(0 as *mut u64)?;
|
|
Ok(())
|
|
}
|
|
&[Type::V128] => {
|
|
let mut result = [0u64; 2];
|
|
|
|
run_wasm(result.as_mut_ptr())?;
|
|
|
|
let mut bytes = [0u8; 16];
|
|
let lo = result[0].to_le_bytes();
|
|
let hi = result[1].to_le_bytes();
|
|
for i in 0..8 {
|
|
bytes[i] = lo[i];
|
|
bytes[i + 8] = hi[i];
|
|
}
|
|
rets.push(Value::V128(u128::from_le_bytes(bytes)));
|
|
Ok(())
|
|
}
|
|
&[ty] => {
|
|
let mut result = 0u64;
|
|
|
|
run_wasm(&mut result)?;
|
|
|
|
rets.push(raw_to_value(result, ty));
|
|
|
|
Ok(())
|
|
}
|
|
result_tys @ _ => {
|
|
let mut results: SmallVec<[u64; 8]> = smallvec![0; num_results];
|
|
|
|
run_wasm(results.as_mut_ptr())?;
|
|
|
|
rets.extend(
|
|
results
|
|
.iter()
|
|
.zip(result_tys.iter())
|
|
.map(|(&raw, &ty)| raw_to_value(raw, ty)),
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A representation of an exported WebAssembly function.
|
|
pub struct DynFunc<'a> {
|
|
pub(crate) signature: Arc<FuncSig>,
|
|
module: &'a ModuleInner,
|
|
pub(crate) instance_inner: &'a InstanceInner,
|
|
func_index: FuncIndex,
|
|
}
|
|
|
|
impl<'a> DynFunc<'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<Vec<Value>>` in order to support
|
|
/// the future multi-value returns WebAssembly feature.
|
|
///
|
|
/// # Usage:
|
|
/// ```
|
|
/// # use wasmer_runtime_core::Instance;
|
|
/// # use wasmer_runtime_core::error::CallResult;
|
|
/// # fn call_foo(instance: &mut Instance) -> CallResult<()> {
|
|
/// instance
|
|
/// .dyn_func("foo")?
|
|
/// .call(&[])?;
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
pub fn call(&self, params: &[Value]) -> CallResult<Vec<Value>> {
|
|
let mut results = Vec::new();
|
|
|
|
call_func_with_index(
|
|
&self.module.info,
|
|
&*self.module.runnable_module,
|
|
&self.instance_inner.import_backing,
|
|
self.instance_inner.vmctx,
|
|
self.func_index,
|
|
params,
|
|
&mut results,
|
|
)?;
|
|
|
|
Ok(results)
|
|
}
|
|
|
|
/// Gets the signature of this `Dynfunc`.
|
|
pub fn signature(&self) -> &FuncSig {
|
|
&*self.signature
|
|
}
|
|
|
|
/// Gets a const pointer to the function represent by this `DynFunc`.
|
|
pub fn raw(&self) -> *const vm::Func {
|
|
match self.func_index.local_or_import(&self.module.info) {
|
|
LocalOrImport::Local(local_func_index) => self
|
|
.module
|
|
.runnable_module
|
|
.get_func(&self.module.info, local_func_index)
|
|
.unwrap()
|
|
.as_ptr(),
|
|
LocalOrImport::Import(import_func_index) => {
|
|
self.instance_inner.import_backing.vm_functions[import_func_index].func
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
fn is_send<T: Send>() {}
|
|
|
|
#[test]
|
|
fn test_instance_is_send() {
|
|
is_send::<Instance>();
|
|
}
|
|
}
|