diff --git a/crates/api/examples/hello.rs b/crates/api/examples/hello.rs
index 317e0361f3..9a461569ba 100644
--- a/crates/api/examples/hello.rs
+++ b/crates/api/examples/hello.rs
@@ -1,19 +1,8 @@
//! Translation of hello example
use anyhow::{ensure, Context as _, Result};
-use std::rc::Rc;
use wasmtime::*;
-struct HelloCallback;
-
-impl Callable for HelloCallback {
- fn call(&self, _params: &[Val], _results: &mut [Val]) -> Result<(), Trap> {
- println!("Calling back...");
- println!("> Hello World!");
- Ok(())
- }
-}
-
fn main() -> Result<()> {
// Configure the initial compilation environment, creating the global
// `Store` structure. Note that you can also tweak configuration settings
@@ -34,8 +23,10 @@ fn main() -> Result<()> {
// Here we handle the imports of the module, which in this case is our
// `HelloCallback` type and its associated implementation of `Callback.
println!("Creating callback...");
- let hello_type = FuncType::new(Box::new([]), Box::new([]));
- let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback));
+ let hello_func = Func::wrap0(&store, || {
+ println!("Calling back...");
+ println!("> Hello World!");
+ });
// Once we've got that all set up we can then move to the instantiation
// phase, pairing together a compiled module as well as a set of imports.
diff --git a/crates/api/src/externals.rs b/crates/api/src/externals.rs
index de9c373e18..fda3d1b635 100644
--- a/crates/api/src/externals.rs
+++ b/crates/api/src/externals.rs
@@ -1,12 +1,9 @@
-use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable};
-use crate::runtime::Store;
use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export};
-use crate::trap::Trap;
-use crate::types::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use crate::Mutability;
+use crate::{ExternType, GlobalType, MemoryType, TableType, ValType};
+use crate::{Func, Store};
use anyhow::{anyhow, bail, Result};
-use std::fmt;
use std::rc::Rc;
use std::slice;
use wasmtime_environ::wasm;
@@ -140,121 +137,6 @@ impl From
for Extern {
}
}
-/// A WebAssembly function which can be called.
-///
-/// This type can represent a number of callable items, such as:
-///
-/// * An exported function from a WebAssembly module.
-/// * A user-defined function used to satisfy an import.
-///
-/// These types of callable items are all wrapped up in this `Func` and can be
-/// used to both instantiate an [`Instance`](crate::Instance) as well as be
-/// extracted from an [`Instance`](crate::Instance).
-///
-/// # `Func` and `Clone`
-///
-/// Functions are internally reference counted so you can `clone` a `Func`. The
-/// cloning process only performs a shallow clone, so two cloned `Func`
-/// instances are equivalent in their functionality.
-#[derive(Clone)]
-pub struct Func {
- _store: Store,
- callable: Rc,
- ty: FuncType,
-}
-
-impl Func {
- /// Creates a new `Func` with the given arguments, typically to create a
- /// user-defined function to pass as an import to a module.
- ///
- /// * `store` - a cache of data where information is stored, typically
- /// shared with a [`Module`](crate::Module).
- ///
- /// * `ty` - the signature of this function, used to indicate what the
- /// inputs and outputs are, which must be WebAssembly types.
- ///
- /// * `callable` - a type implementing the [`Callable`] trait which
- /// is the implementation of this `Func` value.
- ///
- /// Note that the implementation of `callable` must adhere to the `ty`
- /// signature given, error or traps may occur if it does not respect the
- /// `ty` signature.
- pub fn new(store: &Store, ty: FuncType, callable: Rc) -> Self {
- let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
- Func::from_wrapped(store, ty, callable)
- }
-
- fn from_wrapped(
- store: &Store,
- ty: FuncType,
- callable: Rc,
- ) -> Func {
- Func {
- _store: store.clone(),
- callable,
- ty,
- }
- }
-
- /// Returns the underlying wasm type that this `Func` has.
- pub fn ty(&self) -> &FuncType {
- &self.ty
- }
-
- /// Returns the number of parameters that this function takes.
- pub fn param_arity(&self) -> usize {
- self.ty.params().len()
- }
-
- /// Returns the number of results this function produces.
- pub fn result_arity(&self) -> usize {
- self.ty.results().len()
- }
-
- /// Invokes this function with the `params` given, returning the results and
- /// any trap, if one occurs.
- ///
- /// The `params` here must match the type signature of this `Func`, or a
- /// trap will occur. If a trap occurs while executing this function, then a
- /// trap will also be returned.
- ///
- /// This function should not panic unless the underlying function itself
- /// initiates a panic.
- pub fn call(&self, params: &[Val]) -> Result, Trap> {
- let mut results = vec![Val::null(); self.result_arity()];
- self.callable.call(params, &mut results)?;
- Ok(results.into_boxed_slice())
- }
-
- pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
- self.callable.wasmtime_export()
- }
-
- pub(crate) fn from_wasmtime_function(
- export: wasmtime_runtime::Export,
- store: &Store,
- instance_handle: InstanceHandle,
- ) -> Self {
- // This is only called with `Export::Function`, and since it's coming
- // from wasmtime_runtime itself we should support all the types coming
- // out of it, so assert such here.
- let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
- FuncType::from_wasmtime_signature(signature.clone())
- .expect("core wasm signature should be supported")
- } else {
- panic!("expected function export")
- };
- let callable = WasmtimeFn::new(store, instance_handle, export);
- Func::from_wrapped(store, ty, Rc::new(callable))
- }
-}
-
-impl fmt::Debug for Func {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "Func")
- }
-}
-
/// A WebAssembly `global` value which can be read and written to.
///
/// A `global` in WebAssembly is sort of like a global variable within an
diff --git a/crates/api/src/func.rs b/crates/api/src/func.rs
new file mode 100644
index 0000000000..ff040b2b0c
--- /dev/null
+++ b/crates/api/src/func.rs
@@ -0,0 +1,329 @@
+use crate::callable::{NativeCallable, WasmtimeFn, WrappedCallable};
+use crate::{Callable, FuncType, Store, Trap, Val, ValType};
+use std::fmt;
+use std::panic::{self, AssertUnwindSafe};
+use std::rc::Rc;
+use wasmtime_jit::InstanceHandle;
+use wasmtime_runtime::VMContext;
+
+/// A WebAssembly function which can be called.
+///
+/// This type can represent a number of callable items, such as:
+///
+/// * An exported function from a WebAssembly module.
+/// * A user-defined function used to satisfy an import.
+///
+/// These types of callable items are all wrapped up in this `Func` and can be
+/// used to both instantiate an [`Instance`](crate::Instance) as well as be
+/// extracted from an [`Instance`](crate::Instance).
+///
+/// # `Func` and `Clone`
+///
+/// Functions are internally reference counted so you can `clone` a `Func`. The
+/// cloning process only performs a shallow clone, so two cloned `Func`
+/// instances are equivalent in their functionality.
+#[derive(Clone)]
+pub struct Func {
+ _store: Store,
+ callable: Rc,
+ ty: FuncType,
+}
+
+macro_rules! wrappers {
+ ($(
+ $(#[$doc:meta])*
+ ($name:ident $(,$args:ident)*)
+ )*) => ($(
+ $(#[$doc])*
+ pub fn $name(store: &Store, func: F) -> Func
+ where
+ F: Fn($($args),*) -> R + 'static,
+ $($args: WasmArg,)*
+ R: WasmRet,
+ {
+ #[allow(non_snake_case)]
+ unsafe extern "C" fn shim(
+ vmctx: *mut VMContext,
+ _caller_vmctx: *mut VMContext,
+ $($args: $args,)*
+ ) -> R::Abi
+ where
+ F: Fn($($args),*) -> R + 'static,
+ R: WasmRet,
+ {
+ let ret = {
+ let instance = InstanceHandle::from_vmctx(vmctx);
+ let func = instance.host_state().downcast_ref::().expect("state");
+ panic::catch_unwind(AssertUnwindSafe(|| func($($args),*)))
+ };
+ match ret {
+ Ok(ret) => ret.into_abi(),
+ Err(panic) => wasmtime_runtime::resume_panic(panic),
+ }
+ }
+
+ let mut _args = Vec::new();
+ $($args::push(&mut _args);)*
+ let mut ret = Vec::new();
+ R::push(&mut ret);
+ let ty = FuncType::new(_args.into(), ret.into());
+ unsafe {
+ let (instance, export) = crate::trampoline::generate_raw_func_export(
+ &ty,
+ shim:: as *const _,
+ store,
+ Box::new(func),
+ )
+ .expect("failed to generate export");
+ let callable = Rc::new(WasmtimeFn::new(store, instance, export));
+ Func::from_wrapped(store, ty, callable)
+ }
+ }
+ )*)
+}
+
+impl Func {
+ /// Creates a new `Func` with the given arguments, typically to create a
+ /// user-defined function to pass as an import to a module.
+ ///
+ /// * `store` - a cache of data where information is stored, typically
+ /// shared with a [`Module`](crate::Module).
+ ///
+ /// * `ty` - the signature of this function, used to indicate what the
+ /// inputs and outputs are, which must be WebAssembly types.
+ ///
+ /// * `callable` - a type implementing the [`Callable`] trait which
+ /// is the implementation of this `Func` value.
+ ///
+ /// Note that the implementation of `callable` must adhere to the `ty`
+ /// signature given, error or traps may occur if it does not respect the
+ /// `ty` signature.
+ pub fn new(store: &Store, ty: FuncType, callable: Rc) -> Self {
+ let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
+ Func::from_wrapped(store, ty, callable)
+ }
+
+ wrappers! {
+ /// Creates a new `Func` from the given Rust closure, which takes 0
+ /// arguments.
+ ///
+ /// For more information about this function, see [`Func::wrap1`].
+ (wrap0)
+
+ /// Creates a new `Func` from the given Rust closure, which takes 1
+ /// argument.
+ ///
+ /// This function will create a new `Func` which, when called, will
+ /// execute the given Rust closure. Unlike [`Func::new`] the target
+ /// function being called is known statically so the type signature can
+ /// be inferred. Rust types will map to WebAssembly types as follows:
+ ///
+ ///
+ /// | Rust Argument Type | WebAssembly Type |
+ /// |--------------------|------------------|
+ /// | `i32` | `i32` |
+ /// | `i64` | `i64` |
+ /// | `f32` | `f32` |
+ /// | `f64` | `f64` |
+ /// | (not supported) | `v128` |
+ /// | (not supported) | `anyref` |
+ ///
+ /// Any of the Rust types can be returned from the closure as well, in
+ /// addition to some extra types
+ ///
+ /// | Rust Return Type | WebAssembly Return Type | Meaning |
+ /// |-------------------|-------------------------|-------------------|
+ /// | `()` | nothing | no return value |
+ /// | `Result` | `T` | function may trap |
+ ///
+ /// Note that when using this API (and the related `wrap*` family of
+ /// functions), the intention is to create as thin of a layer as
+ /// possible for when WebAssembly calls the function provided. With
+ /// sufficient inlining and optimization the WebAssembly will call
+ /// straight into `func` provided, with no extra fluff entailed.
+ (wrap1, A)
+
+ /// Creates a new `Func` from the given Rust closure, which takes 2
+ /// arguments.
+ ///
+ /// For more information about this function, see [`Func::wrap1`].
+ (wrap2, A, B)
+
+ /// Creates a new `Func` from the given Rust closure, which takes 3
+ /// arguments.
+ ///
+ /// For more information about this function, see [`Func::wrap1`].
+ (wrap3, A, B, C)
+
+ /// Creates a new `Func` from the given Rust closure, which takes 4
+ /// arguments.
+ ///
+ /// For more information about this function, see [`Func::wrap1`].
+ (wrap4, A, B, C, D)
+
+ /// Creates a new `Func` from the given Rust closure, which takes 5
+ /// arguments.
+ ///
+ /// For more information about this function, see [`Func::wrap1`].
+ (wrap5, A, B, C, D, E)
+ }
+
+ fn from_wrapped(
+ store: &Store,
+ ty: FuncType,
+ callable: Rc,
+ ) -> Func {
+ Func {
+ _store: store.clone(),
+ callable,
+ ty,
+ }
+ }
+
+ /// Returns the underlying wasm type that this `Func` has.
+ pub fn ty(&self) -> &FuncType {
+ &self.ty
+ }
+
+ /// Returns the number of parameters that this function takes.
+ pub fn param_arity(&self) -> usize {
+ self.ty.params().len()
+ }
+
+ /// Returns the number of results this function produces.
+ pub fn result_arity(&self) -> usize {
+ self.ty.results().len()
+ }
+
+ /// Invokes this function with the `params` given, returning the results and
+ /// any trap, if one occurs.
+ ///
+ /// The `params` here must match the type signature of this `Func`, or a
+ /// trap will occur. If a trap occurs while executing this function, then a
+ /// trap will also be returned.
+ ///
+ /// This function should not panic unless the underlying function itself
+ /// initiates a panic.
+ pub fn call(&self, params: &[Val]) -> Result, Trap> {
+ let mut results = vec![Val::null(); self.result_arity()];
+ self.callable.call(params, &mut results)?;
+ Ok(results.into_boxed_slice())
+ }
+
+ pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
+ self.callable.wasmtime_export()
+ }
+
+ pub(crate) fn from_wasmtime_function(
+ export: wasmtime_runtime::Export,
+ store: &Store,
+ instance_handle: InstanceHandle,
+ ) -> Self {
+ // This is only called with `Export::Function`, and since it's coming
+ // from wasmtime_runtime itself we should support all the types coming
+ // out of it, so assert such here.
+ let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
+ FuncType::from_wasmtime_signature(signature.clone())
+ .expect("core wasm signature should be supported")
+ } else {
+ panic!("expected function export")
+ };
+ let callable = WasmtimeFn::new(store, instance_handle, export);
+ Func::from_wrapped(store, ty, Rc::new(callable))
+ }
+}
+
+impl fmt::Debug for Func {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Func")
+ }
+}
+
+/// A trait implemented for types which can be arguments to closures passed to
+/// [`Func::wrap1`] and friends.
+///
+/// This trait should not be implemented by user types. This trait may change at
+/// any time internally. The types which implement this trait, however, are
+/// stable over time.
+///
+/// For more information see [`Func::wrap1`]
+pub trait WasmArg {
+ #[doc(hidden)]
+ fn push(dst: &mut Vec);
+}
+
+impl WasmArg for () {
+ fn push(_dst: &mut Vec) {}
+}
+
+impl WasmArg for i32 {
+ fn push(dst: &mut Vec) {
+ dst.push(ValType::I32);
+ }
+}
+
+impl WasmArg for i64 {
+ fn push(dst: &mut Vec) {
+ dst.push(ValType::I64);
+ }
+}
+
+impl WasmArg for f32 {
+ fn push(dst: &mut Vec) {
+ dst.push(ValType::F32);
+ }
+}
+
+impl WasmArg for f64 {
+ fn push(dst: &mut Vec) {
+ dst.push(ValType::F64);
+ }
+}
+
+/// A trait implemented for types which can be returned from closures passed to
+/// [`Func::wrap1`] and friends.
+///
+/// This trait should not be implemented by user types. This trait may change at
+/// any time internally. The types which implement this trait, however, are
+/// stable over time.
+///
+/// For more information see [`Func::wrap1`]
+pub trait WasmRet {
+ #[doc(hidden)]
+ type Abi;
+ #[doc(hidden)]
+ fn push(dst: &mut Vec);
+ #[doc(hidden)]
+ fn into_abi(self) -> Self::Abi;
+}
+
+impl WasmRet for T {
+ type Abi = T;
+ fn push(dst: &mut Vec) {
+ T::push(dst)
+ }
+
+ #[inline]
+ fn into_abi(self) -> Self::Abi {
+ self
+ }
+}
+
+impl WasmRet for Result {
+ type Abi = T;
+ fn push(dst: &mut Vec) {
+ T::push(dst)
+ }
+
+ #[inline]
+ fn into_abi(self) -> Self::Abi {
+ match self {
+ Ok(val) => return val,
+ Err(trap) => handle_trap(trap),
+ }
+
+ fn handle_trap(trap: Trap) -> ! {
+ unsafe { wasmtime_runtime::raise_user_trap(Box::new(trap)) }
+ }
+ }
+}
diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
index 0858b7e971..d68d5802cc 100644
--- a/crates/api/src/lib.rs
+++ b/crates/api/src/lib.rs
@@ -10,6 +10,7 @@
mod callable;
mod externals;
+mod func;
mod instance;
mod module;
mod r#ref;
@@ -21,6 +22,7 @@ mod values;
pub use crate::callable::Callable;
pub use crate::externals::*;
+pub use crate::func::{Func, WasmArg, WasmRet};
pub use crate::instance::Instance;
pub use crate::module::Module;
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
diff --git a/crates/api/src/trampoline/func.rs b/crates/api/src/trampoline/func.rs
index 446e6f8591..e08feda3be 100644
--- a/crates/api/src/trampoline/func.rs
+++ b/crates/api/src/trampoline/func.rs
@@ -4,6 +4,7 @@ use super::create_handle::create_handle;
use super::trap::TrapSink;
use crate::{Callable, FuncType, Store, Trap, Val};
use anyhow::{bail, Result};
+use std::any::Any;
use std::cmp;
use std::convert::TryFrom;
use std::panic::{self, AssertUnwindSafe};
@@ -325,3 +326,35 @@ pub fn create_handle_with_function(
Box::new(trampoline_state),
)
}
+
+pub unsafe fn create_handle_with_raw_function(
+ ft: &FuncType,
+ func: *const VMFunctionBody,
+ store: &Store,
+ state: Box,
+) -> Result {
+ let isa = {
+ let isa_builder = native::builder();
+ let flag_builder = settings::builder();
+ isa_builder.finish(settings::Flags::new(flag_builder))
+ };
+
+ let pointer_type = isa.pointer_type();
+ let sig = match ft.get_wasmtime_signature(pointer_type) {
+ Some(sig) => sig.clone(),
+ None => bail!("not a supported core wasm signature {:?}", ft),
+ };
+
+ let mut module = Module::new();
+ let mut finished_functions: PrimaryMap =
+ PrimaryMap::new();
+
+ let sig_id = module.signatures.push(sig.clone());
+ let func_id = module.functions.push(sig_id);
+ module
+ .exports
+ .insert("trampoline".to_string(), Export::Function(func_id));
+ finished_functions.push(func);
+
+ create_handle(module, Some(store), finished_functions, state)
+}
diff --git a/crates/api/src/trampoline/mod.rs b/crates/api/src/trampoline/mod.rs
index ffb224a232..98ac5f119b 100644
--- a/crates/api/src/trampoline/mod.rs
+++ b/crates/api/src/trampoline/mod.rs
@@ -13,7 +13,9 @@ use self::memory::create_handle_with_memory;
use self::table::create_handle_with_table;
use super::{Callable, FuncType, GlobalType, MemoryType, Store, TableType, Val};
use anyhow::Result;
+use std::any::Any;
use std::rc::Rc;
+use wasmtime_runtime::VMFunctionBody;
pub use self::global::GlobalState;
@@ -27,6 +29,20 @@ pub fn generate_func_export(
Ok((instance, export))
}
+/// Note that this is `unsafe` since `func` must be a valid function pointer and
+/// have a signature which matches `ft`, otherwise the returned
+/// instance/export/etc may exhibit undefined behavior.
+pub unsafe fn generate_raw_func_export(
+ ft: &FuncType,
+ func: *const VMFunctionBody,
+ store: &Store,
+ state: Box,
+) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
+ let instance = func::create_handle_with_raw_function(ft, func, store, state)?;
+ let export = instance.lookup("trampoline").expect("trampoline export");
+ Ok((instance, export))
+}
+
pub fn generate_global_export(
gt: &GlobalType,
val: Val,
diff --git a/crates/api/src/values.rs b/crates/api/src/values.rs
index 446a1c31a2..b61f11124f 100644
--- a/crates/api/src/values.rs
+++ b/crates/api/src/values.rs
@@ -1,7 +1,5 @@
-use crate::externals::Func;
use crate::r#ref::AnyRef;
-use crate::runtime::Store;
-use crate::types::ValType;
+use crate::{Func, Store, ValType};
use anyhow::{bail, Result};
use std::ptr;
use wasmtime_environ::ir;
diff --git a/crates/api/tests/func.rs b/crates/api/tests/func.rs
new file mode 100644
index 0000000000..c7eb15eab8
--- /dev/null
+++ b/crates/api/tests/func.rs
@@ -0,0 +1,203 @@
+use anyhow::Result;
+use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+use wasmtime::{Func, Instance, Module, Store, Trap, ValType};
+
+#[test]
+fn func_constructors() {
+ let store = Store::default();
+ Func::wrap0(&store, || {});
+ Func::wrap1(&store, |_: i32| {});
+ Func::wrap2(&store, |_: i32, _: i64| {});
+ Func::wrap2(&store, |_: f32, _: f64| {});
+ Func::wrap0(&store, || -> i32 { 0 });
+ Func::wrap0(&store, || -> i64 { 0 });
+ Func::wrap0(&store, || -> f32 { 0.0 });
+ Func::wrap0(&store, || -> f64 { 0.0 });
+
+ Func::wrap0(&store, || -> Result<(), Trap> { loop {} });
+ Func::wrap0(&store, || -> Result { loop {} });
+ Func::wrap0(&store, || -> Result { loop {} });
+ Func::wrap0(&store, || -> Result { loop {} });
+ Func::wrap0(&store, || -> Result { loop {} });
+}
+
+#[test]
+fn dtor_runs() {
+ static HITS: AtomicUsize = AtomicUsize::new(0);
+
+ struct A;
+
+ impl Drop for A {
+ fn drop(&mut self) {
+ HITS.fetch_add(1, SeqCst);
+ }
+ }
+
+ let store = Store::default();
+ let a = A;
+ assert_eq!(HITS.load(SeqCst), 0);
+ Func::wrap0(&store, move || {
+ drop(&a);
+ });
+ assert_eq!(HITS.load(SeqCst), 1);
+}
+
+#[test]
+fn dtor_delayed() -> Result<()> {
+ static HITS: AtomicUsize = AtomicUsize::new(0);
+
+ struct A;
+
+ impl Drop for A {
+ fn drop(&mut self) {
+ HITS.fetch_add(1, SeqCst);
+ }
+ }
+
+ let store = Store::default();
+ let a = A;
+ let func = Func::wrap0(&store, move || drop(&a));
+
+ assert_eq!(HITS.load(SeqCst), 0);
+ let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
+ let module = Module::new(&store, &wasm)?;
+ let instance = Instance::new(&module, &[func.into()])?;
+ assert_eq!(HITS.load(SeqCst), 0);
+ drop(instance);
+ assert_eq!(HITS.load(SeqCst), 1);
+ Ok(())
+}
+
+#[test]
+fn signatures_match() {
+ let store = Store::default();
+
+ let f = Func::wrap0(&store, || {});
+ assert_eq!(f.ty().params(), &[]);
+ assert_eq!(f.ty().results(), &[]);
+
+ let f = Func::wrap0(&store, || -> i32 { loop {} });
+ assert_eq!(f.ty().params(), &[]);
+ assert_eq!(f.ty().results(), &[ValType::I32]);
+
+ let f = Func::wrap0(&store, || -> i64 { loop {} });
+ assert_eq!(f.ty().params(), &[]);
+ assert_eq!(f.ty().results(), &[ValType::I64]);
+
+ let f = Func::wrap0(&store, || -> f32 { loop {} });
+ assert_eq!(f.ty().params(), &[]);
+ assert_eq!(f.ty().results(), &[ValType::F32]);
+
+ let f = Func::wrap0(&store, || -> f64 { loop {} });
+ assert_eq!(f.ty().params(), &[]);
+ assert_eq!(f.ty().results(), &[ValType::F64]);
+
+ let f = Func::wrap5(&store, |_: f32, _: f64, _: i32, _: i64, _: i32| -> f64 {
+ loop {}
+ });
+ assert_eq!(
+ f.ty().params(),
+ &[
+ ValType::F32,
+ ValType::F64,
+ ValType::I32,
+ ValType::I64,
+ ValType::I32
+ ]
+ );
+ assert_eq!(f.ty().results(), &[ValType::F64]);
+}
+
+#[test]
+fn import_works() -> Result<()> {
+ static HITS: AtomicUsize = AtomicUsize::new(0);
+
+ let wasm = wat::parse_str(
+ r#"
+ (import "" "" (func))
+ (import "" "" (func (param i32) (result i32)))
+ (import "" "" (func (param i32) (param i64)))
+ (import "" "" (func (param i32 i64 i32 f32 f64)))
+
+ (func $foo
+ call 0
+ i32.const 0
+ call 1
+ i32.const 1
+ i32.add
+ i64.const 3
+ call 2
+
+ i32.const 100
+ i64.const 200
+ i32.const 300
+ f32.const 400
+ f64.const 500
+ call 3
+ )
+ (start $foo)
+ "#,
+ )?;
+ let store = Store::default();
+ let module = Module::new(&store, &wasm)?;
+ Instance::new(
+ &module,
+ &[
+ Func::wrap0(&store, || {
+ assert_eq!(HITS.fetch_add(1, SeqCst), 0);
+ })
+ .into(),
+ Func::wrap1(&store, |x: i32| -> i32 {
+ assert_eq!(x, 0);
+ assert_eq!(HITS.fetch_add(1, SeqCst), 1);
+ 1
+ })
+ .into(),
+ Func::wrap2(&store, |x: i32, y: i64| {
+ assert_eq!(x, 2);
+ assert_eq!(y, 3);
+ assert_eq!(HITS.fetch_add(1, SeqCst), 2);
+ })
+ .into(),
+ Func::wrap5(&store, |a: i32, b: i64, c: i32, d: f32, e: f64| {
+ assert_eq!(a, 100);
+ assert_eq!(b, 200);
+ assert_eq!(c, 300);
+ assert_eq!(d, 400.0);
+ assert_eq!(e, 500.0);
+ assert_eq!(HITS.fetch_add(1, SeqCst), 3);
+ })
+ .into(),
+ ],
+ )?;
+ Ok(())
+}
+
+#[test]
+fn trap_smoke() {
+ let store = Store::default();
+ let f = Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("test")) });
+ let err = f.call(&[]).unwrap_err();
+ assert_eq!(err.message(), "test");
+}
+
+#[test]
+fn trap_import() -> Result<()> {
+ let wasm = wat::parse_str(
+ r#"
+ (import "" "" (func))
+ (start 0)
+ "#,
+ )?;
+ let store = Store::default();
+ let module = Module::new(&store, &wasm)?;
+ let trap = Instance::new(
+ &module,
+ &[Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
+ )
+ .err()
+ .unwrap()
+ .downcast::()?;
+ assert_eq!(trap.message(), "foo");
+ Ok(())
+}
diff --git a/crates/api/tests/traps.rs b/crates/api/tests/traps.rs
index 610760a324..255071b89b 100644
--- a/crates/api/tests/traps.rs
+++ b/crates/api/tests/traps.rs
@@ -260,7 +260,9 @@ fn rust_panic_import() -> Result<()> {
r#"
(module $a
(import "" "" (func $foo))
+ (import "" "" (func $bar))
(func (export "foo") call $foo)
+ (func (export "bar") call $bar)
)
"#,
)?;
@@ -268,13 +270,29 @@ fn rust_panic_import() -> Result<()> {
let module = Module::new(&store, &binary)?;
let sig = FuncType::new(Box::new([]), Box::new([]));
let func = Func::new(&store, sig, Rc::new(Panic));
- let instance = Instance::new(&module, &[func.into()])?;
+ let instance = Instance::new(
+ &module,
+ &[
+ func.into(),
+ Func::wrap0(&store, || panic!("this is another panic")).into(),
+ ],
+ )?;
let func = instance.exports()[0].func().unwrap().clone();
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(func.call(&[]));
}))
.unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
+
+ let func = instance.exports()[1].func().unwrap().clone();
+ let err = panic::catch_unwind(AssertUnwindSafe(|| {
+ drop(func.call(&[]));
+ }))
+ .unwrap_err();
+ assert_eq!(
+ err.downcast_ref::<&'static str>(),
+ Some(&"this is another panic")
+ );
Ok(())
}
@@ -306,6 +324,16 @@ fn rust_panic_start_function() -> Result<()> {
}))
.unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
+
+ let func = Func::wrap0(&store, || panic!("this is another panic"));
+ let err = panic::catch_unwind(AssertUnwindSafe(|| {
+ drop(Instance::new(&module, &[func.into()]));
+ }))
+ .unwrap_err();
+ assert_eq!(
+ err.downcast_ref::<&'static str>(),
+ Some(&"this is another panic")
+ );
Ok(())
}
diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs
index a8109b86e5..9eddacd3fb 100644
--- a/crates/runtime/src/lib.rs
+++ b/crates/runtime/src/lib.rs
@@ -43,6 +43,7 @@ pub use crate::instance::{InstanceHandle, InstantiationError, LinkError};
pub use crate::jit_int::GdbJitImageRegistration;
pub use crate::mmap::Mmap;
pub use crate::sig_registry::SignatureRegistry;
+pub use crate::trap_registry::TrapDescription;
pub use crate::trap_registry::{get_mut_trap_registry, get_trap_registry, TrapRegistrationGuard};
pub use crate::traphandlers::resume_panic;
pub use crate::traphandlers::{raise_user_trap, wasmtime_call, wasmtime_call_trampoline, Trap};
diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs
index 3f6d60a01b..4fcfe190f2 100644
--- a/crates/wast/src/spectest.rs
+++ b/crates/wast/src/spectest.rs
@@ -1,77 +1,35 @@
-use anyhow::Result;
use std::collections::HashMap;
-use std::rc::Rc;
use wasmtime::*;
-struct MyCall(F);
-
-impl Callable for MyCall
-where
- F: Fn(&[Val], &mut [Val]) -> Result<(), Trap>,
-{
- fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
- (self.0)(params, results)
- }
-}
-
-fn wrap(
- store: &Store,
- ty: FuncType,
- callable: impl Fn(&[Val], &mut [Val]) -> Result<(), Trap> + 'static,
-) -> Func {
- Func::new(store, ty, Rc::new(MyCall(callable)))
-}
-
/// Return an instance implementing the "spectest" interface used in the
/// spec testsuite.
pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
let mut ret = HashMap::new();
- let ty = FuncType::new(Box::new([]), Box::new([]));
- let func = wrap(store, ty, |_params, _results| Ok(()));
+ let func = Func::wrap0(store, || {});
ret.insert("print", Extern::Func(func));
- let ty = FuncType::new(Box::new([ValType::I32]), Box::new([]));
- let func = wrap(store, ty, |params, _results| {
- println!("{}: i32", params[0].unwrap_i32());
- Ok(())
- });
+ let func = Func::wrap1(store, |val: i32| println!("{}: i32", val));
ret.insert("print_i32", Extern::Func(func));
- let ty = FuncType::new(Box::new([ValType::I64]), Box::new([]));
- let func = wrap(store, ty, |params, _results| {
- println!("{}: i64", params[0].unwrap_i64());
- Ok(())
- });
+ let func = Func::wrap1(store, |val: i64| println!("{}: i64", val));
ret.insert("print_i64", Extern::Func(func));
- let ty = FuncType::new(Box::new([ValType::F32]), Box::new([]));
- let func = wrap(store, ty, |params, _results| {
- println!("{}: f32", params[0].unwrap_f32());
- Ok(())
- });
+ let func = Func::wrap1(store, |val: f32| println!("{}: f32", val));
ret.insert("print_f32", Extern::Func(func));
- let ty = FuncType::new(Box::new([ValType::F64]), Box::new([]));
- let func = wrap(store, ty, |params, _results| {
- println!("{}: f64", params[0].unwrap_f64());
- Ok(())
- });
+ let func = Func::wrap1(store, |val: f64| println!("{}: f64", val));
ret.insert("print_f64", Extern::Func(func));
- let ty = FuncType::new(Box::new([ValType::I32, ValType::F32]), Box::new([]));
- let func = wrap(store, ty, |params, _results| {
- println!("{}: i32", params[0].unwrap_i32());
- println!("{}: f32", params[1].unwrap_f32());
- Ok(())
+ let func = Func::wrap2(store, |i: i32, f: f32| {
+ println!("{}: i32", i);
+ println!("{}: f32", f);
});
ret.insert("print_i32_f32", Extern::Func(func));
- let ty = FuncType::new(Box::new([ValType::F64, ValType::F64]), Box::new([]));
- let func = wrap(store, ty, |params, _results| {
- println!("{}: f64", params[0].unwrap_f64());
- println!("{}: f64", params[1].unwrap_f64());
- Ok(())
+ let func = Func::wrap2(store, |f1: f64, f2: f64| {
+ println!("{}: f64", f1);
+ println!("{}: f64", f2);
});
ret.insert("print_f64_f64", Extern::Func(func));