Merge branch 'master' into fix/emscripten-translate

This commit is contained in:
Brandon Fish
2019-01-23 22:57:00 -06:00
20 changed files with 521 additions and 120 deletions

View File

@ -5,6 +5,7 @@ pub type CompileResult<T> = std::result::Result<T, Box<CompileError>>;
pub type LinkResult<T> = std::result::Result<T, Vec<LinkError>>;
pub type RuntimeResult<T> = std::result::Result<T, Box<RuntimeError>>;
pub type CallResult<T> = std::result::Result<T, Box<CallError>>;
pub type ResolveResult<T> = std::result::Result<T, Box<ResolveError>>;
/// 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<Type> },
ExportNotFound { 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<Type> },
NoSuchExport { name: String },
ExportNotFunc { name: String },
Resolve(ResolveError),
Runtime(RuntimeError),
}
@ -162,3 +178,39 @@ impl From<Box<RuntimeError>> for Box<CallError> {
Box::new(CallError::Runtime(*runtime_err))
}
}
impl From<Box<ResolveError>> for Box<CallError> {
fn from(resolve_err: Box<ResolveError>) -> Self {
Box::new(CallError::Resolve(*resolve_err))
}
}
impl From<CompileError> for Box<Error> {
fn from(compile_err: CompileError) -> Self {
Box::new(Error::CompileError(compile_err))
}
}
impl From<RuntimeError> for Box<Error> {
fn from(runtime_err: RuntimeError) -> Self {
Box::new(Error::RuntimeError(runtime_err))
}
}
impl From<CallError> for Box<Error> {
fn from(call_err: CallError) -> Self {
Box::new(Error::CallError(call_err))
}
}
impl From<RuntimeError> for Box<CallError> {
fn from(runtime_err: RuntimeError) -> Self {
Box::new(CallError::Runtime(runtime_err))
}
}
impl From<ResolveError> for Box<CallError> {
fn from(resolve_err: ResolveError) -> Self {
Box::new(CallError::Resolve(resolve_err))
}
}

View File

