add 'ProtectedCaller' to runtime

This commit is contained in:
Lachlan Sneff 2019-01-18 12:13:01 -08:00
parent ed87168c48
commit 705708cafe
6 changed files with 183 additions and 81 deletions

View File

@ -12,7 +12,7 @@ use cranelift_codegen::{
};
use target_lexicon::Triple;
use wasmer_runtime::{
backend::Compiler,
backend::{Compiler, Token},
error::{CompileError, CompileResult},
module::ModuleInner,
};
@ -28,7 +28,7 @@ impl CraneliftCompiler {
impl Compiler for CraneliftCompiler {
// Compiles wasm binary to a wasmer module.
fn compile(&self, wasm: &[u8]) -> CompileResult<ModuleInner> {
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner> {
validate(wasm)?;
let isa = get_isa();

View File

@ -1,18 +1,74 @@
use crate::{error::CompileResult, module::ModuleInner, types::LocalFuncIndex, vm};
use crate::{
error::CompileResult,
error::RuntimeResult,
module::ModuleInner,
types::{FuncIndex, LocalFuncIndex, Value},
vm,
};
use std::ptr::NonNull;
pub use crate::mmap::{Mmap, Protect};
pub use crate::sig_registry::SigRegistry;
/// This type cannot be constructed from
/// outside the runtime crate.
pub struct Token {
_private: (),
}
impl Token {
pub(crate) fn generate() -> Self {
Self { _private: () }
}
}
pub trait Compiler {
/// Compiles a `Module` from WebAssembly binary format
fn compile(&self, wasm: &[u8]) -> CompileResult<ModuleInner>;
/// Compiles a `Module` from WebAssembly binary format.
/// The `CompileToken` parameter ensures that this can only
/// be called from inside the runtime.
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>;
}
/// The functionality exposed by this trait is expected to be used
/// for calling functions exported by a webassembly module from
/// host code only.
pub trait ProtectedCaller {
/// This calls the exported function designated by `local_func_index`.
/// Important to note, this supports calling imported functions that are
/// then exported.
///
/// It's invalid to attempt to call a local function that isn't exported and
/// the implementation is expected to check for that. The implementation
/// is also expected to check for correct parameter types and correct
/// parameter number.
///
/// The `returns` parameter is filled with dummy values when passed in and upon function
/// return, will be filled with the return values of the wasm function, as long as the
/// call completed successfully.
///
/// The existance of the Token parameter ensures that this can only be called from
/// within the runtime crate.
fn call(
&self,
module: &ModuleInner,
func_index: FuncIndex,
params: &[Value],
returns: &mut [Value],
vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<()>;
}
pub trait FuncResolver {
/// This returns a pointer to the function designated by the `local_func_index`
/// parameter.
///
/// The existance of the Token parameter ensures that this can only be called from
/// within the runtime crate.
fn get(
&self,
module: &ModuleInner,
local_func_index: LocalFuncIndex,
_: Token,
) -> Option<NonNull<vm::Func>>;
}

View File

@ -1,4 +1,5 @@
use crate::{
backend::Token,
error::{LinkError, LinkResult},
export::{Context, Export},
import::Imports,
@ -178,14 +179,17 @@ impl LocalBacking {
let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index)
.unwrap()
.as_ptr(),
vmctx,
},
LocalOrImport::Local(local_func_index) => {
let token = Token::generate();
vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index, token)
.unwrap()
.as_ptr(),
vmctx,
}
}
LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone()
}
@ -229,14 +233,17 @@ impl LocalBacking {
let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index)
.unwrap()
.as_ptr(),
vmctx,
},
LocalOrImport::Local(local_func_index) => {
let token = Token::generate();
vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index, token)
.unwrap()
.as_ptr(),
vmctx,
}
}
LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone()
}

View File

