Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add types and methods to provide updated API #1313

Merged
merged 22 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion lib/runtime-core-tests/tests/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,44 @@ use std::{convert::TryInto, sync::Arc};
use wasmer_runtime_core::{
compile_with,
error::RuntimeError,
global::Global,
imports,
memory::Memory,
typed_func::{DynamicFunc, Func},
types::{FuncSig, MemoryDescriptor, Type, Value},
units::Pages,
vm, Instance,
vm, DynFunc, Instance,
};
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};

#[test]
fn new_api_works() {
let wasm = r#"
(module
(type $type (func (param i32) (result i32)))
(global (export "my_global") i32 (i32.const 45))
(func (export "add_one") (type $type)
(i32.add (get_local 0)
(i32.const 1)))
(func (export "double") (type $type)
(i32.mul (get_local 0)
(i32.const 2)))
)"#;
let wasm_binary = wat2wasm(wasm.as_bytes()).expect("WAST not valid or malformed");
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
let import_object = imports! {};
let instance = module.instantiate(&import_object).unwrap();

let my_global: Global = instance.exports_new().get("my_global").unwrap();
assert_eq!(my_global.get(), Value::I32(45));
let double: Func<i32, i32> = instance.exports_new().get("double").unwrap();
assert_eq!(double.call(5).unwrap(), 10);
let add_one: DynFunc = instance.exports_new().get("add_one").unwrap();
assert_eq!(add_one.call(&[Value::I32(5)]).unwrap(), &[Value::I32(6)]);
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
let add_one_memory: Option<DynFunc> = instance.exports_new().get("my_global");
assert!(add_one_memory.is_none());
}

macro_rules! call_and_assert {
($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
#[allow(unused_parens)]
Expand Down
22 changes: 17 additions & 5 deletions lib/runtime-core/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
//! The export module contains the implementation data structures and helper functions used to
//! manipulate and access a wasm module's exports including memories, tables, globals, and
//! functions.
//! This module contains types to manipulate and access a Wasm module's exports
//! including memories, tables, globals, and functions.
use crate::{
global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex,
module::ModuleInner, table::Table, types::FuncSig, vm,
global::Global,
instance::{Instance, InstanceInner},
memory::Memory,
module::ExportIndex,
module::ModuleInner,
table::Table,
types::FuncSig,
vm,
};
use indexmap::map::Iter as IndexMapIter;
use std::{ptr::NonNull, sync::Arc};
Expand Down Expand Up @@ -92,3 +97,10 @@ impl<'a> Iterator for ExportIter<'a> {
))
}
}

/// This trait is used to mark types as gettable from an [`Instance`].
pub trait Exportable<'a>: Sized {
/// Implementation of how to get the export corresponding to the implementing type
/// from an [`Instance`] by name.
fn get_self(instance: &'a Instance, name: &str) -> Option<Self>;
}
97 changes: 94 additions & 3 deletions lib/runtime-core/src/instance.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! The instance module contains the implementation data structures and helper functions used to
//! manipulate and access wasm instances.
//! This module contains types for manipulating and accessing Wasm instances.
//!
//! An "instance", or "instantiated module", is a compiled WebAssembly [`Module`] with its
//! corresponding imports (via [`ImportObject`]) that is ready to execute.
use crate::{
backend::RunnableModule,
backing::{ImportBacking, LocalBacking},
error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError},
export::{Context, Export, ExportIter, FuncPointer},
export::{Context, Export, ExportIter, Exportable, FuncPointer},
global::Global,
import::{ImportObject, LikeNamespace},
loader::Loader,
Expand All @@ -19,6 +21,7 @@ use crate::{
};
use smallvec::{smallvec, SmallVec};
use std::{
borrow::Borrow,
mem,
pin::Pin,
ptr::{self, NonNull},
Expand Down Expand Up @@ -58,6 +61,11 @@ pub struct Instance {
}

impl Instance {
/// Access the new exports API.
/// TODO: rename etc.
pub fn exports_new(&self) -> Exports {
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
Exports { instance: self }
}
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
Expand Down Expand Up @@ -814,6 +822,89 @@ impl<'a> DynFunc<'a> {
}
}

impl<'a> Exportable<'a> for Memory {
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
let export_index = instance.module.info.exports.get(name)?;
if let ExportIndex::Memory(idx) = export_index {
let module = instance.module.borrow();
Some(instance.inner.get_memory_from_index(module, *idx))
} else {
None
}
}
}

impl<'a> Exportable<'a> for Table {
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
let export_index = instance.module.info.exports.get(name)?;
if let ExportIndex::Table(idx) = export_index {
let module = instance.module.borrow();
Some(instance.inner.get_table_from_index(module, *idx))
} else {
None
}
}
}