@ -5,17 +5,53 @@ pub trait LikeNamespace {
fn get_export(&mut self, name: &str) -> Option<Export>;
}
/// All of the import data used when instantiating.
///
/// It's suggested that you use the [`imports!`] macro
/// instead of creating an `ImportObject` by hand.
///
/// [`imports!`]: macro.imports.html
///
/// # Usage:
/// ```
/// # use wasmer_runtime_core::imports;
/// # use wasmer_runtime_core::vm::Ctx;
/// let import_object = imports! {
/// "env" => {
/// "foo" => foo<[i32] -> [i32]>,
/// },
/// };
///
/// extern fn foo(n: i32, _: &mut Ctx) -> i32 {
/// n
/// }
/// ```
pub struct ImportObject {
map: HashMap<String, Box<dyn LikeNamespace>>,
}
impl ImportObject {
/// Create a new `ImportObject`.
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
/// Register anything that implements `LikeNamespace` as a namespace.
///
/// # Usage:
/// ```
/// # use wasmer_runtime_core::Instance;
/// # use wasmer_runtime_core::import::{ImportObject, Namespace};
/// fn register(instance: Instance, namespace: Namespace) {
/// let mut import_object = ImportObject::new();
///
/// import_object.register("namespace0", instance);
/// import_object.register("namespace1", namespace);
/// // ...
/// }
/// ```
pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>
where
S: Into<String>,

View File

@ -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)]
@ -23,7 +22,13 @@ pub(crate) struct InstanceInner {
vmctx: Box<vm::Ctx>,
}
/// A WebAssembly instance
/// 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 {
module: Rc<ModuleInner>,
inner: Box<InstanceInner>,
@ -66,37 +71,117 @@ impl Instance {
Ok(instance)
}
/// 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`.
/// 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
/// .func("foo")?
/// .call(&[])?;
/// # Ok(())
/// # }
/// ```
pub fn func(&mut self, name: &str) -> ResolveResult<Function> {
let export_index =
self.module
.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
.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`].
///
/// [`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(&mut self, name: &str, args: &[Value]) -> CallResult<Vec<Value>> {
let export_index =
self.module
.exports
.get(name)
.ok_or_else(|| CallError::NoSuchExport {
.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::ExportNotFunc {
return Err(CallError::Resolve(ResolveError::ExportWrongType {
name: name.to_string(),
}
})
.into());
};
self.call_with_index(func_index, args)
}
/// Returns a immutable reference to the
/// [`Ctx`] used by this Instance.
///
/// [`Ctx`]: struct.Ctx.html
pub fn context(&self) -> &vm::Ctx {
&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 {
&mut self.inner.vmctx
}
/// Returns a iterator over all of the items
/// exported from this instance.
pub fn exports(&mut self) -> ExportIter {
ExportIter::new(&self.module, &mut self.inner)
}
/// The module used to instantiate this Instance.
pub fn module(&self) -> Module {
Module::new(Rc::clone(&self.module))
}
@ -116,7 +201,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(),
})?
@ -143,32 +228,6 @@ impl Instance {
Ok(returns)
}
pub fn get_signature(&self, name: &str) -> CallResult<&FuncSig> {
let export_index =
self.module
.exports
.get(name)
.ok_or_else(|| CallError::NoSuchExport {
name: name.to_string(),
})?;
let func_index = if let ExportIndex::Func(func_index) = export_index {
*func_index
} else {
return Err(CallError::ExportNotFunc {
name: name.to_string(),
}
.into());
};
let sig_index = *self
.module
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
Ok(self.module.sig_registry.lookup_func_sig(sig_index))
}
}
impl InstanceInner {
@ -360,9 +419,88 @@ impl LikeNamespace for Instance {
}
}
// TODO Remove this later, only needed for compilation till emscripten is updated
/// 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<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
/// .func("foo")?
/// .call(&[])?;
/// # Ok(())
/// # }
/// ```
pub fn call(&mut self, params: &[Value]) -> CallResult<Vec<Value>> {
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, _index: usize, _offset: usize) -> *const u8 {
pub fn memory_offset_addr(&self, _: u32, _: usize) -> *const u8 {
unimplemented!()
}
}

View File