@ -1,5 +1,6 @@
use crate::recovery::call_protected;
use crate::{
backend::Token,
backing::{ImportBacking, LocalBacking},
error::{CallError, CallResult, Result},
export::{
@ -67,11 +68,11 @@ impl 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`.
/// The returned values are also each wrapped in a `Value`.
///
/// This will eventually return `Result<Option<Vec<Value>>, String>` in
/// order to support multi-value returns.
pub fn call(&mut self, name: &str, args: &[Value]) -> CallResult<Option<Value>> {
/// This returns `CallResult<Vec<Value>>` in order to support
/// the future multi-value returns webassembly feature.
pub fn call(&mut self, name: &str, args: &[Value]) -> CallResult<Vec<Value>> {
let export_index =
self.module
.exports
@ -102,23 +103,13 @@ impl Instance {
}
impl Instance {
fn call_with_index(
&mut self,
func_index: FuncIndex,
args: &[Value],
) -> CallResult<Option<Value>> {
let (func_ref, ctx, signature) = self.inner.get_func_from_index(&self.module, func_index);
let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
let vmctx_ptr = match ctx {
Context::External(vmctx) => vmctx,
Context::Internal => &mut *self.inner.vmctx,
};
assert!(
signature.returns.len() <= 1,
"multi-value returns not yet supported"
);
fn call_with_index(&mut self, func_index: FuncIndex, args: &[Value]) -> CallResult<Vec<Value>> {
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);
if !signature.check_sig(args) {
Err(CallError::Signature {
@ -127,35 +118,78 @@ impl Instance {
})?
}
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();
// Create an output vector that's full of dummy values.
let mut returns = vec![Value::I32(0); signature.returns.len()];
Ok(call_protected(|| {
signature
.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
})
})?)
let vmctx = match func_index.local_or_import(&self.module) {
LocalOrImport::Local(local_func_index) => &mut *self.inner.vmctx,
LocalOrImport::Import(imported_func_index) => {
self.inner.import_backing.functions[imported_func_index].vmctx
}
};
let token = Token::generate();
self.module.protected_caller.call(
&self.module,
func_index,
args,
&mut returns,
vmctx,
token,
)?;
Ok(returns)
// let (func_ref, ctx, signature) = self.inner.get_func_from_index(&self.module, func_index);
// let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
// let vmctx_ptr = match ctx {
// Context::External(vmctx) => vmctx,
// Context::Internal => &mut *self.inner.vmctx,
// };
// assert!(
// signature.returns.len() <= 1,
// "multi-value returns not yet supported"
// );
// if !signature.check_sig(args) {
// Err(CallError::Signature {
// expected: signature.clone(),
// found: args.iter().map(|val| val.ty()).collect(),
// })?
// }
// 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();
// Ok(call_protected(|| {
// signature
// .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
// })
// })?)
}
}
@ -218,15 +252,18 @@ impl InstanceInner {
.expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => (
module
.func_resolver
.get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr() as *const _,
Context::Internal,
),
LocalOrImport::Local(local_func_index) => {
let token = Token::generate();
(
module
.func_resolver
.get(&module, local_func_index, token)
.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.functions[imported_func_index];
(

View File

@ -32,7 +32,8 @@ use std::rc::Rc;
/// Compile a webassembly module using the provided compiler.
pub fn compile(wasm: &[u8], compiler: &dyn backend::Compiler) -> CompileResult<module::Module> {
let token = backend::Token::generate();
compiler
.compile(wasm)
.compile(wasm, token)
.map(|inner| module::Module::new(Rc::new(inner)))
}

View File

@ -1,5 +1,5 @@
use crate::{
backend::FuncResolver,
backend::{FuncResolver, ProtectedCaller},
error::Result,
import::Imports,
sig_registry::SigRegistry,
@ -18,6 +18,7 @@ use std::rc::Rc;
#[doc(hidden)]
pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>,
pub protected_caller: Box<dyn ProtectedCaller>,
// This are strictly local and the typsystem ensures that.
pub memories: Map<LocalMemoryIndex, Memory>,
pub globals: Map<LocalGlobalIndex, Global>,