2019-01-08 17:09:47 +00:00
|
|
|
use crate::recovery::call_protected;
|
|
|
|
use crate::{
|
|
|
|
backing::{ImportBacking, LocalBacking},
|
2019-01-12 22:52:14 +00:00
|
|
|
export::{Context, Export, ExportIter, FuncPointer, MemoryPointer},
|
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,
|
|
|
|
};
|
|
|
|
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
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
struct InstanceInner {
|
2019-01-11 03:59:57 +00:00
|
|
|
#[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
|
|
|
}
|
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
pub struct Instance {
|
|
|
|
pub module: Module,
|
|
|
|
inner: Box<InstanceInner>,
|
|
|
|
}
|
|
|
|
|
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-12 22:52:14 +00:00
|
|
|
) -> Result<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-12 22:52:14 +00:00
|
|
|
let mut inner = Box::new(InstanceInner {
|
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.
|
2019-01-12 22:52:14 +00:00
|
|
|
*inner.vmctx = vm::Ctx::new(&mut inner.backing, &mut inner.import_backing);
|
|
|
|
|
|
|
|
let mut instance = Instance {
|
|
|
|
module,
|
|
|
|
inner,
|
|
|
|
};
|
2019-01-11 03:59:57 +00:00
|
|
|
|
|
|
|
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.
|
2019-01-09 23:31:11 +00:00
|
|
|
/// 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
|
|
|
///
|
2019-01-09 23:31:11 +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)
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
pub fn exports(&self) -> ExportIter {
|
|
|
|
ExportIter::new(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Instance {
|
2019-01-08 17:09:47 +00:00
|
|
|
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,
|
2019-01-12 22:52:14 +00:00
|
|
|
Context::Internal => &mut *self.inner.vmctx,
|
2019-01-11 03:59:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
pub(crate) fn get_export_from_index(&self, export_index: &ExportIndex) -> Export {
|
2019-01-11 03:59:57 +00:00
|
|
|
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 => {
|
2019-01-12 22:52:14 +00:00
|
|
|
Context::External(&*self.inner.vmctx as *const vm::Ctx as *mut vm::Ctx)
|
2019-01-11 03:59:57 +00:00
|
|
|
}
|
|
|
|
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 => {
|
2019-01-12 22:52:14 +00:00
|
|
|
Context::External(&*self.inner.vmctx as *const vm::Ctx as *mut vm::Ctx)
|
2019-01-12 20:34:23 +00:00
|
|
|
}
|
|
|
|
ctx @ Context::External(_) => ctx,
|
|
|
|
},
|
|
|
|
memory,
|
|
|
|
}
|
|
|
|
}
|
2019-01-11 03:59:57 +00:00
|
|
|
ExportIndex::Global(_global_index) => unimplemented!(),
|
|
|
|
ExportIndex::Table(_table_index) => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
fn get_func_from_index(&self, func_index: FuncIndex) -> (FuncPointer, Context, FuncSig) {
|
2019-01-11 03:59:57 +00:00
|
|
|
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) {
|
2019-01-12 22:52:14 +00:00
|
|
|
let imported_func = &self.inner.import_backing.functions[func_index.index()];
|
2019-01-11 03:59:57 +00:00
|
|
|
(
|
|
|
|
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();
|
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
(unsafe { FuncPointer::new(func_ptr) }, ctx, signature)
|
2019-01-11 03:59:57 +00:00
|
|
|
}
|
2019-01-12 20:34:23 +00:00
|
|
|
|
|
|
|
fn get_memory_from_index(
|
|
|
|
&self,
|
|
|
|
mem_index: MemoryIndex,
|
2019-01-12 22:52:14 +00:00
|
|
|
) -> (MemoryPointer, Context, Memory) {
|
2019-01-12 20:34:23 +00:00
|
|
|
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 } =
|
2019-01-12 22:52:14 +00:00
|
|
|
&self.inner.import_backing.memories[mem_index.index()];
|
|
|
|
(unsafe { MemoryPointer::new(*memory) }, Context::External(*vmctx), *mem)
|
2019-01-12 20:34:23 +00:00
|
|
|
} else {
|
2019-01-12 22:52:14 +00:00
|
|
|
let vm_mem = &self.inner.backing.memories[mem_index.index() as usize];
|
2019-01-12 20:34:23 +00:00
|
|
|
(
|
2019-01-12 22:52:14 +00:00
|
|
|
unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) },
|
2019-01-12 20:34:23 +00:00
|
|
|
Context::Internal,
|
|
|
|
*self
|
|
|
|
.module
|
|
|
|
.memories
|
|
|
|
.get(mem_index)
|
|
|
|
.expect("broken invariant, memories"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2019-01-08 17:09:47 +00:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:52:14 +00:00
|
|
|
impl Namespace for Instance {
|
2019-01-12 21:24:17 +00:00
|
|
|
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-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")
|
|
|
|
}
|
|
|
|
}
|