diff --git a/crates/api/src/func.rs b/crates/api/src/func.rs index 69c0848a6130..368b34ac276b 100644 --- a/crates/api/src/func.rs +++ b/crates/api/src/func.rs @@ -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,65 +148,6 @@ pub struct Func { ty: FuncType, } -macro_rules! wrappers { - ($( - $(#[$doc:meta])* - ($name:ident $(,$args:ident)*) - )*) => ($( - $(#[$doc])* - pub fn $name(store: &Store, func: F) -> Func - where - F: Fn($($args),*) -> R + 'static, - $($args: WasmTy,)* - R: WasmRet, - { - #[allow(non_snake_case)] - unsafe extern "C" fn shim( - 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::().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), - } - } - - let mut _args = Vec::new(); - $($args::push(&mut _args);)* - let mut ret = Vec::new(); - R::push(&mut ret); - let ty = FuncType::new(_args.into(), ret.into()); - unsafe { - let (instance, export) = crate::trampoline::generate_raw_func_export( - &ty, - std::slice::from_raw_parts_mut( - shim:: as *mut _, - 0, - ), - store, - Box::new(func), - ) - .expect("failed to generate export"); - let callable = Rc::new(WasmtimeFn::new(store, instance, export)); - Func::from_wrapped(store, ty, callable) - } - } - )*) -} - macro_rules! getters { ($( $(#[$doc:meta])* @@ -252,15 +193,15 @@ macro_rules! getters { unsafe extern "C" fn( *mut VMContext, *mut VMContext, - $($args::Abi,)* - ) -> R::Abi, + $($args,)* + ) -> R, >(address); let mut ret = None; $(let $args = $args.into_abi();)* wasmtime_runtime::catch_traps(vmctx, || { ret = Some(f(vmctx, ptr::null_mut(), $($args,)*)); }).map_err(Trap::from_jit)?; - Ok(R::from_abi(vmctx, ret.unwrap())) + Ok(ret.unwrap()) } }) } @@ -288,220 +229,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` | 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::()?; - /// 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::()?; - /// 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` | 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::()?; + /// 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::()?; + /// 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(store: &Store, func: impl IntoFunc) -> Func { + func.into_func(store) } fn from_wrapped( @@ -597,7 +512,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 /// @@ -709,44 +624,28 @@ 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); #[doc(hidden)] fn matches(tys: impl Iterator) -> anyhow::Result<()>; - #[doc(hidden)] - fn from_abi(vmctx: *mut VMContext, abi: Self::Abi) -> Self; - #[doc(hidden)] - fn into_abi(self) -> Self::Abi; } -impl WasmTy for () { - type Abi = (); +unsafe impl WasmTy for () { fn push(_dst: &mut Vec) {} fn matches(_tys: impl Iterator) -> anyhow::Result<()> { Ok(()) } - #[inline] - fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self { - abi - } - #[inline] - fn into_abi(self) -> Self::Abi { - self - } } -impl WasmTy for i32 { - type Abi = Self; +unsafe impl WasmTy for i32 { fn push(dst: &mut Vec) { dst.push(ValType::I32); } @@ -759,18 +658,9 @@ 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 - } } -impl WasmTy for i64 { - type Abi = Self; +unsafe impl WasmTy for i64 { fn push(dst: &mut Vec) { dst.push(ValType::I64); } @@ -783,18 +673,9 @@ 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 - } } -impl WasmTy for f32 { - type Abi = Self; +unsafe impl WasmTy for f32 { fn push(dst: &mut Vec) { dst.push(ValType::F32); } @@ -807,18 +688,9 @@ 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 - } } -impl WasmTy for f64 { - type Abi = Self; +unsafe impl WasmTy for f64 { fn push(dst: &mut Vec) { dst.push(ValType::F64); } @@ -831,25 +703,17 @@ 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 - } } /// 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)] @@ -860,8 +724,8 @@ pub trait WasmRet { fn into_abi(self) -> Self::Abi; } -impl WasmRet for T { - type Abi = T::Abi; +unsafe impl WasmRet for T { + type Abi = T; fn push(dst: &mut Vec) { T::push(dst) } @@ -872,12 +736,12 @@ impl WasmRet for T { #[inline] fn into_abi(self) -> Self::Abi { - T::into_abi(self) + self } } -impl WasmRet for Result { - type Abi = T::Abi; +unsafe impl WasmRet for Result { + type Abi = T; fn push(dst: &mut Vec) { T::push(dst) } @@ -898,3 +762,167 @@ impl WasmRet for Result { } } } + +/// 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 { + #[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. +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. + /// + /// 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 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 { + unsafe { + if self.caller_vmctx.is_null() { + return None; + } + let instance = InstanceHandle::from_vmctx(self.caller_vmctx); + let export = match instance.lookup(name) { + Some(m @ Export::Memory { .. }) => 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 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),*) + }) + } + } + + impl 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. + #[allow(non_snake_case)] + unsafe extern "C" fn shim( + 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), + } + } + + 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 (instance, export) = crate::trampoline::generate_raw_func_export( + &ty, + std::slice::from_raw_parts_mut( + shim:: as *mut _, + 0, + ), + store, + Box::new((self, store_clone)), + ) + .expect("failed to generate export"); + let callable = Rc::new(WasmtimeFn::new(store, instance, export)); + 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) +} diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index a5d5e8358d24..785b864d60a7 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -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}; diff --git a/crates/api/tests/externals.rs b/crates/api/tests/externals.rs index 2635f8acee25..ba9978cbb960 100644 --- a/crates/api/tests/externals.rs +++ b/crates/api/tests/externals.rs @@ -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()); diff --git a/crates/api/tests/func.rs b/crates/api/tests/func.rs index dcb1a1d8c9a4..9a05785d4f6e 100644 --- a/crates/api/tests/func.rs +++ b/crates/api/tests/func.rs @@ -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::wrap0(&store, || -> Result<(), Trap> { loop {} }); - Func::wrap0(&store, || -> Result { loop {} }); - Func::wrap0(&store, || -> Result { loop {} }); - Func::wrap0(&store, || -> Result { loop {} }); - Func::wrap0(&store, || -> Result { loop {} }); + 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::wrap(&store, || -> Result<(), Trap> { loop {} }); + Func::wrap(&store, || -> Result { loop {} }); + Func::wrap(&store, || -> Result { loop {} }); + Func::wrap(&store, || -> Result { loop {} }); + Func::wrap(&store, || -> Result { 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::().is_err()); assert!(f.get1::<(), ()>().is_ok()); @@ -216,23 +216,23 @@ fn get_from_wrapper() { assert!(f.get2::().is_err()); assert!(f.get2::().is_err()); - let f = Func::wrap0(&store, || -> i32 { loop {} }); + let f = Func::wrap(&store, || -> i32 { loop {} }); assert!(f.get0::().is_ok()); - let f = Func::wrap0(&store, || -> f32 { loop {} }); + let f = Func::wrap(&store, || -> f32 { loop {} }); assert!(f.get0::().is_ok()); - let f = Func::wrap0(&store, || -> f64 { loop {} }); + let f = Func::wrap(&store, || -> f64 { loop {} }); assert!(f.get0::().is_ok()); - let f = Func::wrap1(&store, |_: i32| {}); + let f = Func::wrap(&store, |_: i32| {}); assert!(f.get1::().is_ok()); assert!(f.get1::().is_err()); assert!(f.get1::().is_err()); assert!(f.get1::().is_err()); - let f = Func::wrap1(&store, |_: i64| {}); + let f = Func::wrap(&store, |_: i64| {}); assert!(f.get1::().is_ok()); - let f = Func::wrap1(&store, |_: f32| {}); + let f = Func::wrap(&store, |_: f32| {}); assert!(f.get1::().is_ok()); - let f = Func::wrap1(&store, |_: f64| {}); + let f = Func::wrap(&store, |_: f64| {}); assert!(f.get1::().is_ok()); } @@ -289,3 +289,68 @@ fn get_from_module() -> anyhow::Result<()> { assert!(f2.get1::().is_err()); 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(()) +} diff --git a/crates/api/tests/traps.rs b/crates/api/tests/traps.rs index 5ef3bf264406..0705b09427f4 100644 --- a/crates/api/tests/traps.rs +++ b/crates/api/tests/traps.rs @@ -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()])); })) diff --git a/crates/wasi-common/wig/src/wasi.rs b/crates/wasi-common/wig/src/wasi.rs index 4e681bfc4733..14a38117d53b 100644 --- a/crates/wasi-common/wig/src/wasi.rs +++ b/crates/wasi-common/wig/src/wasi.rs @@ -182,24 +182,26 @@ pub fn define_struct(args: TokenStream) -> TokenStream { } let format_str = format!("{}({})", name, formats.join(", ")); - let wrap = format_ident!("wrap{}", shim_arg_decls.len() + 1); ctor_externs.push(quote! { let my_cx = cx.clone(); - let #name_ident = wasmtime::Func::#wrap( + let #name_ident = wasmtime::Func::wrap( store, - move |mem: crate::WasiCallerMemory #(,#shim_arg_decls)*| -> #ret_ty { + move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { log::trace!( #format_str, #(#format_args),* ); unsafe { - let memory = match mem.get() { - Ok(e) => e, - Err(e) => #handle_early_error, + let memory = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + let e = wasi_common::wasi::__WASI_ERRNO_INVAL; + #handle_early_error + } }; hostcalls::#name_ident( &mut my_cx.borrow_mut(), - memory, + memory.data_unchecked_mut(), #(#hostcall_args),* ) #cvt_ret } diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 5a6d29c1992a..bfa75c248d70 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -16,60 +16,3 @@ pub fn is_wasi_module(name: &str) -> bool { // trick. 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) {} - - fn matches(_tys: impl Iterator) -> 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 { - definition, - vmctx: _, - memory: _, - }) => WasiCallerMemory { - base: (*definition).base, - len: (*definition).current_length, - }, - _ => WasiCallerMemory { - base: std::ptr::null_mut(), - len: 0, - }, - } - } - } - - fn into_abi(self) {} -} - -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)) - } - } -} diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 4fcfe190f2d6..3004e1e04d43 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -6,28 +6,28 @@ use wasmtime::*; pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> { let mut ret = HashMap::new(); - let func = Func::wrap0(store, || {}); + let func = Func::wrap(store, || {}); 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)); - 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)); - 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)); - 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)); - let func = Func::wrap2(store, |i: i32, f: f32| { + let func = Func::wrap(store, |i: i32, f: f32| { println!("{}: i32", i); println!("{}: f32", f); }); 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", f2); }); diff --git a/docs/embed-rust.md b/docs/embed-rust.md index 391f42103cb2..36ab0bc7ea73 100644 --- a/docs/embed-rust.md +++ b/docs/embed-rust.md @@ -149,12 +149,12 @@ looks like this: # (import "" "double" (func $double (param i32) (result i32))))"#)?; // First we can create our `log` function, which will simply print out the // parameter it receives. -let log = Func::wrap1(&store, |param: i32| { +let log = Func::wrap(&store, |param: i32| { println!("log: {}", param); }); // 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 // instantiation process. This is the second slice argument, where each diff --git a/examples/hello.rs b/examples/hello.rs index 8d3ddb04f643..a23974942ad6 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -20,7 +20,7 @@ fn main() -> Result<()> { // Here we handle the imports of the module, which in this case is our // `HelloCallback` type and its associated implementation of `Callback. println!("Creating callback..."); - let hello_func = Func::wrap0(&store, || { + let hello_func = Func::wrap(&store, || { println!("Calling back..."); println!("> Hello World!"); });