wasmer/lib/runtime/src/instance.rs

286 lines
9.3 KiB
Rust
Raw Normal View History

2019-01-08 17:09:47 +00:00
use crate::recovery::call_protected;
use crate::{
backing::{ImportBacking, LocalBacking},
2019-01-11 03:59:57 +00:00
export::{Context, Export},
2019-01-12 21:24:17 +00:00
import::{ImportResolver, Namespace},
2019-01-11 03:59:57 +00:00
module::{ExportIndex, Module},
2019-01-12 20:34:23 +00:00
types::{FuncIndex, FuncSig, MapIndex, Memory, MemoryIndex, Type, Value},
2019-01-08 17:09:47 +00:00
vm,
};
2019-01-11 03:59:57 +00:00
use hashbrown::hash_map;
2019-01-08 17:09:47 +00:00
use libffi::high::{arg as libffi_arg, call as libffi_call, CodePtr};
2019-01-11 03:59:57 +00:00
use std::rc::Rc;
use std::{iter, mem};
2019-01-08 17:09:47 +00:00
pub struct Instance {
2019-01-11 03:59:57 +00:00
pub module: Module,
#[allow(dead_code)]
2019-01-08 17:09:47 +00:00
pub(crate) backing: LocalBacking,
2019-01-11 03:59:57 +00:00
#[allow(dead_code)]
imports: Rc<dyn ImportResolver>,
2019-01-08 17:09:47 +00:00
import_backing: ImportBacking,
2019-01-11 03:59:57 +00:00
vmctx: Box<vm::Ctx>,
2019-01-08 17:09:47 +00:00
}
impl Instance {
2019-01-09 02:57:28 +00:00
pub(crate) fn new(
module: Module,
2019-01-11 03:59:57 +00:00
imports: Rc<dyn ImportResolver>,
2019-01-09 02:57:28 +00:00
) -> Result<Box<Instance>, String> {
2019-01-11 03:59:57 +00:00
// 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 = unsafe { Box::new(mem::uninitialized()) };
2019-01-08 17:09:47 +00:00
2019-01-11 03:59:57 +00:00
let import_backing = ImportBacking::new(&module, &*imports, &mut *vmctx)?;
let backing = LocalBacking::new(&module, &import_backing, &mut *vmctx);
2019-01-08 17:09:47 +00:00
2019-01-11 03:59:57 +00:00
// When Pin is stablized, this will use `Box::pinned` instead of `Box::new`.
2019-01-08 17:09:47 +00:00
let mut instance = Box::new(Instance {
2019-01-11 03:59:57 +00:00
module,
2019-01-08 17:09:47 +00:00
backing,
2019-01-11 03:59:57 +00:00
imports,
2019-01-08 17:09:47 +00:00
import_backing,
2019-01-11 03:59:57 +00:00
vmctx,
2019-01-08 17:09:47 +00:00
});
2019-01-11 03:59:57 +00:00
// Initialize the vm::Ctx in-place after the import_backing
// has been boxed.
*instance.vmctx = vm::Ctx::new(&mut instance.backing, &mut instance.import_backing);
if let Some(start_index) = instance.module.start_func {
2019-01-08 17:09:47 +00:00
instance.call_with_index(start_index, &[])?;
}
Ok(instance)
}
/// Call an exported webassembly function given the export name.
/// Pass arguments by wrapping each one in the `Value` enum.
/// The returned value is also returned in a `Value`.
2019-01-08 17:09:47 +00:00
///
/// This will eventually return `Result<Option<Vec<Value>>, String>` in
2019-01-08 17:09:47 +00:00
/// order to support multi-value returns.
pub fn call(&mut self, name: &str, args: &[Value]) -> Result<Option<Value>, String> {
2019-01-11 03:59:57 +00:00
let export_index = self
2019-01-08 17:09:47 +00:00
.module
.exports
.get(name)
2019-01-11 03:59:57 +00:00
.ok_or_else(|| format!("there is no export with that name: {}", name))?;
let func_index = if let ExportIndex::Func(func_index) = export_index {
*func_index
} else {
return Err("that export is not a function".to_string());
};
2019-01-08 17:09:47 +00:00
self.call_with_index(func_index, args)
}
fn call_with_index(
&mut self,
func_index: FuncIndex,
args: &[Value],
) -> Result<Option<Value>, String> {
2019-01-11 03:59:57 +00:00
let (func_ref, ctx, signature) = self.get_func_from_index(func_index);
2019-01-08 17:09:47 +00:00
2019-01-11 03:59:57 +00:00
let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
let vmctx_ptr = match ctx {
Context::External(vmctx) => vmctx,
Context::Internal => &mut *self.vmctx,
};
assert!(
signature.returns.len() <= 1,
"multi-value returns not yet supported"
);
2019-01-08 17:09:47 +00:00
2019-01-11 03:59:57 +00:00
if !signature.check_sig(args) {
return Err("incorrect signature".to_string());
}
2019-01-08 17:09:47 +00:00
let libffi_args: Vec<_> = args
.iter()
.map(|val| match val {
Value::I32(ref x) => libffi_arg(x),
Value::I64(ref x) => libffi_arg(x),
Value::F32(ref x) => libffi_arg(x),
Value::F64(ref x) => libffi_arg(x),
})
.chain(iter::once(libffi_arg(&vmctx_ptr)))
.collect();
call_protected(|| {
2019-01-11 03:59:57 +00:00
signature
2019-01-08 17:09:47 +00:00
.returns
.first()
.map(|ty| match ty {
Type::I32 => Value::I32(unsafe { libffi_call(func_ptr, &libffi_args) }),
Type::I64 => Value::I64(unsafe { libffi_call(func_ptr, &libffi_args) }),
Type::F32 => Value::F32(unsafe { libffi_call(func_ptr, &libffi_args) }),
Type::F64 => Value::F64(unsafe { libffi_call(func_ptr, &libffi_args) }),
})
.or_else(|| {
// call with no returns
unsafe {
libffi_call::<()>(func_ptr, &libffi_args);
}
None
})
})
}
2019-01-11 03:59:57 +00:00
pub fn exports(&self) -> ExportIter {
ExportIter::new(self)
}
fn get_export_from_index(&self, export_index: &ExportIndex) -> Export {
match export_index {
ExportIndex::Func(func_index) => {
let (func, ctx, signature) = self.get_func_from_index(*func_index);
Export::Function {
func,
ctx: match ctx {
Context::Internal => {
Context::External(&*self.vmctx as *const vm::Ctx as *mut vm::Ctx)
}
ctx @ Context::External(_) => ctx,
},
signature,
}
}
2019-01-12 20:34:23 +00:00
ExportIndex::Memory(memory_index) => {
let (local, ctx, memory) = self.get_memory_from_index(*memory_index);
Export::Memory {
local,
ctx: match ctx {
Context::Internal => {
Context::External(&*self.vmctx as *const vm::Ctx as *mut vm::Ctx)
}
ctx @ Context::External(_) => ctx,
},
memory,
}
}
2019-01-11 03:59:57 +00:00
ExportIndex::Global(_global_index) => unimplemented!(),
ExportIndex::Table(_table_index) => unimplemented!(),
}
}
fn get_func_from_index(&self, func_index: FuncIndex) -> (FuncRef, Context, FuncSig) {
let sig_index = *self
.module
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = if self.module.is_imported_function(func_index) {
let imported_func = &self.import_backing.functions[func_index.index()];
(
imported_func.func as *const _,
Context::External(imported_func.vmctx),
)
} else {
(
self.module
.func_resolver
.get(&self.module, func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr() as *const _,
Context::Internal,
)
};
let signature = self.module.sig_registry.lookup_func_sig(sig_index).clone();
(FuncRef(func_ptr), ctx, signature)
}
2019-01-12 20:34:23 +00:00
fn get_memory_from_index(
&self,
mem_index: MemoryIndex,
) -> (*mut vm::LocalMemory, Context, Memory) {
if self.module.is_imported_memory(mem_index) {
let &(_, mem) = &self
.module
.imported_memories
.get(mem_index)
.expect("missing imported memory index");
let vm::ImportedMemory { memory, vmctx } =
&self.import_backing.memories[mem_index.index()];
(*memory, Context::External(*vmctx), *mem)
} else {
// let vm_mem = .memories[mem_index.index() as usize];
let vm_mem =
unsafe { &mut (*self.vmctx.local_backing).memories[mem_index.index() as usize] };
(
&mut vm_mem.into_vm_memory(),
Context::Internal,
*self
.module
.memories
.get(mem_index)
.expect("broken invariant, memories"),
)
}
}
2019-01-08 17:09:47 +00:00
}
2019-01-12 21:24:17 +00:00
impl Namespace for Box<Instance> {
fn get_export(&self, name: &str) -> Option<Export> {
let export_index = self.module.exports.get(name)?;
Some(self.get_export_from_index(export_index))
}
}
2019-01-11 03:59:57 +00:00
#[derive(Debug, Clone)]
2019-01-09 03:09:09 +00:00
pub struct FuncRef(*const vm::Func);
impl FuncRef {
/// This needs to be unsafe because there is
/// no way to check whether the passed function
/// is valid and has the right signature.
pub unsafe fn new(f: *const vm::Func) -> Self {
FuncRef(f)
}
pub(crate) fn inner(&self) -> *const vm::Func {
self.0
}
}
2019-01-11 03:59:57 +00:00
pub struct ExportIter<'a> {
instance: &'a Instance,
iter: hash_map::Iter<'a, String, ExportIndex>,
2019-01-08 17:09:47 +00:00
}
2019-01-11 03:59:57 +00:00
impl<'a> ExportIter<'a> {
fn new(instance: &'a Instance) -> Self {
2019-01-08 17:09:47 +00:00
Self {
2019-01-11 03:59:57 +00:00
instance,
iter: instance.module.exports.iter(),
2019-01-08 17:09:47 +00:00
}
}
}
2019-01-11 03:59:57 +00:00
impl<'a> Iterator for ExportIter<'a> {
type Item = (String, Export);
fn next(&mut self) -> Option<(String, Export)> {
let (name, export_index) = self.iter.next()?;
Some((
name.clone(),
self.instance.get_export_from_index(export_index),
))
2019-01-08 17:09:47 +00:00
}
}
2019-01-10 01:45:48 +00:00
// TODO Remove this later, only needed for compilation till emscripten is updated
impl Instance {
2019-01-10 04:43:18 +00:00
pub fn memory_offset_addr(&self, _index: usize, _offset: usize) -> *const usize {
2019-01-10 01:45:48 +00:00
unimplemented!("TODO replace this emscripten stub")
}
}