impl<'a> Exportable<'a> for Global {
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
let export_index = instance.module.info.exports.get(name)?;
if let ExportIndex::Global(idx) = export_index {
let module = instance.module.borrow();
Some(instance.inner.get_global_from_index(module, *idx))
} else {
None
}
}
}

impl<'a> Exportable<'a> for DynFunc<'a> {
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
instance.dyn_func(name).ok()
}
}

impl<'a, Args: WasmTypeList, Rets: WasmTypeList> Exportable<'a> for Func<'a, Args, Rets, Wasm> {
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
instance.func(name).ok()
}
}

/// `Exports` is used to get exports like [`Func`]s, [`DynFunc`]s, [`Memory`]s,
/// [`Global`]s, and [`Table`]s from an [`Instance`].
///
/// Use [`Instance::exports_new`] to get an `Exports` from an [`Instance`].
pub struct Exports<'a> {
instance: &'a Instance,
}

impl<'a> Exports<'a> {
/// Get an export.
///
/// ```
/// # use wasmer_runtime_core::{DynFunc, Func, Instance};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be wasmer_runtime instead? I would prefer people depending on the the outer shell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's possible to move the doc comments with examples to wasmer_runtime instead, though the # on that line means that it's invisible to users in the docs, so it doesn't matter here

/// # use wasmer_runtime_core::global::Global;
/// # use wasmer_runtime_core::types::Value;
/// # fn example_fn(instance: &Instance) -> Option<()> {
/// // We can get a function as a static `Func`
/// let func: Func<i32, i32> = instance.exports_new().get("my_func")?;
/// let _result = func.call(42);
///
/// // Or we can get it as a dynamic `DynFunc`
/// let dyn_func: DynFunc = instance.exports_new().get("my_func")?;
/// let _result= dyn_func.call(&[Value::I32(42)]);
///
/// // We can also get other exports like `Global`s, `Memory`s, and `Table`s
/// let _counter: Global = instance.exports_new().get("counter")?;
///
/// # Some(())
/// # }
/// ```
pub fn get<T: Exportable<'a>>(&self, name: &str) -> Option<T> {
T::get_self(self.instance, name)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
84 changes: 80 additions & 4 deletions lib/runtime-core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ macro_rules! func {
/// ```
#[macro_export]
macro_rules! imports {
( $( $ns_name:expr => $ns:tt, )* ) => {{
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
use $crate::{
import::{ImportObject, Namespace},
};
Expand All @@ -91,7 +91,7 @@ macro_rules! imports {

import_object
}};
($state_gen:expr, $( $ns_name:expr => $ns:tt, )* ) => {{
($state_gen:expr, $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
use $crate::{
import::{ImportObject, Namespace},
};
Expand All @@ -111,7 +111,7 @@ macro_rules! imports {
#[macro_export]
#[doc(hidden)]
macro_rules! __imports_internal {
( { $( $imp_name:expr => $import_item:expr, )* } ) => {{
( { $( $imp_name:expr => $import_item:expr ),* $(,)? } ) => {{
let mut ns = Namespace::new();
$(
ns.insert($imp_name, $import_item);
Expand All @@ -126,11 +126,87 @@ macro_rules! __imports_internal {
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
( $( $imp_name:expr => $import_item:expr, )* ) => {{
( $( $imp_name:expr => $import_item:expr ),* $(,)? ) => {{
let mut ns = $crate::import::Namespace::new();
$(
ns.insert($imp_name, $import_item);
)*
ns
}};
}

#[cfg(test)]
mod test {
#[test]
fn imports_macro_allows_trailing_comma_and_none() {
fn func(arg: i32) -> i32 {
arg + 1
}
let _ = imports! {
"env" => {
"func" => func!(func),
},
};
let _ = imports! {
"env" => {
"func" => func!(func),
}
};
let _ = imports! {
"env" => {
"func" => func!(func),
},
"abc" => {
"def" => func!(func),
}
};
let _ = imports! {
"env" => {
"func" => func!(func)
},
};
let _ = imports! {
"env" => {
"func" => func!(func)
}
};
let _ = imports! {
"env" => {
"func1" => func!(func),
"func2" => func!(func)
}
};
let _ = imports! {
"env" => {
"func1" => func!(func),
"func2" => func!(func),
}
};
use std::{ffi, ptr};
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
fn dtor(_arg: *mut ffi::c_void) {}
fn state_creator() -> (*mut ffi::c_void, fn(*mut ffi::c_void)) {
(ptr::null_mut() as *mut ffi::c_void, dtor)
}
let _ = imports! {
state_creator,
"env" => {
"func1" => func!(func),
"func2" => func!(func),
}
};
let _ = imports! {
state_creator,
"env" => {
"func1" => func!(func),
"func2" => func!(func)
},
};
let _ = imports! {
state_creator,
"env" => {
"func1" => func!(func),
"func2" => func!(func),
},
};
}
}
Loading