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::{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 anyhow::{ensure, Context as _};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::panic::{self, AssertUnwindSafe};
|
use std::panic::{self, AssertUnwindSafe};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
||||||
|
|
||||||
/// A WebAssembly function which can be called.
|
/// 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`
|
/// `Func`
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@@ -69,7 +69,7 @@ use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
|||||||
///
|
///
|
||||||
/// // Create a custom `Func` which can execute arbitrary code inside of the
|
/// // Create a custom `Func` which can execute arbitrary code inside of the
|
||||||
/// // closure.
|
/// // 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.
|
/// // Next we can hook that up to a wasm module which uses it.
|
||||||
/// let module = Module::new(
|
/// let module = Module::new(
|
||||||
@@ -148,95 +148,6 @@ pub struct Func {
|
|||||||
ty: FuncType,
|
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 {
|
macro_rules! getters {
|
||||||
($(
|
($(
|
||||||
$(#[$doc:meta])*
|
$(#[$doc:meta])*
|
||||||
@@ -277,15 +188,15 @@ macro_rules! getters {
|
|||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
*mut VMContext,
|
*mut VMContext,
|
||||||
*mut VMContext,
|
*mut VMContext,
|
||||||
$($args::Abi,)*
|
$($args,)*
|
||||||
) -> R::Abi,
|
) -> R,
|
||||||
>(f.address);
|
>(f.address);
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
$(let $args = $args.into_abi();)*
|
$(let $args = $args.into_abi();)*
|
||||||
wasmtime_runtime::catch_traps(f.vmctx, || {
|
wasmtime_runtime::catch_traps(f.vmctx, || {
|
||||||
ret = Some(fnptr(f.vmctx, ptr::null_mut(), $($args,)*));
|
ret = Some(fnptr(f.vmctx, ptr::null_mut(), $($args,)*));
|
||||||
}).map_err(Trap::from_jit)?;
|
}).map_err(Trap::from_jit)?;
|
||||||
Ok(R::from_abi(f.vmctx, ret.unwrap()))
|
Ok(ret.unwrap())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -313,15 +224,7 @@ impl Func {
|
|||||||
Func::from_wrapped(store, ty, callable)
|
Func::from_wrapped(store, ty, callable)
|
||||||
}
|
}
|
||||||
|
|
||||||
wrappers! {
|
/// Creates a new `Func` from the given Rust closure.
|
||||||
/// 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
|
/// This function will create a new `Func` which, when called, will
|
||||||
/// execute the given Rust closure. Unlike [`Func::new`] the target
|
/// execute the given Rust closure. Unlike [`Func::new`] the target
|
||||||
@@ -345,11 +248,20 @@ impl Func {
|
|||||||
/// | `()` | nothing | no return value |
|
/// | `()` | nothing | no return value |
|
||||||
/// | `Result<T, Trap>` | `T` | function may trap |
|
/// | `Result<T, Trap>` | `T` | function may trap |
|
||||||
///
|
///
|
||||||
/// Note that when using this API (and the related `wrap*` family of
|
/// At this time multi-value returns are not supported, and supporting this
|
||||||
/// functions), the intention is to create as thin of a layer as
|
/// is the subject of [#1178].
|
||||||
/// possible for when WebAssembly calls the function provided. With
|
///
|
||||||
/// sufficient inlining and optimization the WebAssembly will call
|
/// [#1178]: https://github.com/bytecodealliance/wasmtime/issues/1178
|
||||||
/// straight into `func` provided, with no extra fluff entailed.
|
///
|
||||||
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
@@ -360,7 +272,7 @@ impl Func {
|
|||||||
/// # use wasmtime::*;
|
/// # use wasmtime::*;
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
/// # let store = Store::default();
|
/// # let store = Store::default();
|
||||||
/// let add = Func::wrap2(&store, |a: i32, b: i32| a + b);
|
/// let add = Func::wrap(&store, |a: i32, b: i32| a + b);
|
||||||
/// let module = Module::new(
|
/// let module = Module::new(
|
||||||
/// &store,
|
/// &store,
|
||||||
/// r#"
|
/// r#"
|
||||||
@@ -386,7 +298,7 @@ impl Func {
|
|||||||
/// # use wasmtime::*;
|
/// # use wasmtime::*;
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
/// # let store = Store::default();
|
/// # let store = Store::default();
|
||||||
/// let add = Func::wrap2(&store, |a: i32, b: i32| {
|
/// let add = Func::wrap(&store, |a: i32, b: i32| {
|
||||||
/// match a.checked_add(b) {
|
/// match a.checked_add(b) {
|
||||||
/// Some(i) => Ok(i),
|
/// Some(i) => Ok(i),
|
||||||
/// None => Err(Trap::new("overflow")),
|
/// None => Err(Trap::new("overflow")),
|
||||||
@@ -417,7 +329,7 @@ impl Func {
|
|||||||
/// # use wasmtime::*;
|
/// # use wasmtime::*;
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
/// # let store = Store::default();
|
/// # let store = Store::default();
|
||||||
/// let debug = Func::wrap4(&store, |a: i32, b: f32, c: i64, d: f64| {
|
/// let debug = Func::wrap(&store, |a: i32, b: f32, c: i64, d: f64| {
|
||||||
/// println!("a={}", a);
|
/// println!("a={}", a);
|
||||||
/// println!("b={}", b);
|
/// println!("b={}", b);
|
||||||
/// println!("c={}", c);
|
/// println!("c={}", c);
|
||||||
@@ -442,91 +354,64 @@ impl Func {
|
|||||||
/// # Ok(())
|
/// # 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`].
|
/// Finally if you want to get really fancy you can also implement
|
||||||
(wrap2, A1, A2)
|
/// imports that read/write wasm module's memory
|
||||||
|
|
||||||
/// 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)
|
/// use std::str;
|
||||||
|
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 4
|
|
||||||
/// arguments.
|
|
||||||
///
|
///
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// # use wasmtime::*;
|
||||||
(wrap4, A1, A2, A3, A4)
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
|
/// # let store = Store::default();
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 5
|
/// let log_str = Func::wrap(&store, |caller: Caller<'_>, ptr: i32, len: i32| {
|
||||||
/// arguments.
|
/// let mem = match caller.get_export("memory") {
|
||||||
|
/// Some(Extern::Memory(mem)) => mem,
|
||||||
|
/// _ => return Err(Trap::new("failed to find host memory")),
|
||||||
|
/// };
|
||||||
///
|
///
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// // We're reading raw wasm memory here so we need `unsafe`. Note
|
||||||
(wrap5, A1, A2, A3, A4, A5)
|
/// // though that this should be safe because we don't reenter wasm
|
||||||
|
/// // while we're reading wasm memory, nor should we clash with
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 6
|
/// // any other memory accessors (assuming they're well-behaved
|
||||||
/// arguments.
|
/// // too).
|
||||||
///
|
/// unsafe {
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// let data = mem.data_unchecked()
|
||||||
(wrap6, A1, A2, A3, A4, A5, A6)
|
/// .get(ptr as u32 as usize..)
|
||||||
|
/// .and_then(|arr| arr.get(..len as u32 as usize));
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 7
|
/// let string = match data {
|
||||||
/// arguments.
|
/// Some(data) => match str::from_utf8(data) {
|
||||||
///
|
/// Ok(s) => s,
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// Err(_) => return Err(Trap::new("invalid utf-8")),
|
||||||
(wrap7, A1, A2, A3, A4, A5, A6, A7)
|
/// },
|
||||||
|
/// None => return Err(Trap::new("pointer/length out of bounds")),
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 8
|
/// };
|
||||||
/// arguments.
|
/// assert_eq!(string, "Hello, world!");
|
||||||
///
|
/// println!("{}", string);
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// }
|
||||||
(wrap8, A1, A2, A3, A4, A5, A6, A7, A8)
|
/// Ok(())
|
||||||
|
/// });
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 9
|
/// let module = Module::new(
|
||||||
/// arguments.
|
/// &store,
|
||||||
///
|
/// r#"
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// (module
|
||||||
(wrap9, A1, A2, A3, A4, A5, A6, A7, A8, A9)
|
/// (import "" "" (func $log_str (param i32 i32)))
|
||||||
|
/// (func (export "foo")
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 10
|
/// i32.const 4 ;; ptr
|
||||||
/// arguments.
|
/// i32.const 13 ;; len
|
||||||
///
|
/// call $log_str)
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// (memory (export "memory") 1)
|
||||||
(wrap10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)
|
/// (data (i32.const 4) "Hello, world!"))
|
||||||
|
/// "#,
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 11
|
/// )?;
|
||||||
/// arguments.
|
/// let instance = Instance::new(&module, &[log_str.into()])?;
|
||||||
///
|
/// let foo = instance.exports()[0].func().unwrap().get0::<()>()?;
|
||||||
/// For more information about this function, see [`Func::wrap1`].
|
/// foo()?;
|
||||||
(wrap11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// Creates a new `Func` from the given Rust closure, which takes 12
|
/// ```
|
||||||
/// arguments.
|
pub fn wrap<Params, Results>(store: &Store, func: impl IntoFunc<Params, Results>) -> Func {
|
||||||
///
|
func.into_func(store)
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_wrapped(
|
fn from_wrapped(
|
||||||
@@ -634,7 +519,7 @@ impl Func {
|
|||||||
/// instead this function jumps directly into JIT code.
|
/// instead this function jumps directly into JIT code.
|
||||||
///
|
///
|
||||||
/// For more information about which Rust types match up to which wasm
|
/// 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
|
/// # Return
|
||||||
///
|
///
|
||||||
@@ -746,52 +631,36 @@ impl fmt::Debug for Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A trait implemented for types which can be arguments to closures passed to
|
/// 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
|
/// 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
|
/// any time internally. The types which implement this trait, however, are
|
||||||
/// stable over time.
|
/// stable over time.
|
||||||
///
|
///
|
||||||
/// For more information see [`Func::wrap1`]
|
/// For more information see [`Func::wrap`]
|
||||||
pub trait WasmTy {
|
pub unsafe trait WasmTy: Copy {
|
||||||
#[doc(hidden)]
|
|
||||||
type Abi: Copy;
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn push(dst: &mut Vec<ValType>);
|
fn push(dst: &mut Vec<ValType>);
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
|
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn from_abi(vmctx: *mut VMContext, abi: Self::Abi) -> Self;
|
unsafe fn load(ptr: &mut *const u128) -> Self;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn into_abi(self) -> Self::Abi;
|
unsafe fn store(abi: Self, ptr: *mut u128);
|
||||||
#[doc(hidden)]
|
|
||||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi;
|
|
||||||
#[doc(hidden)]
|
|
||||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmTy for () {
|
unsafe impl WasmTy for () {
|
||||||
type Abi = ();
|
|
||||||
fn push(_dst: &mut Vec<ValType>) {}
|
fn push(_dst: &mut Vec<ValType>) {}
|
||||||
fn matches(_tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
|
fn matches(_tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
unsafe fn load(_ptr: &mut *const u128) -> Self {}
|
||||||
abi
|
|
||||||
}
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi(self) -> Self::Abi {
|
unsafe fn store(_abi: Self, _ptr: *mut u128) {}
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
unsafe fn load(_ptr: &mut *const u128) -> Self::Abi {}
|
|
||||||
#[inline]
|
|
||||||
unsafe fn store(_abi: Self::Abi, _ptr: *mut u128) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmTy for i32 {
|
unsafe impl WasmTy for i32 {
|
||||||
type Abi = Self;
|
|
||||||
fn push(dst: &mut Vec<ValType>) {
|
fn push(dst: &mut Vec<ValType>) {
|
||||||
dst.push(ValType::I32);
|
dst.push(ValType::I32);
|
||||||
}
|
}
|
||||||
@@ -805,27 +674,18 @@ impl WasmTy for i32 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||||
abi
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self) -> Self::Abi {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
|
||||||
let ret = **ptr as Self;
|
let ret = **ptr as Self;
|
||||||
*ptr = (*ptr).add(1);
|
*ptr = (*ptr).add(1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||||
*ptr = abi as u128;
|
*ptr = abi as u128;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmTy for i64 {
|
unsafe impl WasmTy for i64 {
|
||||||
type Abi = Self;
|
|
||||||
fn push(dst: &mut Vec<ValType>) {
|
fn push(dst: &mut Vec<ValType>) {
|
||||||
dst.push(ValType::I64);
|
dst.push(ValType::I64);
|
||||||
}
|
}
|
||||||
@@ -839,27 +699,18 @@ impl WasmTy for i64 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||||
abi
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self) -> Self::Abi {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
|
||||||
let ret = **ptr as Self;
|
let ret = **ptr as Self;
|
||||||
*ptr = (*ptr).add(1);
|
*ptr = (*ptr).add(1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||||
*ptr = abi as u128;
|
*ptr = abi as u128;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmTy for f32 {
|
unsafe impl WasmTy for f32 {
|
||||||
type Abi = Self;
|
|
||||||
fn push(dst: &mut Vec<ValType>) {
|
fn push(dst: &mut Vec<ValType>) {
|
||||||
dst.push(ValType::F32);
|
dst.push(ValType::F32);
|
||||||
}
|
}
|
||||||
@@ -873,27 +724,18 @@ impl WasmTy for f32 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||||
abi
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self) -> Self::Abi {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
|
||||||
let ret = f32::from_bits(**ptr as u32);
|
let ret = f32::from_bits(**ptr as u32);
|
||||||
*ptr = (*ptr).add(1);
|
*ptr = (*ptr).add(1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||||
*ptr = abi.to_bits() as u128;
|
*ptr = abi.to_bits() as u128;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmTy for f64 {
|
unsafe impl WasmTy for f64 {
|
||||||
type Abi = Self;
|
|
||||||
fn push(dst: &mut Vec<ValType>) {
|
fn push(dst: &mut Vec<ValType>) {
|
||||||
dst.push(ValType::F64);
|
dst.push(ValType::F64);
|
||||||
}
|
}
|
||||||
@@ -907,34 +749,26 @@ impl WasmTy for f64 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
|
unsafe fn load(ptr: &mut *const u128) -> Self {
|
||||||
abi
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self) -> Self::Abi {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
unsafe fn load(ptr: &mut *const u128) -> Self::Abi {
|
|
||||||
let ret = f64::from_bits(**ptr as u64);
|
let ret = f64::from_bits(**ptr as u64);
|
||||||
*ptr = (*ptr).add(1);
|
*ptr = (*ptr).add(1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128) {
|
unsafe fn store(abi: Self, ptr: *mut u128) {
|
||||||
*ptr = abi.to_bits() as u128;
|
*ptr = abi.to_bits() as u128;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait implemented for types which can be returned from closures passed to
|
/// 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
|
/// 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
|
/// any time internally. The types which implement this trait, however, are
|
||||||
/// stable over time.
|
/// stable over time.
|
||||||
///
|
///
|
||||||
/// For more information see [`Func::wrap1`]
|
/// For more information see [`Func::wrap`]
|
||||||
pub trait WasmRet {
|
pub unsafe trait WasmRet {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
type Abi;
|
type Abi;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@@ -947,8 +781,8 @@ pub trait WasmRet {
|
|||||||
unsafe fn store(abi: Self::Abi, ptr: *mut u128);
|
unsafe fn store(abi: Self::Abi, ptr: *mut u128);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WasmTy> WasmRet for T {
|
unsafe impl<T: WasmTy> WasmRet for T {
|
||||||
type Abi = T::Abi;
|
type Abi = T;
|
||||||
fn push(dst: &mut Vec<ValType>) {
|
fn push(dst: &mut Vec<ValType>) {
|
||||||
T::push(dst)
|
T::push(dst)
|
||||||
}
|
}
|
||||||
@@ -959,7 +793,7 @@ impl<T: WasmTy> WasmRet for T {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi(self) -> Self::Abi {
|
fn into_abi(self) -> Self::Abi {
|
||||||
T::into_abi(self)
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -968,8 +802,8 @@ impl<T: WasmTy> WasmRet for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WasmTy> WasmRet for Result<T, Trap> {
|
unsafe impl<T: WasmTy> WasmRet for Result<T, Trap> {
|
||||||
type Abi = T::Abi;
|
type Abi = T;
|
||||||
fn push(dst: &mut Vec<ValType>) {
|
fn push(dst: &mut Vec<ValType>) {
|
||||||
T::push(dst)
|
T::push(dst)
|
||||||
}
|
}
|
||||||
@@ -995,3 +829,198 @@ impl<T: WasmTy> WasmRet for Result<T, Trap> {
|
|||||||
T::store(abi, ptr);
|
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::callable::Callable;
|
||||||
pub use crate::externals::*;
|
pub use crate::externals::*;
|
||||||
pub use crate::frame_info::FrameInfo;
|
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::instance::Instance;
|
||||||
pub use crate::module::Module;
|
pub use crate::module::Module;
|
||||||
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
|
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ fn cross_store() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
// ============ Cross-store instantiation ==============
|
// ============ Cross-store instantiation ==============
|
||||||
|
|
||||||
let func = Func::wrap0(&store2, || {});
|
let func = Func::wrap(&store2, || {});
|
||||||
let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
||||||
let global = Global::new(&store2, ty, Val::I32(0))?;
|
let global = Global::new(&store2, ty, Val::I32(0))?;
|
||||||
let ty = MemoryType::new(Limits::new(1, None));
|
let ty = MemoryType::new(Limits::new(1, None));
|
||||||
@@ -84,8 +84,8 @@ fn cross_store() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
// ============ Cross-store globals ==============
|
// ============ Cross-store globals ==============
|
||||||
|
|
||||||
let store1val = Val::FuncRef(Func::wrap0(&store1, || {}));
|
let store1val = Val::FuncRef(Func::wrap(&store1, || {}));
|
||||||
let store2val = Val::FuncRef(Func::wrap0(&store2, || {}));
|
let store2val = Val::FuncRef(Func::wrap(&store2, || {}));
|
||||||
|
|
||||||
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
|
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
|
||||||
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());
|
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
use wasmtime::{Callable, Func, FuncType, Instance, Module, Store, Trap, Val, ValType};
|
use wasmtime::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn func_constructors() {
|
fn func_constructors() {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
Func::wrap0(&store, || {});
|
Func::wrap(&store, || {});
|
||||||
Func::wrap1(&store, |_: i32| {});
|
Func::wrap(&store, |_: i32| {});
|
||||||
Func::wrap2(&store, |_: i32, _: i64| {});
|
Func::wrap(&store, |_: i32, _: i64| {});
|
||||||
Func::wrap2(&store, |_: f32, _: f64| {});
|
Func::wrap(&store, |_: f32, _: f64| {});
|
||||||
Func::wrap0(&store, || -> i32 { 0 });
|
Func::wrap(&store, || -> i32 { 0 });
|
||||||
Func::wrap0(&store, || -> i64 { 0 });
|
Func::wrap(&store, || -> i64 { 0 });
|
||||||
Func::wrap0(&store, || -> f32 { 0.0 });
|
Func::wrap(&store, || -> f32 { 0.0 });
|
||||||
Func::wrap0(&store, || -> f64 { 0.0 });
|
Func::wrap(&store, || -> f64 { 0.0 });
|
||||||
|
|
||||||
Func::wrap0(&store, || -> Result<(), Trap> { loop {} });
|
Func::wrap(&store, || -> Result<(), Trap> { loop {} });
|
||||||
Func::wrap0(&store, || -> Result<i32, Trap> { loop {} });
|
Func::wrap(&store, || -> Result<i32, Trap> { loop {} });
|
||||||
Func::wrap0(&store, || -> Result<i64, Trap> { loop {} });
|
Func::wrap(&store, || -> Result<i64, Trap> { loop {} });
|
||||||
Func::wrap0(&store, || -> Result<f32, Trap> { loop {} });
|
Func::wrap(&store, || -> Result<f32, Trap> { loop {} });
|
||||||
Func::wrap0(&store, || -> Result<f64, Trap> { loop {} });
|
Func::wrap(&store, || -> Result<f64, Trap> { loop {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -37,7 +37,7 @@ fn dtor_runs() {
|
|||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let a = A;
|
let a = A;
|
||||||
assert_eq!(HITS.load(SeqCst), 0);
|
assert_eq!(HITS.load(SeqCst), 0);
|
||||||
Func::wrap0(&store, move || {
|
Func::wrap(&store, move || {
|
||||||
drop(&a);
|
drop(&a);
|
||||||
});
|
});
|
||||||
assert_eq!(HITS.load(SeqCst), 1);
|
assert_eq!(HITS.load(SeqCst), 1);
|
||||||
@@ -57,7 +57,7 @@ fn dtor_delayed() -> Result<()> {
|
|||||||
|
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let a = A;
|
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);
|
assert_eq!(HITS.load(SeqCst), 0);
|
||||||
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
|
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
|
||||||
@@ -73,27 +73,27 @@ fn dtor_delayed() -> Result<()> {
|
|||||||
fn signatures_match() {
|
fn signatures_match() {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
|
|
||||||
let f = Func::wrap0(&store, || {});
|
let f = Func::wrap(&store, || {});
|
||||||
assert_eq!(f.ty().params(), &[]);
|
assert_eq!(f.ty().params(), &[]);
|
||||||
assert_eq!(f.ty().results(), &[]);
|
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().params(), &[]);
|
||||||
assert_eq!(f.ty().results(), &[ValType::I32]);
|
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().params(), &[]);
|
||||||
assert_eq!(f.ty().results(), &[ValType::I64]);
|
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().params(), &[]);
|
||||||
assert_eq!(f.ty().results(), &[ValType::F32]);
|
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().params(), &[]);
|
||||||
assert_eq!(f.ty().results(), &[ValType::F64]);
|
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 {}
|
loop {}
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -144,23 +144,23 @@ fn import_works() -> Result<()> {
|
|||||||
Instance::new(
|
Instance::new(
|
||||||
&module,
|
&module,
|
||||||
&[
|
&[
|
||||||
Func::wrap0(&store, || {
|
Func::wrap(&store, || {
|
||||||
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
|
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
Func::wrap1(&store, |x: i32| -> i32 {
|
Func::wrap(&store, |x: i32| -> i32 {
|
||||||
assert_eq!(x, 0);
|
assert_eq!(x, 0);
|
||||||
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
|
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
|
||||||
1
|
1
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
Func::wrap2(&store, |x: i32, y: i64| {
|
Func::wrap(&store, |x: i32, y: i64| {
|
||||||
assert_eq!(x, 2);
|
assert_eq!(x, 2);
|
||||||
assert_eq!(y, 3);
|
assert_eq!(y, 3);
|
||||||
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
|
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
|
||||||
})
|
})
|
||||||
.into(),
|
.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!(a, 100);
|
||||||
assert_eq!(b, 200);
|
assert_eq!(b, 200);
|
||||||
assert_eq!(c, 300);
|
assert_eq!(c, 300);
|
||||||
@@ -177,7 +177,7 @@ fn import_works() -> Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn trap_smoke() {
|
fn trap_smoke() {
|
||||||
let store = Store::default();
|
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();
|
let err = f.call(&[]).unwrap_err();
|
||||||
assert_eq!(err.message(), "test");
|
assert_eq!(err.message(), "test");
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ fn trap_import() -> Result<()> {
|
|||||||
let module = Module::new(&store, &wasm)?;
|
let module = Module::new(&store, &wasm)?;
|
||||||
let trap = Instance::new(
|
let trap = Instance::new(
|
||||||
&module,
|
&module,
|
||||||
&[Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
|
&[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
|
||||||
)
|
)
|
||||||
.err()
|
.err()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -206,7 +206,7 @@ fn trap_import() -> Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn get_from_wrapper() {
|
fn get_from_wrapper() {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let f = Func::wrap0(&store, || {});
|
let f = Func::wrap(&store, || {});
|
||||||
assert!(f.get0::<()>().is_ok());
|
assert!(f.get0::<()>().is_ok());
|
||||||
assert!(f.get0::<i32>().is_err());
|
assert!(f.get0::<i32>().is_err());
|
||||||
assert!(f.get1::<(), ()>().is_ok());
|
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, ()>().is_err());
|
||||||
assert!(f.get2::<i32, 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());
|
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());
|
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());
|
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::<i32, ()>().is_ok());
|
||||||
assert!(f.get1::<i64, ()>().is_err());
|
assert!(f.get1::<i64, ()>().is_err());
|
||||||
assert!(f.get1::<f32, ()>().is_err());
|
assert!(f.get1::<f32, ()>().is_err());
|
||||||
assert!(f.get1::<f64, ()>().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());
|
assert!(f.get1::<i64, ()>().is_ok());
|
||||||
let f = Func::wrap1(&store, |_: f32| {});
|
let f = Func::wrap(&store, |_: f32| {});
|
||||||
assert!(f.get1::<f32, ()>().is_ok());
|
assert!(f.get1::<f32, ()>().is_ok());
|
||||||
let f = Func::wrap1(&store, |_: f64| {});
|
let f = Func::wrap(&store, |_: f64| {});
|
||||||
assert!(f.get1::<f64, ()>().is_ok());
|
assert!(f.get1::<f64, ()>().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ fn get_from_module() -> anyhow::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn call_wrapped_func() -> Result<()> {
|
fn call_wrapped_func() -> Result<()> {
|
||||||
let store = Store::default();
|
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!(a, 1);
|
||||||
assert_eq!(b, 2);
|
assert_eq!(b, 2);
|
||||||
assert_eq!(c, 3.0);
|
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.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)?;
|
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(&[])?;
|
let results = f.call(&[])?;
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
assert_eq!(results[0].unwrap_i32(), 1);
|
assert_eq!(results[0].unwrap_i32(), 1);
|
||||||
assert_eq!(f.get0::<i32>()?()?, 1);
|
assert_eq!(f.get0::<i32>()?()?, 1);
|
||||||
|
|
||||||
let f = Func::wrap0(&store, || 2i64);
|
let f = Func::wrap(&store, || 2i64);
|
||||||
let results = f.call(&[])?;
|
let results = f.call(&[])?;
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
assert_eq!(results[0].unwrap_i64(), 2);
|
assert_eq!(results[0].unwrap_i64(), 2);
|
||||||
assert_eq!(f.get0::<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(&[])?;
|
let results = f.call(&[])?;
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
assert_eq!(results[0].unwrap_f32(), 3.0);
|
assert_eq!(results[0].unwrap_f32(), 3.0);
|
||||||
assert_eq!(f.get0::<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(&[])?;
|
let results = f.call(&[])?;
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
assert_eq!(results[0].unwrap_f64(), 4.0);
|
assert_eq!(results[0].unwrap_f64(), 4.0);
|
||||||
assert_eq!(f.get0::<f64>()?()?, 4.0);
|
assert_eq!(f.get0::<f64>()?()?, 4.0);
|
||||||
Ok(())
|
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,
|
&module,
|
||||||
&[
|
&[
|
||||||
func.into(),
|
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();
|
let func = instance.exports()[0].func().unwrap().clone();
|
||||||
@@ -329,7 +329,7 @@ fn rust_panic_start_function() -> Result<()> {
|
|||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
|
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(|| {
|
let err = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||||
drop(Instance::new(&module, &[func.into()]));
|
drop(Instance::new(&module, &[func.into()]));
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -182,24 +182,26 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let format_str = format!("{}({})", name, formats.join(", "));
|
let format_str = format!("{}({})", name, formats.join(", "));
|
||||||
let wrap = format_ident!("wrap{}", shim_arg_decls.len() + 1);
|
|
||||||
ctor_externs.push(quote! {
|
ctor_externs.push(quote! {
|
||||||
let my_cx = cx.clone();
|
let my_cx = cx.clone();
|
||||||
let #name_ident = wasmtime::Func::#wrap(
|
let #name_ident = wasmtime::Func::wrap(
|
||||||
store,
|
store,
|
||||||
move |mem: crate::WasiCallerMemory #(,#shim_arg_decls)*| -> #ret_ty {
|
move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
#format_str,
|
#format_str,
|
||||||
#(#format_args),*
|
#(#format_args),*
|
||||||
);
|
);
|
||||||
unsafe {
|
unsafe {
|
||||||
let memory = match mem.get() {
|
let memory = match caller.get_export("memory") {
|
||||||
Ok(e) => e,
|
Some(wasmtime::Extern::Memory(m)) => m,
|
||||||
Err(e) => #handle_early_error,
|
_ => {
|
||||||
|
let e = wasi_common::wasi::__WASI_ERRNO_INVAL;
|
||||||
|
#handle_early_error
|
||||||
|
}
|
||||||
};
|
};
|
||||||
hostcalls::#name_ident(
|
hostcalls::#name_ident(
|
||||||
&mut my_cx.borrow_mut(),
|
&mut my_cx.borrow_mut(),
|
||||||
memory,
|
memory.data_unchecked_mut(),
|
||||||
#(#hostcall_args),*
|
#(#hostcall_args),*
|
||||||
) #cvt_ret
|
) #cvt_ret
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,58 +16,3 @@ pub fn is_wasi_module(name: &str) -> bool {
|
|||||||
// trick.
|
// trick.
|
||||||
name.starts_with("wasi")
|
name.starts_with("wasi")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is an internal structure used to acquire a handle on the caller's
|
|
||||||
/// wasm memory buffer.
|
|
||||||
///
|
|
||||||
/// This exploits how we can implement `WasmTy` for ourselves locally even
|
|
||||||
/// though crates in general should not be doing that. This is a crate in
|
|
||||||
/// the wasmtime project, however, so we should be able to keep up with our own
|
|
||||||
/// changes.
|
|
||||||
///
|
|
||||||
/// In general this type is wildly unsafe. We need to update the wasi crates to
|
|
||||||
/// probably work with more `wasmtime`-like APIs to grip with the unsafety
|
|
||||||
/// around dealing with caller memory.
|
|
||||||
struct WasiCallerMemory {
|
|
||||||
base: *mut u8,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl wasmtime::WasmTy for WasiCallerMemory {
|
|
||||||
type Abi = ();
|
|
||||||
|
|
||||||
fn push(_dst: &mut Vec<wasmtime::ValType>) {}
|
|
||||||
|
|
||||||
fn matches(_tys: impl Iterator<Item = wasmtime::ValType>) -> anyhow::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_abi(vmctx: *mut wasmtime_runtime::VMContext, _abi: ()) -> Self {
|
|
||||||
unsafe {
|
|
||||||
match wasmtime_runtime::InstanceHandle::from_vmctx(vmctx).lookup("memory") {
|
|
||||||
Some(wasmtime_runtime::Export::Memory(m)) => WasiCallerMemory {
|
|
||||||
base: (*m.definition).base,
|
|
||||||
len: (*m.definition).current_length,
|
|
||||||
},
|
|
||||||
_ => WasiCallerMemory {
|
|
||||||
base: std::ptr::null_mut(),
|
|
||||||
len: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_abi(self) {}
|
|
||||||
unsafe fn load(_ptr: &mut *const u128) {}
|
|
||||||
unsafe fn store(_abi: Self::Abi, _ptr: *mut u128) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasiCallerMemory {
|
|
||||||
unsafe fn get(&self) -> Result<&mut [u8], wasi_common::wasi::__wasi_errno_t> {
|
|
||||||
if self.base.is_null() {
|
|
||||||
Err(wasi_common::wasi::__WASI_ERRNO_INVAL)
|
|
||||||
} else {
|
|
||||||
Ok(std::slice::from_raw_parts_mut(self.base, self.len))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,28 +6,28 @@ use wasmtime::*;
|
|||||||
pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
|
pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
|
||||||
let mut ret = HashMap::new();
|
let mut ret = HashMap::new();
|
||||||
|
|
||||||
let func = Func::wrap0(store, || {});
|
let func = Func::wrap(store, || {});
|
||||||
ret.insert("print", Extern::Func(func));
|
ret.insert("print", Extern::Func(func));
|
||||||
|
|
||||||
let func = Func::wrap1(store, |val: i32| println!("{}: i32", val));
|
let func = Func::wrap(store, |val: i32| println!("{}: i32", val));
|
||||||
ret.insert("print_i32", Extern::Func(func));
|
ret.insert("print_i32", Extern::Func(func));
|
||||||
|
|
||||||
let func = Func::wrap1(store, |val: i64| println!("{}: i64", val));
|
let func = Func::wrap(store, |val: i64| println!("{}: i64", val));
|
||||||
ret.insert("print_i64", Extern::Func(func));
|
ret.insert("print_i64", Extern::Func(func));
|
||||||
|
|
||||||
let func = Func::wrap1(store, |val: f32| println!("{}: f32", val));
|
let func = Func::wrap(store, |val: f32| println!("{}: f32", val));
|
||||||
ret.insert("print_f32", Extern::Func(func));
|
ret.insert("print_f32", Extern::Func(func));
|
||||||
|
|
||||||
let func = Func::wrap1(store, |val: f64| println!("{}: f64", val));
|
let func = Func::wrap(store, |val: f64| println!("{}: f64", val));
|
||||||
ret.insert("print_f64", Extern::Func(func));
|
ret.insert("print_f64", Extern::Func(func));
|
||||||
|
|
||||||
let func = Func::wrap2(store, |i: i32, f: f32| {
|
let func = Func::wrap(store, |i: i32, f: f32| {
|
||||||
println!("{}: i32", i);
|
println!("{}: i32", i);
|
||||||
println!("{}: f32", f);
|
println!("{}: f32", f);
|
||||||
});
|
});
|
||||||
ret.insert("print_i32_f32", Extern::Func(func));
|
ret.insert("print_i32_f32", Extern::Func(func));
|
||||||
|
|
||||||
let func = Func::wrap2(store, |f1: f64, f2: f64| {
|
let func = Func::wrap(store, |f1: f64, f2: f64| {
|
||||||
println!("{}: f64", f1);
|
println!("{}: f64", f1);
|
||||||
println!("{}: f64", f2);
|
println!("{}: f64", f2);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -149,12 +149,12 @@ looks like this:
|
|||||||
# (import "" "double" (func $double (param i32) (result i32))))"#)?;
|
# (import "" "double" (func $double (param i32) (result i32))))"#)?;
|
||||||
// First we can create our `log` function, which will simply print out the
|
// First we can create our `log` function, which will simply print out the
|
||||||
// parameter it receives.
|
// parameter it receives.
|
||||||
let log = Func::wrap1(&store, |param: i32| {
|
let log = Func::wrap(&store, |param: i32| {
|
||||||
println!("log: {}", param);
|
println!("log: {}", param);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Next we can create our double function which doubles the input it receives.
|
// Next we can create our double function which doubles the input it receives.
|
||||||
let double = Func::wrap1(&store, |param: i32| param * 2);
|
let double = Func::wrap(&store, |param: i32| param * 2);
|
||||||
|
|
||||||
// When instantiating the module we now need to provide the imports to the
|
// When instantiating the module we now need to provide the imports to the
|
||||||
// instantiation process. This is the second slice argument, where each
|
// instantiation process. This is the second slice argument, where each
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ fn main() -> Result<()> {
|
|||||||
// Here we handle the imports of the module, which in this case is our
|
// Here we handle the imports of the module, which in this case is our
|
||||||
// `HelloCallback` type and its associated implementation of `Callback.
|
// `HelloCallback` type and its associated implementation of `Callback.
|
||||||
println!("Creating callback...");
|
println!("Creating callback...");
|
||||||
let hello_func = Func::wrap0(&store, || {
|
let hello_func = Func::wrap(&store, || {
|
||||||
println!("Calling back...");
|
println!("Calling back...");
|
||||||
println!("> Hello World!");
|
println!("> Hello World!");
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user