Add a first-class way of accessing caller's exports (#1290)
* Add a first-class way of accessing caller's exports This commit is a continuation of #1237 and updates the API of `Func` to allow defining host functions which have easy access to a caller's memory in particular. The new APIs look like so: * The `Func::wrap*` family of functions was condensed into one `Func::wrap` function. * The ABI layer of conversions in `WasmTy` were removed * An optional `Caller<'_>` argument can be at the front of all host-defined functions now. The old way the wasi bindings looked up memory has been removed and is now replaced with the `Caller` type. The `Caller` type has a `get_export` method on it which allows looking up a caller's export by name, allowing you to get access to the caller's memory easily, and even during instantiation. * Add a temporary note * Move some docs
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
use crate::callable::{NativeCallable, WasmtimeFn, WrappedCallable};
|
||||
use crate::{Callable, FuncType, Store, Trap, Val, ValType};
|
||||
use crate::{Callable, Extern, FuncType, Memory, Store, Trap, Val, ValType};
|
||||
use anyhow::{ensure, Context as _};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
||||
use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
||||
|
||||
/// A WebAssembly function which can be called.
|
||||
///
|
||||
@@ -59,7 +59,7 @@ use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// You can also use the [`wrap*` family of functions](Func::wrap1) to create a
|
||||
/// You can also use the [`wrap` function](Func::wrap) to create a
|
||||
/// `Func`
|
||||
///
|
||||
/// ```
|
||||
@@ -69,7 +69,7 @@ use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
||||
///
|
||||
/// // Create a custom `Func` which can execute arbitrary code inside of the
|
||||
/// // closure.
|
||||
/// let add = Func::wrap2(&store, |a: i32, b: i32| -> i32 { a + b });
|
||||
/// let add = Func::wrap(&store, |a: i32, b: i32| -> i32 { a + b });
|
||||
///
|
||||
/// // Next we can hook that up to a wasm module which uses it.
|
||||
/// let module = Module::new(
|
||||
@@ -148,95 +148,6 @@ pub struct Func {
|
||||
ty: FuncType,
|
||||
}
|
||||
|
||||
macro_rules! wrappers {
|
||||
($(
|
||||
$(#[$doc:meta])*
|
||||
($name:ident $(,$args:ident)*)
|
||||
)*) => ($(
|
||||
$(#[$doc])*
|
||||
pub fn $name<F, $($args,)* R>(store: &Store, func: F) -> Func
|
||||
where
|
||||
F: Fn($($args),*) -> R + 'static,
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn shim<F, $($args,)* R>(
|
||||
vmctx: *mut VMContext,
|
||||
_caller_vmctx: *mut VMContext,
|
||||
$($args: $args::Abi,)*
|
||||
) -> R::Abi
|
||||
where
|
||||
F: Fn($($args),*) -> R + 'static,
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
let ret = {
|
||||
let instance = InstanceHandle::from_vmctx(vmctx);
|
||||
let func = instance.host_state().downcast_ref::<F>().expect("state");
|
||||
panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
func($($args::from_abi(_caller_vmctx, $args)),*)
|
||||
}))
|
||||
};
|
||||
match ret {
|
||||
Ok(ret) => ret.into_abi(),
|
||||
Err(panic) => wasmtime_runtime::resume_panic(panic),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn trampoline<F, $($args,)* R>(
|
||||
callee_vmctx: *mut VMContext,
|
||||
caller_vmctx: *mut VMContext,
|
||||
ptr: *const VMFunctionBody,
|
||||
args: *mut u128,
|
||||
)
|
||||
where
|
||||
F: Fn($($args),*) -> R + 'static,
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
let ptr = mem::transmute::<
|
||||
*const VMFunctionBody,
|
||||
unsafe extern "C" fn(
|
||||
*mut VMContext,
|
||||
*mut VMContext,
|
||||
$($args::Abi,)*
|
||||
) -> R::Abi,
|
||||
>(ptr);
|
||||
|
||||
let mut _next = args as *const u128;
|
||||
$(let $args = $args::load(&mut _next);)*
|
||||
|
||||
let ret = ptr(callee_vmctx, caller_vmctx, $($args),*);
|
||||
R::store(ret, args);
|
||||
}
|
||||
|
||||
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 trampoline = trampoline::<F, $($args,)* R>;
|
||||
let (instance, export) = crate::trampoline::generate_raw_func_export(
|
||||
&ty,
|
||||
std::slice::from_raw_parts_mut(
|
||||
shim::<F, $($args,)* R> as *mut _,
|
||||
0,
|
||||
),
|
||||
trampoline,
|
||||
store,
|
||||
Box::new(func),
|
||||
)
|
||||
.expect("failed to generate export");
|
||||
let callable = Rc::new(WasmtimeFn::new(store, instance, export, trampoline));
|
||||
Func::from_wrapped(store, ty, callable)
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
macro_rules! getters {
|
||||
($(
|
||||
$(#[$doc:meta])*
|
||||
@@ -277,15 +188,15 @@ macro_rules! getters {
|
||||
unsafe extern "C" fn(
|
||||
*mut VMContext,
|
||||
*mut VMContext,
|
||||
$($args::Abi,)*
|
||||
) -> R::Abi,
|
||||
$($args,)*
|
||||
) -> R,
|
||||
>(f.address);
|
||||
let mut ret = None;
|
||||
$(let $args = $args.into_abi();)*
|
||||
wasmtime_runtime::catch_traps(f.vmctx, || {
|
||||
ret = Some(fnptr(f.vmctx, ptr::null_mut(), $($args,)*));
|
||||
}).map_err(Trap::from_jit)?;
|
||||
Ok(R::from_abi(f.vmctx, ret.unwrap()))
|
||||
Ok(ret.unwrap())
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -313,220 +224,194 @@ impl Func {
|
||||
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, Trap>` | `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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// First up we can see how simple wasm imports can be implemented, such
|
||||
/// as a function that adds its two arguments and returns the result.
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let add = Func::wrap2(&store, |a: i32, b: i32| a + b);
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $add (param i32 i32) (result i32)))
|
||||
/// (func (export "foo") (param i32 i32) (result i32)
|
||||
/// local.get 0
|
||||
/// local.get 1
|
||||
/// call $add))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[add.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get2::<i32, i32, i32>()?;
|
||||
/// assert_eq!(foo(1, 2)?, 3);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// We can also do the same thing, but generate a trap if the addition
|
||||
/// overflows:
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let add = Func::wrap2(&store, |a: i32, b: i32| {
|
||||
/// match a.checked_add(b) {
|
||||
/// Some(i) => Ok(i),
|
||||
/// None => Err(Trap::new("overflow")),
|
||||
/// }
|
||||
/// });
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $add (param i32 i32) (result i32)))
|
||||
/// (func (export "foo") (param i32 i32) (result i32)
|
||||
/// local.get 0
|
||||
/// local.get 1
|
||||
/// call $add))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[add.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get2::<i32, i32, i32>()?;
|
||||
/// assert_eq!(foo(1, 2)?, 3);
|
||||
/// assert!(foo(i32::max_value(), 1).is_err());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// And don't forget all the wasm types are supported!
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let debug = Func::wrap4(&store, |a: i32, b: f32, c: i64, d: f64| {
|
||||
/// println!("a={}", a);
|
||||
/// println!("b={}", b);
|
||||
/// println!("c={}", c);
|
||||
/// println!("d={}", d);
|
||||
/// });
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $debug (param i32 f32 i64 f64)))
|
||||
/// (func (export "foo")
|
||||
/// i32.const 1
|
||||
/// f32.const 2
|
||||
/// i64.const 3
|
||||
/// f64.const 4
|
||||
/// call $debug))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[debug.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get0::<()>()?;
|
||||
/// foo()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
(wrap1, A1)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 2
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap2, A1, A2)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 3
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap3, A1, A2, A3)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 4
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap4, A1, A2, A3, A4)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 5
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap5, A1, A2, A3, A4, A5)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 6
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap6, A1, A2, A3, A4, A5, A6)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 7
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap7, A1, A2, A3, A4, A5, A6, A7)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 8
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap8, A1, A2, A3, A4, A5, A6, A7, A8)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 9
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap9, A1, A2, A3, A4, A5, A6, A7, A8, A9)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 10
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 11
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 12
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 13
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 14
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure, which takes 15
|
||||
/// arguments.
|
||||
///
|
||||
/// For more information about this function, see [`Func::wrap1`].
|
||||
(wrap15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)
|
||||
/// Creates a new `Func` from the given Rust closure.
|
||||
///
|
||||
/// 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, Trap>` | `T` | function may trap |
|
||||
///
|
||||
/// At this time multi-value returns are not supported, and supporting this
|
||||
/// is the subject of [#1178].
|
||||
///
|
||||
/// [#1178]: https://github.com/bytecodealliance/wasmtime/issues/1178
|
||||
///
|
||||
/// Finally you can also optionally take [`Caller`] as the first argument of
|
||||
/// your closure. If inserted then you're able to inspect the caller's
|
||||
/// state, for example the [`Memory`] it has exported so you can read what
|
||||
/// pointers point to.
|
||||
///
|
||||
/// Note that when using this API, 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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// First up we can see how simple wasm imports can be implemented, such
|
||||
/// as a function that adds its two arguments and returns the result.
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let add = Func::wrap(&store, |a: i32, b: i32| a + b);
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $add (param i32 i32) (result i32)))
|
||||
/// (func (export "foo") (param i32 i32) (result i32)
|
||||
/// local.get 0
|
||||
/// local.get 1
|
||||
/// call $add))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[add.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get2::<i32, i32, i32>()?;
|
||||
/// assert_eq!(foo(1, 2)?, 3);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// We can also do the same thing, but generate a trap if the addition
|
||||
/// overflows:
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let add = Func::wrap(&store, |a: i32, b: i32| {
|
||||
/// match a.checked_add(b) {
|
||||
/// Some(i) => Ok(i),
|
||||
/// None => Err(Trap::new("overflow")),
|
||||
/// }
|
||||
/// });
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $add (param i32 i32) (result i32)))
|
||||
/// (func (export "foo") (param i32 i32) (result i32)
|
||||
/// local.get 0
|
||||
/// local.get 1
|
||||
/// call $add))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[add.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get2::<i32, i32, i32>()?;
|
||||
/// assert_eq!(foo(1, 2)?, 3);
|
||||
/// assert!(foo(i32::max_value(), 1).is_err());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// And don't forget all the wasm types are supported!
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let debug = Func::wrap(&store, |a: i32, b: f32, c: i64, d: f64| {
|
||||
/// println!("a={}", a);
|
||||
/// println!("b={}", b);
|
||||
/// println!("c={}", c);
|
||||
/// println!("d={}", d);
|
||||
/// });
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $debug (param i32 f32 i64 f64)))
|
||||
/// (func (export "foo")
|
||||
/// i32.const 1
|
||||
/// f32.const 2
|
||||
/// i64.const 3
|
||||
/// f64.const 4
|
||||
/// call $debug))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[debug.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get0::<()>()?;
|
||||
/// foo()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Finally if you want to get really fancy you can also implement
|
||||
/// imports that read/write wasm module's memory
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let log_str = Func::wrap(&store, |caller: Caller<'_>, ptr: i32, len: i32| {
|
||||
/// let mem = match caller.get_export("memory") {
|
||||
/// Some(Extern::Memory(mem)) => mem,
|
||||
/// _ => return Err(Trap::new("failed to find host memory")),
|
||||
/// };
|
||||
///
|
||||
/// // We're reading raw wasm memory here so we need `unsafe`. Note
|
||||
/// // though that this should be safe because we don't reenter wasm
|
||||
/// // while we're reading wasm memory, nor should we clash with
|
||||
/// // any other memory accessors (assuming they're well-behaved
|
||||
/// // too).
|
||||
/// unsafe {
|
||||
/// let data = mem.data_unchecked()
|
||||
/// .get(ptr as u32 as usize..)
|
||||
/// .and_then(|arr| arr.get(..len as u32 as usize));
|
||||
/// let string = match data {
|
||||
/// Some(data) => match str::from_utf8(data) {
|
||||
/// Ok(s) => s,
|
||||
/// Err(_) => return Err(Trap::new("invalid utf-8")),
|
||||
/// },
|
||||
/// None => return Err(Trap::new("pointer/length out of bounds")),
|
||||
/// };
|
||||
/// assert_eq!(string, "Hello, world!");
|
||||
/// println!("{}", string);
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (import "" "" (func $log_str (param i32 i32)))
|
||||
/// (func (export "foo")
|
||||
/// i32.const 4 ;; ptr
|
||||
/// i32.const 13 ;; len
|
||||
/// call $log_str)
|
||||
/// (memory (export "memory") 1)
|
||||
/// (data (i32.const 4) "Hello, world!"))
|
||||
/// "#,
|
||||
/// )?;
|
||||
/// let instance = Instance::new(&module, &[log_str.into()])?;
|
||||
/// let foo = instance.exports()[0].func().unwrap().get0::<()>()?;
|
||||
/// foo()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn wrap<Params, Results>(store: &Store, func: impl IntoFunc<Params, Results>) -> Func {
|
||||
func.into_func(store)
|
||||
}
|
||||
|
||||
fn from_wrapped(
|
||||
@@ -634,7 +519,7 @@ impl Func {
|
||||
/// instead this function jumps directly into JIT code.
|
||||
///
|
||||
/// For more information about which Rust types match up to which wasm
|
||||
/// types, see the documentation on [`Func::wrap1`].
|
||||
/// types, see the documentation on [`Func::wrap`].
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
@@ -746,52 +631,36 @@ impl fmt::Debug for Func {
|
||||
}
|
||||
|
||||
/// A trait implemented for types which can be arguments to closures passed to
|
||||
/// [`Func::wrap1`] and friends.
|
||||
/// [`Func::wrap`] 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 WasmTy {
|
||||
#[doc(hidden)]
|
||||
type Abi: Copy;
|
||||
/// For more information see [`Func::wrap`]
|
||||
pub unsafe trait WasmTy: Copy {
|
||||
#[doc(hidden)]
|
||||
fn push(dst: &mut Vec<ValType>);
|
||||
#[doc(hidden)]
|
||||
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn from_abi(vmctx: *mut VMContext, abi: Self::Abi) -> Self;
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self;
|
||||
#[doc(hidden)]
|
||||
fn into_abi(self) -> Self::Abi;
|
||||
#[doc(hidden)]
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi;
|
||||
#[doc(hidden)]
|
||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128);
|
||||
unsafe fn store(abi: Self, ptr: *mut u128);
|
||||
}
|
||||
|
||||
impl WasmTy for () {
|
||||
type Abi = ();
|
||||
unsafe impl WasmTy for () {
|
||||
fn push(_dst: &mut Vec<ValType>) {}
|
||||
fn matches(_tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
unsafe fn load(_ptr: &mut *const u128) -> Self {}
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn load(_ptr: &mut *const u128) -> Self::Abi {}
|
||||
#[inline]
|
||||
unsafe fn store(_abi: Self::Abi, _ptr: *mut u128) {}
|
||||
unsafe fn store(_abi: Self, _ptr: *mut u128) {}
|
||||
}
|
||||
|
||||
impl WasmTy for i32 {
|
||||
type Abi = Self;
|
||||
unsafe impl WasmTy for i32 {
|
||||
fn push(dst: &mut Vec<ValType>) {
|
||||
dst.push(ValType::I32);
|
||||
}
|
||||
@@ -805,27 +674,18 @@ impl WasmTy for i32 {
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||
let ret = **ptr as Self;
|
||||
*ptr = (*ptr).add(1);
|
||||
return ret;
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
||||
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||
*ptr = abi as u128;
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmTy for i64 {
|
||||
type Abi = Self;
|
||||
unsafe impl WasmTy for i64 {
|
||||
fn push(dst: &mut Vec<ValType>) {
|
||||
dst.push(ValType::I64);
|
||||
}
|
||||
@@ -839,27 +699,18 @@ impl WasmTy for i64 {
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||
let ret = **ptr as Self;
|
||||
*ptr = (*ptr).add(1);
|
||||
return ret;
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
||||
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||
*ptr = abi as u128;
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmTy for f32 {
|
||||
type Abi = Self;
|
||||
unsafe impl WasmTy for f32 {
|
||||
fn push(dst: &mut Vec<ValType>) {
|
||||
dst.push(ValType::F32);
|
||||
}
|
||||
@@ -873,27 +724,18 @@ impl WasmTy for f32 {
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||
let ret = f32::from_bits(**ptr as u32);
|
||||
*ptr = (*ptr).add(1);
|
||||
return ret;
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
||||
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||
*ptr = abi.to_bits() as u128;
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmTy for f64 {
|
||||
type Abi = Self;
|
||||
unsafe impl WasmTy for f64 {
|
||||
fn push(dst: &mut Vec<ValType>) {
|
||||
dst.push(ValType::F64);
|
||||
}
|
||||
@@ -907,34 +749,26 @@ impl WasmTy for f64 {
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
||||
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||
let ret = f64::from_bits(**ptr as u64);
|
||||
*ptr = (*ptr).add(1);
|
||||
return ret;
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
||||
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||
*ptr = abi.to_bits() as u128;
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait implemented for types which can be returned from closures passed to
|
||||
/// [`Func::wrap1`] and friends.
|
||||
/// [`Func::wrap`] 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 {
|
||||
/// For more information see [`Func::wrap`]
|
||||
pub unsafe trait WasmRet {
|
||||
#[doc(hidden)]
|
||||
type Abi;
|
||||
#[doc(hidden)]
|
||||
@@ -947,8 +781,8 @@ pub trait WasmRet {
|
||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128);
|
||||
}
|
||||
|
||||
impl<T: WasmTy> WasmRet for T {
|
||||
type Abi = T::Abi;
|
||||
unsafe impl<T: WasmTy> WasmRet for T {
|
||||
type Abi = T;
|
||||
fn push(dst: &mut Vec<ValType>) {
|
||||
T::push(dst)
|
||||
}
|
||||
@@ -959,7 +793,7 @@ impl<T: WasmTy> WasmRet for T {
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
T::into_abi(self)
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -968,8 +802,8 @@ impl<T: WasmTy> WasmRet for T {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WasmTy> WasmRet for Result<T, Trap> {
|
||||
type Abi = T::Abi;
|
||||
unsafe impl<T: WasmTy> WasmRet for Result<T, Trap> {
|
||||
type Abi = T;
|
||||
fn push(dst: &mut Vec<ValType>) {
|
||||
T::push(dst)
|
||||
}
|
||||
@@ -995,3 +829,198 @@ impl<T: WasmTy> WasmRet for Result<T, Trap> {
|
||||
T::store(abi, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal trait implemented for all arguments that can be passed to
|
||||
/// [`Func::wrap`].
|
||||
///
|
||||
/// This trait should not be implemented by external users, it's only intended
|
||||
/// as an implementation detail of this crate.
|
||||
pub trait IntoFunc<Params, Results> {
|
||||
#[doc(hidden)]
|
||||
fn into_func(self, store: &Store) -> Func;
|
||||
}
|
||||
|
||||
/// A structure representing the *caller's* context when creating a function
|
||||
/// via [`Func::wrap`].
|
||||
///
|
||||
/// This structure can be taken as the first parameter of a closure passed to
|
||||
/// [`Func::wrap`], and it can be used to learn information about the caller of
|
||||
/// the function, such as the calling module's memory, exports, etc.
|
||||
///
|
||||
/// The primary purpose of this structure is to provide access to the
|
||||
/// caller's information, such as it's exported memory. This allows
|
||||
/// functions which take pointers as arguments to easily read the memory the
|
||||
/// pointers point into.
|
||||
///
|
||||
/// Note that this is intended to be a pretty temporary mechanism for accessing
|
||||
/// the caller's memory until interface types has been fully standardized and
|
||||
/// implemented.
|
||||
pub struct Caller<'a> {
|
||||
store: &'a Store,
|
||||
caller_vmctx: *mut VMContext,
|
||||
}
|
||||
|
||||
impl Caller<'_> {
|
||||
/// Looks up an export from the caller's module by the `name` given.
|
||||
///
|
||||
/// Note that this function is only implemented for the `Extern::Memory`
|
||||
/// type currently. No other exported structure can be acquired through this
|
||||
/// just yet, but this may be implemented in the future!
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If a memory export with the `name` provided was found, then it is
|
||||
/// returned as a `Memory`. There are a number of situations, however, where
|
||||
/// the memory may not be available:
|
||||
///
|
||||
/// * The caller instance may not have an export named `name`
|
||||
/// * The export named `name` may not be an exported memory
|
||||
/// * There may not be a caller available, for example if `Func` was called
|
||||
/// directly from host code.
|
||||
///
|
||||
/// It's recommended to take care when calling this API and gracefully
|
||||
/// handling a `None` return value.
|
||||
pub fn get_export(&self, name: &str) -> Option<Extern> {
|
||||
unsafe {
|
||||
if self.caller_vmctx.is_null() {
|
||||
return None;
|
||||
}
|
||||
let instance = InstanceHandle::from_vmctx(self.caller_vmctx);
|
||||
let export = match instance.lookup(name) {
|
||||
Some(Export::Memory(m)) => m,
|
||||
_ => return None,
|
||||
};
|
||||
let mem = Memory::from_wasmtime_memory(export, self.store, instance);
|
||||
Some(Extern::Memory(mem))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_into_func {
|
||||
($(
|
||||
($($args:ident)*)
|
||||
)*) => ($(
|
||||
// Implement for functions without a leading `&Caller` parameter,
|
||||
// delegating to the implementation below which does have the leading
|
||||
// `Caller` parameter.
|
||||
impl<F, $($args,)* R> IntoFunc<($($args,)*), R> for F
|
||||
where
|
||||
F: Fn($($args),*) -> R + 'static,
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn into_func(self, store: &Store) -> Func {
|
||||
Func::wrap(store, move |_: Caller<'_>, $($args:$args),*| {
|
||||
self($($args),*)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl<F, $($args,)* R> IntoFunc<(Caller<'_>, $($args,)*), R> for F
|
||||
where
|
||||
F: Fn(Caller<'_>, $($args),*) -> R + 'static,
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
fn into_func(self, store: &Store) -> Func {
|
||||
// Note that this shim's ABI must match that expected by
|
||||
// cranelift, since cranelift is generating raw function calls
|
||||
// directly to this function.
|
||||
unsafe extern "C" fn shim<F, $($args,)* R>(
|
||||
vmctx: *mut VMContext,
|
||||
caller_vmctx: *mut VMContext,
|
||||
$($args: $args,)*
|
||||
) -> R::Abi
|
||||
where
|
||||
F: Fn(Caller<'_>, $($args),*) -> R + 'static,
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
let ret = {
|
||||
let instance = InstanceHandle::from_vmctx(vmctx);
|
||||
let (func, store) = instance.host_state().downcast_ref::<(F, Store)>().expect("state");
|
||||
panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
func(
|
||||
Caller { store, caller_vmctx },
|
||||
$($args,)*
|
||||
)
|
||||
}))
|
||||
};
|
||||
match ret {
|
||||
Ok(ret) => ret.into_abi(),
|
||||
Err(panic) => wasmtime_runtime::resume_panic(panic),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn trampoline<$($args,)* R>(
|
||||
callee_vmctx: *mut VMContext,
|
||||
caller_vmctx: *mut VMContext,
|
||||
ptr: *const VMFunctionBody,
|
||||
args: *mut u128,
|
||||
)
|
||||
where
|
||||
$($args: WasmTy,)*
|
||||
R: WasmRet,
|
||||
{
|
||||
let ptr = mem::transmute::<
|
||||
*const VMFunctionBody,
|
||||
unsafe extern "C" fn(
|
||||
*mut VMContext,
|
||||
*mut VMContext,
|
||||
$($args,)*
|
||||
) -> R::Abi,
|
||||
>(ptr);
|
||||
|
||||
let mut _next = args as *const u128;
|
||||
$(let $args = $args::load(&mut _next);)*
|
||||
let ret = ptr(callee_vmctx, caller_vmctx, $($args),*);
|
||||
R::store(ret, args);
|
||||
}
|
||||
|
||||
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());
|
||||
let store_clone = store.clone();
|
||||
unsafe {
|
||||
let trampoline = trampoline::<$($args,)* R>;
|
||||
let (instance, export) = crate::trampoline::generate_raw_func_export(
|
||||
&ty,
|
||||
std::slice::from_raw_parts_mut(
|
||||
shim::<F, $($args,)* R> as *mut _,
|
||||
0,
|
||||
),
|
||||
trampoline,
|
||||
store,
|
||||
Box::new((self, store_clone)),
|
||||
)
|
||||
.expect("failed to generate export");
|
||||
let callable = Rc::new(WasmtimeFn::new(store, instance, export, trampoline));
|
||||
Func::from_wrapped(store, ty, callable)
|
||||
}
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl_into_func! {
|
||||
()
|
||||
(A1)
|
||||
(A1 A2)
|
||||
(A1 A2 A3)
|
||||
(A1 A2 A3 A4)
|
||||
(A1 A2 A3 A4 A5)
|
||||
(A1 A2 A3 A4 A5 A6)
|
||||
(A1 A2 A3 A4 A5 A6 A7)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14)
|
||||
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ mod values;
|
||||
pub use crate::callable::Callable;
|
||||
pub use crate::externals::*;
|
||||
pub use crate::frame_info::FrameInfo;
|
||||
pub use crate::func::{Func, WasmRet, WasmTy};
|
||||
pub use crate::func::*;
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::module::Module;
|
||||
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
|
||||
|
||||
@@ -62,7 +62,7 @@ fn cross_store() -> anyhow::Result<()> {
|
||||
|
||||
// ============ Cross-store instantiation ==============
|
||||
|
||||
let func = Func::wrap0(&store2, || {});
|
||||
let func = Func::wrap(&store2, || {});
|
||||
let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
||||
let global = Global::new(&store2, ty, Val::I32(0))?;
|
||||
let ty = MemoryType::new(Limits::new(1, None));
|
||||
@@ -84,8 +84,8 @@ fn cross_store() -> anyhow::Result<()> {
|
||||
|
||||
// ============ Cross-store globals ==============
|
||||
|
||||
let store1val = Val::FuncRef(Func::wrap0(&store1, || {}));
|
||||
let store2val = Val::FuncRef(Func::wrap0(&store2, || {}));
|
||||
let store1val = Val::FuncRef(Func::wrap(&store1, || {}));
|
||||
let store2val = Val::FuncRef(Func::wrap(&store2, || {}));
|
||||
|
||||
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
|
||||
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
use anyhow::Result;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
use wasmtime::{Callable, Func, FuncType, Instance, Module, Store, Trap, Val, ValType};
|
||||
use wasmtime::*;
|
||||
|
||||
#[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::wrap(&store, || {});
|
||||
Func::wrap(&store, |_: i32| {});
|
||||
Func::wrap(&store, |_: i32, _: i64| {});
|
||||
Func::wrap(&store, |_: f32, _: f64| {});
|
||||
Func::wrap(&store, || -> i32 { 0 });
|
||||
Func::wrap(&store, || -> i64 { 0 });
|
||||
Func::wrap(&store, || -> f32 { 0.0 });
|
||||
Func::wrap(&store, || -> f64 { 0.0 });
|
||||
|
||||
Func::wrap0(&store, || -> Result<(), Trap> { loop {} });
|
||||
Func::wrap0(&store, || -> Result<i32, Trap> { loop {} });
|
||||
Func::wrap0(&store, || -> Result<i64, Trap> { loop {} });
|
||||
Func::wrap0(&store, || -> Result<f32, Trap> { loop {} });
|
||||
Func::wrap0(&store, || -> Result<f64, Trap> { loop {} });
|
||||
Func::wrap(&store, || -> Result<(), Trap> { loop {} });
|
||||
Func::wrap(&store, || -> Result<i32, Trap> { loop {} });
|
||||
Func::wrap(&store, || -> Result<i64, Trap> { loop {} });
|
||||
Func::wrap(&store, || -> Result<f32, Trap> { loop {} });
|
||||
Func::wrap(&store, || -> Result<f64, Trap> { loop {} });
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -37,7 +37,7 @@ fn dtor_runs() {
|
||||
let store = Store::default();
|
||||
let a = A;
|
||||
assert_eq!(HITS.load(SeqCst), 0);
|
||||
Func::wrap0(&store, move || {
|
||||
Func::wrap(&store, move || {
|
||||
drop(&a);
|
||||
});
|
||||
assert_eq!(HITS.load(SeqCst), 1);
|
||||
@@ -57,7 +57,7 @@ fn dtor_delayed() -> Result<()> {
|
||||
|
||||
let store = Store::default();
|
||||
let a = A;
|
||||
let func = Func::wrap0(&store, move || drop(&a));
|
||||
let func = Func::wrap(&store, move || drop(&a));
|
||||
|
||||
assert_eq!(HITS.load(SeqCst), 0);
|
||||
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
|
||||
@@ -73,27 +73,27 @@ fn dtor_delayed() -> Result<()> {
|
||||
fn signatures_match() {
|
||||
let store = Store::default();
|
||||
|
||||
let f = Func::wrap0(&store, || {});
|
||||
let f = Func::wrap(&store, || {});
|
||||
assert_eq!(f.ty().params(), &[]);
|
||||
assert_eq!(f.ty().results(), &[]);
|
||||
|
||||
let f = Func::wrap0(&store, || -> i32 { loop {} });
|
||||
let f = Func::wrap(&store, || -> i32 { loop {} });
|
||||
assert_eq!(f.ty().params(), &[]);
|
||||
assert_eq!(f.ty().results(), &[ValType::I32]);
|
||||
|
||||
let f = Func::wrap0(&store, || -> i64 { loop {} });
|
||||
let f = Func::wrap(&store, || -> i64 { loop {} });
|
||||
assert_eq!(f.ty().params(), &[]);
|
||||
assert_eq!(f.ty().results(), &[ValType::I64]);
|
||||
|
||||
let f = Func::wrap0(&store, || -> f32 { loop {} });
|
||||
let f = Func::wrap(&store, || -> f32 { loop {} });
|
||||
assert_eq!(f.ty().params(), &[]);
|
||||
assert_eq!(f.ty().results(), &[ValType::F32]);
|
||||
|
||||
let f = Func::wrap0(&store, || -> f64 { loop {} });
|
||||
let f = Func::wrap(&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 {
|
||||
let f = Func::wrap(&store, |_: f32, _: f64, _: i32, _: i64, _: i32| -> f64 {
|
||||
loop {}
|
||||
});
|
||||
assert_eq!(
|
||||
@@ -144,23 +144,23 @@ fn import_works() -> Result<()> {
|
||||
Instance::new(
|
||||
&module,
|
||||
&[
|
||||
Func::wrap0(&store, || {
|
||||
Func::wrap(&store, || {
|
||||
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
|
||||
})
|
||||
.into(),
|
||||
Func::wrap1(&store, |x: i32| -> i32 {
|
||||
Func::wrap(&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| {
|
||||
Func::wrap(&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| {
|
||||
Func::wrap(&store, |a: i32, b: i64, c: i32, d: f32, e: f64| {
|
||||
assert_eq!(a, 100);
|
||||
assert_eq!(b, 200);
|
||||
assert_eq!(c, 300);
|
||||
@@ -177,7 +177,7 @@ fn import_works() -> Result<()> {
|
||||
#[test]
|
||||
fn trap_smoke() {
|
||||
let store = Store::default();
|
||||
let f = Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("test")) });
|
||||
let f = Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("test")) });
|
||||
let err = f.call(&[]).unwrap_err();
|
||||
assert_eq!(err.message(), "test");
|
||||
}
|
||||
@@ -194,7 +194,7 @@ fn trap_import() -> Result<()> {
|
||||
let module = Module::new(&store, &wasm)?;
|
||||
let trap = Instance::new(
|
||||
&module,
|
||||
&[Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
|
||||
&[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
|
||||
)
|
||||
.err()
|
||||
.unwrap()
|
||||
@@ -206,7 +206,7 @@ fn trap_import() -> Result<()> {
|
||||
#[test]
|
||||
fn get_from_wrapper() {
|
||||
let store = Store::default();
|
||||
let f = Func::wrap0(&store, || {});
|
||||
let f = Func::wrap(&store, || {});
|
||||
assert!(f.get0::<()>().is_ok());
|
||||
assert!(f.get0::<i32>().is_err());
|
||||
assert!(f.get1::<(), ()>().is_ok());
|
||||
@@ -216,23 +216,23 @@ fn get_from_wrapper() {
|
||||
assert!(f.get2::<i32, i32, ()>().is_err());
|
||||
assert!(f.get2::<i32, i32, i32>().is_err());
|
||||
|
||||
let f = Func::wrap0(&store, || -> i32 { loop {} });
|
||||
let f = Func::wrap(&store, || -> i32 { loop {} });
|
||||
assert!(f.get0::<i32>().is_ok());
|
||||
let f = Func::wrap0(&store, || -> f32 { loop {} });
|
||||
let f = Func::wrap(&store, || -> f32 { loop {} });
|
||||
assert!(f.get0::<f32>().is_ok());
|
||||
let f = Func::wrap0(&store, || -> f64 { loop {} });
|
||||
let f = Func::wrap(&store, || -> f64 { loop {} });
|
||||
assert!(f.get0::<f64>().is_ok());
|
||||
|
||||
let f = Func::wrap1(&store, |_: i32| {});
|
||||
let f = Func::wrap(&store, |_: i32| {});
|
||||
assert!(f.get1::<i32, ()>().is_ok());
|
||||
assert!(f.get1::<i64, ()>().is_err());
|
||||
assert!(f.get1::<f32, ()>().is_err());
|
||||
assert!(f.get1::<f64, ()>().is_err());
|
||||
let f = Func::wrap1(&store, |_: i64| {});
|
||||
let f = Func::wrap(&store, |_: i64| {});
|
||||
assert!(f.get1::<i64, ()>().is_ok());
|
||||
let f = Func::wrap1(&store, |_: f32| {});
|
||||
let f = Func::wrap(&store, |_: f32| {});
|
||||
assert!(f.get1::<f32, ()>().is_ok());
|
||||
let f = Func::wrap1(&store, |_: f64| {});
|
||||
let f = Func::wrap(&store, |_: f64| {});
|
||||
assert!(f.get1::<f64, ()>().is_ok());
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ fn get_from_module() -> anyhow::Result<()> {
|
||||
#[test]
|
||||
fn call_wrapped_func() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let f = Func::wrap4(&store, |a: i32, b: i64, c: f32, d: f64| {
|
||||
let f = Func::wrap(&store, |a: i32, b: i64, c: f32, d: f64| {
|
||||
assert_eq!(a, 1);
|
||||
assert_eq!(b, 2);
|
||||
assert_eq!(c, 3.0);
|
||||
@@ -302,28 +302,93 @@ fn call_wrapped_func() -> Result<()> {
|
||||
f.call(&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()])?;
|
||||
f.get4::<i32, i64, f32, f64, ()>()?(1, 2, 3.0, 4.0)?;
|
||||
|
||||
let f = Func::wrap0(&store, || 1i32);
|
||||
let f = Func::wrap(&store, || 1i32);
|
||||
let results = f.call(&[])?;
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].unwrap_i32(), 1);
|
||||
assert_eq!(f.get0::<i32>()?()?, 1);
|
||||
|
||||
let f = Func::wrap0(&store, || 2i64);
|
||||
let f = Func::wrap(&store, || 2i64);
|
||||
let results = f.call(&[])?;
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].unwrap_i64(), 2);
|
||||
assert_eq!(f.get0::<i64>()?()?, 2);
|
||||
|
||||
let f = Func::wrap0(&store, || 3.0f32);
|
||||
let f = Func::wrap(&store, || 3.0f32);
|
||||
let results = f.call(&[])?;
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].unwrap_f32(), 3.0);
|
||||
assert_eq!(f.get0::<f32>()?()?, 3.0);
|
||||
|
||||
let f = Func::wrap0(&store, || 4.0f64);
|
||||
let f = Func::wrap(&store, || 4.0f64);
|
||||
let results = f.call(&[])?;
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].unwrap_f64(), 4.0);
|
||||
assert_eq!(f.get0::<f64>()?()?, 4.0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caller_memory() -> anyhow::Result<()> {
|
||||
let store = Store::default();
|
||||
let f = Func::wrap(&store, |c: Caller<'_>| {
|
||||
assert!(c.get_export("x").is_none());
|
||||
assert!(c.get_export("y").is_none());
|
||||
assert!(c.get_export("z").is_none());
|
||||
});
|
||||
f.call(&[])?;
|
||||
|
||||
let f = Func::wrap(&store, |c: Caller<'_>| {
|
||||
assert!(c.get_export("x").is_none());
|
||||
});
|
||||
let module = Module::new(
|
||||
&store,
|
||||
r#"
|
||||
(module
|
||||
(import "" "" (func $f))
|
||||
(start $f)
|
||||
)
|
||||
|
||||
"#,
|
||||
)?;
|
||||
Instance::new(&module, &[f.into()])?;
|
||||
|
||||
let f = Func::wrap(&store, |c: Caller<'_>| {
|
||||
assert!(c.get_export("memory").is_some());
|
||||
});
|
||||
let module = Module::new(
|
||||
&store,
|
||||
r#"
|
||||
(module
|
||||
(import "" "" (func $f))
|
||||
(memory (export "memory") 1)
|
||||
(start $f)
|
||||
)
|
||||
|
||||
"#,
|
||||
)?;
|
||||
Instance::new(&module, &[f.into()])?;
|
||||
|
||||
let f = Func::wrap(&store, |c: Caller<'_>| {
|
||||
assert!(c.get_export("m").is_some());
|
||||
assert!(c.get_export("f").is_none());
|
||||
assert!(c.get_export("g").is_none());
|
||||
assert!(c.get_export("t").is_none());
|
||||
});
|
||||
let module = Module::new(
|
||||
&store,
|
||||
r#"
|
||||
(module
|
||||
(import "" "" (func $f))
|
||||
(memory (export "m") 1)
|
||||
(func (export "f"))
|
||||
(global (export "g") i32 (i32.const 0))
|
||||
(table (export "t") 1 funcref)
|
||||
(start $f)
|
||||
)
|
||||
|
||||
"#,
|
||||
)?;
|
||||
Instance::new(&module, &[f.into()])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ fn rust_panic_import() -> Result<()> {
|
||||
&module,
|
||||
&[
|
||||
func.into(),
|
||||
Func::wrap0(&store, || panic!("this is another panic")).into(),
|
||||
Func::wrap(&store, || panic!("this is another panic")).into(),
|
||||
],
|
||||
)?;
|
||||
let func = instance.exports()[0].func().unwrap().clone();
|
||||
@@ -329,7 +329,7 @@ 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 func = Func::wrap(&store, || panic!("this is another panic"));
|
||||
let err = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
drop(Instance::new(&module, &[func.into()]));
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user