@ -42,7 +42,12 @@ pub mod prelude {
pub use crate::{export_func, imports};
}
/// Compile a webassembly module using the provided compiler.
/// Compile a [`Module`] using the provided compiler from
/// WebAssembly binary code. This function is useful if it
/// is necessary to a compile a module before it can be instantiated
/// and must be used if you wish to use a different backend from the default.
///
/// [`Module`]: struct.Module.html
pub fn compile_with(
wasm: &[u8],
compiler: &dyn backend::Compiler,
@ -53,9 +58,9 @@ pub fn compile_with(
.map(|inner| module::Module::new(Rc::new(inner)))
}
/// This function performs validation as defined by the
/// WebAssembly specification and returns true if validation
/// succeeded, false if validation failed.
/// Perform validation as defined by the
/// WebAssembly specification. Returns `true` if validation
/// succeeded, `false` if validation failed.
pub fn validate(wasm: &[u8]) -> bool {
use wasmparser::WasmDecoder;
let mut parser = wasmparser::ValidatingParser::new(wasm, None);

View File

@ -51,6 +51,29 @@ macro_rules! __export_func_convert_type {
};
}
/// Generate an [`ImportObject`] safely.
///
/// [`ImportObject`]: struct.ImportObject.html
///
/// # Note:
/// The `import` macro currently only supports
/// importing functions.
///
///
/// # Usage:
/// ```
/// # use wasmer_runtime_core::imports;
/// # use wasmer_runtime_core::vm::Ctx;
/// let import_object = imports! {
/// "env" => {
/// "foo" => foo<[i32] -> [i32]>,
/// },
/// };
///
/// extern fn foo(n: i32, _: &mut Ctx) -> i32 {
/// n
/// }
/// ```
#[macro_export]
macro_rules! imports {
( $( $ns_name:expr => $ns:tt, )* ) => {{

View File

@ -42,16 +42,44 @@ pub struct ModuleInner {
pub sig_registry: SigRegistry,
}
pub struct Module(pub Rc<ModuleInner>);
/// A compiled WebAssembly module.
///
/// `Module` is returned by the [`compile`] and
/// [`compile_with`] functions.
///
/// [`compile`]: fn.compile.html
/// [`compile_with`]: fn.compile_with.html
pub struct Module(#[doc(hidden)] pub Rc<ModuleInner>);
impl Module {
pub(crate) fn new(inner: Rc<ModuleInner>) -> Self {
Module(inner)
}
/// Instantiate a WebAssembly module with the provided imports.
pub fn instantiate(&self, imports: ImportObject) -> Result<Instance> {
Instance::new(Rc::clone(&self.0), Box::new(imports))
/// Instantiate a WebAssembly module with the provided [`ImportObject`].
///
/// [`ImportObject`]: struct.ImportObject.html
///
/// # Note:
/// Instantiating a `Module` will also call the function designated as `start`
/// in the WebAssembly module, if there is one.
///
/// # Usage:
/// ```
/// # use wasmer_runtime_core::error::Result;
/// # use wasmer_runtime_core::Module;
/// # use wasmer_runtime_core::imports;
/// # fn instantiate(module: &Module) -> Result<()> {
/// let import_object = imports! {
/// // ...
/// };
/// let instance = module.instantiate(import_object)?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn instantiate(&self, import_object: ImportObject) -> Result<Instance> {
Instance::new(Rc::clone(&self.0), Box::new(import_object))
}
}

View File

@ -1,7 +1,7 @@
use errno;
use nix::libc;
use page_size;
use std::ops::Range;
use std::ops::{Bound, RangeBounds};
use std::{ptr, slice};
#[derive(Debug)]
@ -42,13 +42,30 @@ impl Memory {
}
}
pub unsafe fn protect(&mut self, range: Range<usize>, protect: Protect) -> Result<(), String> {
pub unsafe fn protect(
&mut self,
range: impl RangeBounds<usize>,
protect: Protect,
) -> Result<(), String> {
let protect = protect.to_protect_const();
let range_start = match range.start_bound() {
Bound::Included(start) => *start,
Bound::Excluded(start) => *start,
Bound::Unbounded => 0,
};
let range_end = match range.end_bound() {
Bound::Included(end) => *end,
Bound::Excluded(end) => *end,
Bound::Unbounded => self.size(),
};
let page_size = page_size::get();
let start = self
.ptr
.add(round_down_to_page_size(range.start, page_size));
let size = round_up_to_page_size(range.end - range.start, page_size);
.add(round_down_to_page_size(range_start, page_size));
let size = round_up_to_page_size(range_end - range_start, page_size);
assert!(size <= self.size);
let success = libc::mprotect(start as _, size, protect as i32);

View File

@ -4,7 +4,7 @@ use winapi::um::memoryapi::{
PAGE_NOACCESS, PAGE_EXECUTE_READ, PAGE_READWRITE, PAGE_READONLY,
};
use page_size;
use std::ops::Range;
use std::ops::{Bound, RangeBounds};
use std::{ptr, slice};
#[derive(Debug)]
@ -43,13 +43,26 @@ impl Memory {
}
}
pub unsafe fn protect(&mut self, range: Range<usize>, protect: Protect) -> Result<(), String> {
pub unsafe fn protect(&mut self, range: impl RangeBounds<usize>, protect: Protect) -> Result<(), String> {
let protect = protect.to_protect_const();
let range_start = match range.start_bound() {
Bound::Included(start) => *start,
Bound::Excluded(start) => *start,
Bound::Unbounded => 0,
};
let range_end = match range.end_bound() {
Bound::Included(end) => *end,
Bound::Excluded(end) => *end,
Bound::Unbounded => self.size(),
};
let page_size = page_size::get();
let start = self
.ptr
.add(round_down_to_page_size(range.start, page_size));
let size = round_up_to_page_size(range.end - range.start, page_size);
.add(round_down_to_page_size(range_start, page_size));
let size = round_up_to_page_size(range_end - range_start, page_size);
assert!(size <= self.size);
// Commit the virtual memory.

View File

@ -1,5 +1,6 @@
use crate::{module::ModuleInner, structures::TypedIndex};
/// Represents a WebAssembly type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type {
/// The `i32` type.
@ -12,6 +13,10 @@ pub enum Type {
F64,
}
/// Represents a WebAssembly value.
///
/// As the number of types in WebAssembly expand,
/// this structure will expand as well.
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
/// The `i32` type.

View File

@ -6,6 +6,9 @@ use crate::{
};
use std::{ffi::c_void, mem, ptr, slice};
/// The context of the currently running WebAssembly instance.
///
///
#[derive(Debug)]
#[repr(C)]
pub struct Ctx {
@ -39,6 +42,7 @@ pub struct Ctx {
}
impl Ctx {
#[doc(hidden)]
pub unsafe fn new(
local_backing: &mut LocalBacking,
import_backing: &mut ImportBacking,
@ -63,6 +67,7 @@ impl Ctx {
}
}
#[doc(hidden)]
pub unsafe fn new_with_data(
local_backing: &mut LocalBacking,
import_backing: &mut ImportBacking,
@ -89,7 +94,64 @@ impl Ctx {
}
}
pub fn memory<'a>(&'a mut self, mem_index: u32) -> &'a mut [u8] {
/// This exposes the specified memory of the WebAssembly instance
/// as a immutable slice.
///
/// WebAssembly will soon support multiple linear memories, so this
/// forces the user to specify.
///
/// # Usage:
///
/// ```
/// # use wasmer_runtime_core::{
/// # vm::Ctx,
/// # };
/// fn read_memory(ctx: &Ctx) -> u8 {
/// let first_memory = ctx.memory(0);
/// // Read the first byte of that linear memory.
/// first_memory[0]
/// }
/// ```
pub fn memory<'a>(&'a self, mem_index: u32) -> &'a [u8] {
let module = unsafe { &*self.module };
let mem_index = MemoryIndex::new(mem_index as usize);
match mem_index.local_or_import(module) {
LocalOrImport::Local(local_mem_index) => {
let local_backing = unsafe { &*self.local_backing };
&local_backing.memories[local_mem_index][..]
}
LocalOrImport::Import(import_mem_index) => {
let import_backing = unsafe { &mut *self.import_backing };
let vm_memory_import = import_backing.memories[import_mem_index].clone();
unsafe {
let memory = &*vm_memory_import.memory;
slice::from_raw_parts(memory.base, memory.size)
}
}
}
}
/// This exposes the specified memory of the WebAssembly instance
/// as a mutable slice.
///
/// WebAssembly will soon support multiple linear memories, so this
/// forces the user to specify.
///
/// # Usage:
///
/// ```
/// # use wasmer_runtime_core::{
/// # vm::Ctx,
/// # error::Result,
/// # };
/// extern fn host_func(ctx: &mut Ctx) {
/// let first_memory = ctx.memory_mut(0);
/// // Set the first byte of that linear memory.
/// first_memory[0] = 42;
/// }
/// ```
pub fn memory_mut<'a>(&'a mut self, mem_index: u32) -> &'a mut [u8] {
let module = unsafe { &*self.module };
let mem_index = MemoryIndex::new(mem_index as usize);
match mem_index.local_or_import(module) {
@ -110,6 +172,7 @@ impl Ctx {
}
}
#[doc(hidden)]
impl Ctx {
#[allow(clippy::erasing_op)] // TODO
pub fn offset_memories() -> u8 {
@ -145,10 +208,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)]