use crate::runtime::StoreInner;
use crate::trampoline::StoreInstanceHandle;
use crate::{Extern, ExternRef, FuncType, Memory, Store, Trap, Val, ValType};
use anyhow::{bail, ensure, Context as _, Result};
use smallvec::{smallvec, SmallVec};
use std::cmp::max;
use std::fmt;
use std::mem;
use std::panic::{self, AssertUnwindSafe};
use std::ptr::{self, NonNull};
use std::rc::Weak;
use wasmtime_runtime::{
raise_user_trap, Export, InstanceHandle, VMContext, VMFunctionBody, VMSharedSignatureIndex,
VMTrampoline,
};
#[derive(Clone)]
pub struct Func {
instance: StoreInstanceHandle,
trampoline: VMTrampoline,
export: wasmtime_runtime::ExportFunction,
}
macro_rules! getters {
($(
$(#[$doc:meta])*
($name:ident $(,$args:ident)*)
)*) => ($(
$(#[$doc])*
#[allow(non_snake_case)]
pub fn $name<$($args,)* R>(&self)
-> anyhow::Result<impl Fn($($args,)*) -> Result<R, Trap>>
where
$($args: WasmTy,)*
R: WasmTy,
{
let ty = self.ty();
let mut params = ty.params().iter().cloned();
let n = 0;
$(
let n = n + 1;
$args::matches(&mut params)
.with_context(|| format!("Type mismatch in argument {}", n))?;
)*
ensure!(params.next().is_none(), "Type mismatch: too many arguments (expected {})", n);
let mut results = ty.results().iter().cloned();
R::matches(&mut results)
.context("Type mismatch in return type")?;
ensure!(results.next().is_none(), "Type mismatch: too many return values (expected 1)");
let instance = self.instance.clone();
let anyfunc = self.export.anyfunc;
Ok(move |$($args: $args),*| -> Result<R, Trap> {
unsafe {
let fnptr = mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(
*mut VMContext,
*mut VMContext,
$( $args::Abi, )*
) -> R::Abi,
>(anyfunc.as_ref().func_ptr.as_ptr());
let mut ret = None;
let weak_store = instance.store.weak();
let weak_store = WeakStore(&weak_store);
$(
// Because this returned closure is not marked `unsafe`,
// we have to check that incoming values are compatible
// with our store.
if !$args.compatible_with_store(weak_store) {
return Err(Trap::new(
"attempt to pass cross-`Store` value to Wasm as function argument"
));
}
let $args = $args.into_abi_for_arg(weak_store);
)*
invoke_wasm_and_catch_traps(anyfunc.as_ref().vmctx, &instance.store, || {
ret = Some(fnptr(
anyfunc.as_ref().vmctx,
ptr::null_mut(),
$( $args, )*
));
})?;
Ok(R::from_abi(ret.unwrap(), weak_store))
}
})
}
)*)
}
impl Func {
pub fn new(
store: &Store,
ty: FuncType,
func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + 'static,
) -> Self {
let store_weak = store.weak();
let ty_clone = ty.clone();
let func = Box::new(move |caller_vmctx, values_vec: *mut u128| {
const STACK_ARGS: usize = 4;
const STACK_RETURNS: usize = 2;
let mut args: SmallVec<[Val; STACK_ARGS]> =
SmallVec::with_capacity(ty_clone.params().len());
let store = Store::upgrade(&store_weak).unwrap();
for (i, ty) in ty_clone.params().iter().enumerate() {
unsafe {
let val = Val::read_value_from(&store, values_vec.add(i), ty);
args.push(val);
}
}
let mut returns: SmallVec<[Val; STACK_RETURNS]> =
smallvec![Val::null(); ty_clone.results().len()];
func(
Caller {
store: &store_weak,
caller_vmctx,
},
&args,
&mut returns,
)?;
for (i, (ret, ty)) in returns.into_iter().zip(ty_clone.results()).enumerate() {
if ret.ty() != *ty {
return Err(Trap::new(
"function attempted to return an incompatible value",
));
}
unsafe {
ret.write_value_to(&store, values_vec.add(i));
}
}
Ok(())
});
let (instance, export, trampoline) =
crate::trampoline::generate_func_export(&ty, func, store).expect("generated func");
Func {
instance,
trampoline,
export,
}
}
pub(crate) unsafe fn from_caller_checked_anyfunc(
store: &Store,
anyfunc: *mut wasmtime_runtime::VMCallerCheckedAnyfunc,
) -> Option<Self> {
let anyfunc = NonNull::new(anyfunc)?;
debug_assert!(
anyfunc.as_ref().type_index != wasmtime_runtime::VMSharedSignatureIndex::default()
);
let instance_handle = wasmtime_runtime::InstanceHandle::from_vmctx(anyfunc.as_ref().vmctx);
let export = wasmtime_runtime::ExportFunction { anyfunc };
let instance = store.existing_instance_handle(instance_handle);
let f = Func::from_wasmtime_function(export, instance);
Some(f)
}
pub fn wrap<Params, Results>(store: &Store, func: impl IntoFunc<Params, Results>) -> Func {
func.into_func(store)
}
pub(crate) fn sig_index(&self) -> VMSharedSignatureIndex {
unsafe { self.export.anyfunc.as_ref().type_index }
}
pub fn ty(&self) -> FuncType {
let wft = self.instance.store.lookup_signature(self.sig_index());
FuncType::from_wasm_func_type(&wft)
}
pub fn param_arity(&self) -> usize {
let sig = self
.instance
.store
.lookup_signature(unsafe { self.export.anyfunc.as_ref().type_index });
sig.params.len()
}
pub fn result_arity(&self) -> usize {
let sig = self
.instance
.store
.lookup_signature(unsafe { self.export.anyfunc.as_ref().type_index });
sig.returns.len()
}
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>> {
let my_ty = self.ty();
if my_ty.params().len() != params.len() {
bail!(
"expected {} arguments, got {}",
my_ty.params().len(),
params.len()
);
}
let mut values_vec = vec![0; max(params.len(), my_ty.results().len())];
let param_tys = my_ty.params().iter();
for ((arg, slot), ty) in params.iter().cloned().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != *ty {
bail!(
"argument type mismatch: found {} but expected {}",
arg.ty(),
ty
);
}
if !arg.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` values are not currently supported");
}
unsafe {
arg.write_value_to(&self.instance.store, slot);
}
}
unsafe {
let anyfunc = self.export.anyfunc.as_ref();
invoke_wasm_and_catch_traps(anyfunc.vmctx, &self.instance.store, || {
(self.trampoline)(
anyfunc.vmctx,
ptr::null_mut(),
anyfunc.func_ptr.as_ptr(),
values_vec.as_mut_ptr(),
)
})?;
}
let mut results = Vec::with_capacity(my_ty.results().len());
for (index, ty) in my_ty.results().iter().enumerate() {
unsafe {
let ptr = values_vec.as_ptr().add(index);
results.push(Val::read_value_from(&self.instance.store, ptr, ty));
}
}
Ok(results.into())
}
pub(crate) fn caller_checked_anyfunc(
&self,
) -> NonNull<wasmtime_runtime::VMCallerCheckedAnyfunc> {
self.export.anyfunc
}
pub(crate) fn from_wasmtime_function(
export: wasmtime_runtime::ExportFunction,
instance: StoreInstanceHandle,
) -> Self {
let trampoline = instance
.trampoline(unsafe { export.anyfunc.as_ref().type_index })
.expect("failed to retrieve trampoline from module");
Func {
instance,
export,
trampoline,
}
}
getters! {
(get0)
(get1, A1)
(get2, A1, A2)
(get3, A1, A2, A3)
(get4, A1, A2, A3, A4)
(get5, A1, A2, A3, A4, A5)
(get6, A1, A2, A3, A4, A5, A6)
(get7, A1, A2, A3, A4, A5, A6, A7)
(get8, A1, A2, A3, A4, A5, A6, A7, A8)
(get9, A1, A2, A3, A4, A5, A6, A7, A8, A9)
(get10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)
(get11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)
(get12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)
(get13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)
(get14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)
(get15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)
}
pub fn store(&self) -> &Store {
&self.instance.store
}
pub(crate) fn matches_expected(&self, expected: VMSharedSignatureIndex) -> bool {
self.sig_index() == expected
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMFunctionImport {
unsafe {
let f = self.caller_checked_anyfunc();
wasmtime_runtime::VMFunctionImport {
body: f.as_ref().func_ptr,
vmctx: f.as_ref().vmctx,
}
}
}
}
impl fmt::Debug for Func {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Func")
}
}
pub(crate) fn invoke_wasm_and_catch_traps(
vmctx: *mut VMContext,
store: &Store,
closure: impl FnMut(),
) -> Result<(), Trap> {
let signalhandler = store.signal_handler();
unsafe {
let canary = 0;
let _auto_reset_canary = store
.externref_activations_table()
.set_stack_canary(&canary);
wasmtime_runtime::catch_traps(
vmctx,
store.engine().config().max_wasm_stack,
|addr| store.is_in_jit_code(addr),
signalhandler.as_deref(),
closure,
)
.map_err(Trap::from_runtime)
}
}
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct WeakStore<'a>(&'a Weak<StoreInner>);
pub unsafe trait WasmTy {
#[doc(hidden)]
type Abi: Copy;
#[doc(hidden)]
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool;
#[doc(hidden)]
fn into_abi_for_arg<'a>(self, store: WeakStore<'a>) -> Self::Abi;
#[doc(hidden)]
unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self;
#[doc(hidden)]
fn push(dst: &mut Vec<ValType>);
#[doc(hidden)]
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
#[doc(hidden)]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi;
#[doc(hidden)]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128);
}
pub unsafe trait WasmRet {
#[doc(hidden)]
type Abi: Copy;
#[doc(hidden)]
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool;
#[doc(hidden)]
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi;
#[doc(hidden)]
unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self;
#[doc(hidden)]
fn push(dst: &mut Vec<ValType>);
#[doc(hidden)]
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
#[doc(hidden)]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi;
#[doc(hidden)]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128);
}
unsafe impl WasmTy for () {
type Abi = Self;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {}
#[inline]
unsafe fn from_abi<'a>(_abi: Self::Abi, _store: WeakStore<'a>) -> Self {}
fn push(_dst: &mut Vec<ValType>) {}
fn matches(_tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
Ok(())
}
#[inline]
unsafe fn load_from_args(_ptr: &mut *const u128) -> Self::Abi {}
#[inline]
unsafe fn store_to_args(_abi: Self::Abi, _ptr: *mut u128) {}
}
unsafe impl WasmTy for i32 {
type Abi = Self;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
self
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
abi
}
fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::I32);
}
fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
let next = tys.next();
ensure!(
next == Some(ValType::I32),
"Type mismatch, expected i32, got {:?}",
next
);
Ok(())
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
let ret = **ptr as Self;
*ptr = (*ptr).add(1);
return ret;
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
*ptr = abi as u128;
}
}
unsafe impl WasmTy for u32 {
type Abi = <i32 as WasmTy>::Abi;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
self as i32
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
abi as Self
}
fn push(dst: &mut Vec<ValType>) {
<i32 as WasmTy>::push(dst)
}
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
<i32 as WasmTy>::matches(tys)
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
<i32 as WasmTy>::load_from_args(ptr)
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
<i32 as WasmTy>::store_to_args(abi, ptr)
}
}
unsafe impl WasmTy for i64 {
type Abi = Self;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
self
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
abi
}
fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::I64);
}
fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
let next = tys.next();
ensure!(
next == Some(ValType::I64),
"Type mismatch, expected i64, got {:?}",
next
);
Ok(())
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
let ret = **ptr as Self;
*ptr = (*ptr).add(1);
return ret;
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
*ptr = abi as u128;
}
}
unsafe impl WasmTy for u64 {
type Abi = <i64 as WasmTy>::Abi;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
self as i64
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
abi as Self
}
fn push(dst: &mut Vec<ValType>) {
<i64 as WasmTy>::push(dst)
}
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
<i64 as WasmTy>::matches(tys)
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
<i64 as WasmTy>::load_from_args(ptr)
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
<i64 as WasmTy>::store_to_args(abi, ptr)
}
}
unsafe impl WasmTy for f32 {
type Abi = Self;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
self
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
abi
}
fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::F32);
}
fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
let next = tys.next();
ensure!(
next == Some(ValType::F32),
"Type mismatch, expected f32, got {:?}",
next
);
Ok(())
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
let ret = f32::from_bits(**ptr as u32);
*ptr = (*ptr).add(1);
return ret;
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
*ptr = abi.to_bits() as u128;
}
}
unsafe impl WasmTy for f64 {
type Abi = Self;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
self
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
abi
}
fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::F64);
}
fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
let next = tys.next();
ensure!(
next == Some(ValType::F64),
"Type mismatch, expected f64, got {:?}",
next
);
Ok(())
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
let ret = f64::from_bits(**ptr as u64);
*ptr = (*ptr).add(1);
return ret;
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
*ptr = abi.to_bits() as u128;
}
}
unsafe impl WasmTy for Option<ExternRef> {
type Abi = *mut u8;
#[inline]
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
true
}
#[inline]
fn into_abi_for_arg<'a>(self, store: WeakStore<'a>) -> Self::Abi {
if let Some(x) = self {
let store = Store::upgrade(store.0).unwrap();
let abi = x.inner.as_raw();
unsafe {
store
.externref_activations_table()
.insert_with_gc(x.inner, store.stack_map_registry());
}
abi
} else {
ptr::null_mut()
}
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self {
if abi.is_null() {
None
} else {
Some(ExternRef {
inner: wasmtime_runtime::VMExternRef::clone_from_raw(abi),
})
}
}
fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::ExternRef);
}
fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
let next = tys.next();
ensure!(
next == Some(ValType::ExternRef),
"Type mismatch, expected externref, got {:?}",
next
);
Ok(())
}
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
let ret = **ptr as usize as *mut u8;
*ptr = (*ptr).add(1);
ret
}
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
ptr::write(ptr, abi as usize as u128);
}
}
unsafe impl WasmTy for Option<Func> {
type Abi = *mut wasmtime_runtime::VMCallerCheckedAnyfunc;
#[inline]
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool {
if let Some(f) = self {
let store = Store::upgrade(store.0).unwrap();
Store::same(&store, f.store())
} else {
true
}
}
#[inline]
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
if let Some(f) = self {
f.caller_checked_anyfunc().as_ptr()
} else {
ptr::null_mut()
}
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self {
let store = Store::upgrade(store.0).unwrap();
Func::from_caller_checked_anyfunc(&store, abi)
}
fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::FuncRef);
}
fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
let next = tys.next();
ensure!(
next == Some(ValType::FuncRef),
"Type mismatch, expected funcref, got {:?}",
next
);
Ok(())
}
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
let ret = **ptr as usize as *mut wasmtime_runtime::VMCallerCheckedAnyfunc;
*ptr = (*ptr).add(1);
ret
}
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
ptr::write(ptr, abi as usize as u128);
}
}
unsafe impl<T> WasmRet for T
where
T: WasmTy,
{
type Abi = <T as WasmTy>::Abi;
#[inline]
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool {
<Self as WasmTy>::compatible_with_store(self, store)
}
#[inline]
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi {
<Self as WasmTy>::into_abi_for_arg(self, store)
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self {
<Self as WasmTy>::from_abi(abi, store)
}
#[inline]
fn push(dst: &mut Vec<ValType>) {
<Self as WasmTy>::push(dst)
}
#[inline]
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
<Self as WasmTy>::matches(tys)
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
<Self as WasmTy>::load_from_args(ptr)
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
<Self as WasmTy>::store_to_args(abi, ptr)
}
}
unsafe impl<T> WasmRet for Result<T, Trap>
where
T: WasmTy,
{
type Abi = <T as WasmTy>::Abi;
#[inline]
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool {
match self {
Ok(x) => <T as WasmTy>::compatible_with_store(x, store),
Err(_) => true,
}
}
#[inline]
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi {
match self {
Ok(val) => return <T as WasmTy>::into_abi_for_arg(val, store),
Err(trap) => handle_trap(trap),
}
unsafe fn handle_trap(trap: Trap) -> ! {
raise_user_trap(trap.into())
}
}
#[inline]
unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self {
Ok(<T as WasmTy>::from_abi(abi, store))
}
fn push(dst: &mut Vec<ValType>) {
<T as WasmTy>::push(dst)
}
fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
<T as WasmTy>::matches(tys)
}
#[inline]
unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi {
<T as WasmTy>::load_from_args(ptr)
}
#[inline]
unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) {
<T as WasmTy>::store_to_args(abi, ptr);
}
}
pub trait IntoFunc<Params, Results> {
#[doc(hidden)]
fn into_func(self, store: &Store) -> Func;
}
pub struct Caller<'a> {
store: &'a Weak<StoreInner>,
caller_vmctx: *mut VMContext,
}
impl Caller<'_> {
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 = instance.lookup(name)?;
debug_assert!(self.store.upgrade().is_some());
let handle =
Store::from_inner(self.store.upgrade()?).existing_instance_handle(instance);
match export {
Export::Memory(m) => Some(Extern::Memory(Memory::from_wasmtime_memory(m, handle))),
Export::Function(f) => Some(Extern::Func(Func::from_wasmtime_function(f, handle))),
_ => None,
}
}
}
pub fn store(&self) -> Store {
Store::upgrade(&self.store).unwrap()
}
}
#[inline(never)]
#[cold]
unsafe fn raise_cross_store_trap() -> ! {
#[derive(Debug)]
struct CrossStoreError;
impl std::error::Error for CrossStoreError {}
impl fmt::Display for CrossStoreError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"host function attempted to return cross-`Store` \
value to Wasm",
)
}
}
raise_user_trap(Box::new(CrossStoreError));
}
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 {
unsafe extern "C" fn wasm_to_host_shim<F, $($args,)* R>(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
$( $args: $args::Abi, )*
) -> R::Abi
where
F: Fn(Caller<'_>, $( $args ),*) -> R + 'static,
$( $args: WasmTy, )*
R: WasmRet,
{
let state = (*vmctx).host_state();
debug_assert!(state.is::<(F, Weak<StoreInner>)>());
let (func, store) = &*(state as *const _ as *const (F, Weak<StoreInner>));
let weak_store = WeakStore(store);
let ret = {
panic::catch_unwind(AssertUnwindSafe(|| {
func(
Caller { store, caller_vmctx },
$( $args::from_abi($args, weak_store), )*
)
}))
};
match ret {
Err(panic) => wasmtime_runtime::resume_panic(panic),
Ok(ret) => {
if !ret.compatible_with_store(weak_store) {
raise_cross_store_trap();
}
ret.into_abi_for_ret(weak_store)
}
}
}
unsafe extern "C" fn host_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::Abi, )*
) -> R::Abi,
>(ptr);
let mut _next = args as *const u128;
$( let $args = $args::load_from_args(&mut _next); )*
let ret = ptr(callee_vmctx, caller_vmctx, $( $args ),*);
R::store_to_args(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_weak = store.weak();
let trampoline = host_trampoline::<$($args,)* R>;
let (instance, export) = unsafe {
crate::trampoline::generate_raw_func_export(
&ty,
std::slice::from_raw_parts_mut(
wasm_to_host_shim::<F, $($args,)* R> as *mut _,
0,
),
trampoline,
store,
Box::new((self, store_weak)),
)
.expect("failed to generate export")
};
Func {
instance,
export,
trampoline,
}
}
}
)*)
}
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)
(A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16)
}
#[test]
fn wasm_ty_roundtrip() -> Result<(), anyhow::Error> {
use crate::*;
let store = Store::default();
let debug = Func::wrap(&store, |a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| {
assert_eq!(a, -1);
assert_eq!(b, 1);
assert_eq!(c, 2.0);
assert_eq!(d, -3);
assert_eq!(e, 3);
assert_eq!(f, 4.0);
});
let module = Module::new(
store.engine(),
r#"
(module
(import "" "" (func $debug (param i32 i32 f32 i64 i64 f64)))
(func (export "foo") (param i32 i32 f32 i64 i64 f64)
(if (i32.ne (local.get 0) (i32.const -1))
(then unreachable)
)
(if (i32.ne (local.get 1) (i32.const 1))
(then unreachable)
)
(if (f32.ne (local.get 2) (f32.const 2))
(then unreachable)
)
(if (i64.ne (local.get 3) (i64.const -3))
(then unreachable)
)
(if (i64.ne (local.get 4) (i64.const 3))
(then unreachable)
)
(if (f64.ne (local.get 5) (f64.const 4))
(then unreachable)
)
local.get 0
local.get 1
local.get 2
local.get 3
local.get 4
local.get 5
call $debug
)
)
"#,
)?;
let instance = Instance::new(&store, &module, &[debug.into()])?;
let foo = instance
.get_func("foo")
.unwrap()
.get6::<i32, u32, f32, i64, u64, f64, ()>()?;
foo(-1, 1, 2.0, -3, 3, 4.0)?;
Ok(())
}