diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 956b95d7b..f1e6a0613 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -9,7 +9,7 @@ use crate::{ module::{ExportIndex, Module, ModuleInner}, sig_registry::SigRegistry, table::Table, - typed_func::{Func, Safe, WasmTypeList}, + typed_func::{Func, Wasm, WasmTypeList}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, vm, }; @@ -107,7 +107,7 @@ impl Instance { /// # Ok(()) /// # } /// ``` - pub fn func(&self, name: &str) -> ResolveResult> + pub fn func(&self, name: &str) -> ResolveResult> where Args: WasmTypeList, Rets: WasmTypeList, @@ -157,8 +157,8 @@ impl Instance { } }; - let typed_func: Func = - unsafe { Func::new_from_ptr(func_ptr as _, ctx) }; + let typed_func: Func = + unsafe { Func::from_raw_parts(trampoline, invoke, f as _, ctx, invoke_env) }; Ok(typed_func) } else { diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4191f347e..48e689d1d 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -6,24 +6,79 @@ use crate::{ types::{FuncSig, Type, WasmExternType}, vm::Ctx, }; -use std::{any::Any, cell::UnsafeCell, marker::PhantomData, mem, panic, ptr, sync::Arc}; +use std::{ + any::Any, cell::UnsafeCell, ffi::c_void, fmt, marker::PhantomData, mem, panic, ptr, sync::Arc, +}; thread_local! { pub static EARLY_TRAPPER: UnsafeCell>> = UnsafeCell::new(None); } -pub trait Safeness {} -pub struct Safe; -pub struct Unsafe; -impl Safeness for Safe {} -impl Safeness for Unsafe {} +#[repr(C)] +pub enum WasmTrapInfo { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, +} + +impl fmt::Display for WasmTrapInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + WasmTrapInfo::Unreachable => "unreachable", + WasmTrapInfo::IncorrectCallIndirectSignature => { + "incorrect `call_indirect` signature" + } + WasmTrapInfo::MemoryOutOfBounds => "memory out-of-bounds access", + WasmTrapInfo::CallIndirectOOB => "`call_indirect` out-of-bounds", + WasmTrapInfo::IllegalArithmetic => "illegal arithmetic operation", + WasmTrapInfo::Unknown => "unknown", + } + ) + } +} + +pub trait Kind {} + +type Trampoline = extern "C" fn(*mut Ctx, *const c_void, *const u64, *mut u64); +type Invoke = extern "C" fn( + Trampoline, + *mut Ctx, + *const c_void, + *const u64, + *mut u64, + &mut WasmTrapInfo, +) -> bool; + +pub struct Wasm { + trampoline: Trampoline, + invoke: Invoke, + invoke_env: *mut c_void, +} + +pub struct Host(()); +impl Kind for Wasm {} +impl Kind for Host {} pub trait WasmTypeList { type CStruct; + type RetArray: AsMut<[u64]>; + fn from_ret_array(array: Self::RetArray) -> Self; + fn empty_ret_array() -> Self::RetArray; fn from_c_struct(c_struct: Self::CStruct) -> Self; fn into_c_struct(self) -> Self::CStruct; fn types() -> &'static [Type]; - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets + unsafe fn call( + self, + f: *const c_void, + wasm: Wasm, + ctx: *mut Ctx, + ) -> Result where Rets: WasmTypeList; } @@ -33,7 +88,7 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - fn to_raw(&self) -> *const (); + fn to_raw(&self) -> *const c_void; } pub trait TrapEarly @@ -71,19 +126,31 @@ where // Func::new(f) // } -pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> { - f: *const (), +pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { + inner: Inner, + f: *const c_void, ctx: *mut Ctx, - _phantom: PhantomData<(&'a (), Safety, Args, Rets)>, + _phantom: PhantomData<(&'a (), Args, Rets)>, } -impl<'a, Args, Rets> Func<'a, Args, Rets, Safe> +impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm> where Args: WasmTypeList, Rets: WasmTypeList, { - pub(crate) unsafe fn new_from_ptr(f: *const (), ctx: *mut Ctx) -> Func<'a, Args, Rets, Safe> { + pub(crate) unsafe fn from_raw_parts( + trampoline: Trampoline, + invoke: Invoke, + f: *const c_void, + ctx: *mut Ctx, + invoke_env: *mut c_void, + ) -> Func<'a, Args, Rets, Wasm> { Func { + inner: Wasm { + trampoline, + invoke, + invoke_env, + }, f, ctx, _phantom: PhantomData, @@ -91,16 +158,17 @@ where } } -impl<'a, Args, Rets> Func<'a, Args, Rets, Unsafe> +impl<'a, Args, Rets> Func<'a, Args, Rets, Host> where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(f: F) -> Func<'a, Args, Rets, Unsafe> + pub fn new(f: F) -> Func<'a, Args, Rets, Host> where F: ExternalFunction, { Func { + inner: Host(()), f: f.to_raw(), ctx: ptr::null_mut(), _phantom: PhantomData, @@ -108,11 +176,11 @@ where } } -impl<'a, Args, Rets, Safety> Func<'a, Args, Rets, Safety> +impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner> where Args: WasmTypeList, Rets: WasmTypeList, - Safety: Safeness, + Inner: Kind, { pub fn params(&self) -> &'static [Type] { Args::types() @@ -124,6 +192,13 @@ where impl WasmTypeList for (A,) { type CStruct = S1; + type RetArray = [u64; 1]; + fn from_ret_array(array: Self::RetArray) -> Self { + (WasmExternType::from_bits(array[0]),) + } + fn empty_ret_array() -> Self::RetArray { + [0u64] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { let S1(a) = c_struct; (a,) @@ -137,19 +212,45 @@ impl WasmTypeList for (A,) { &[A::TYPE] } #[allow(non_snake_case)] - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern "C" fn(*mut Ctx, A) -> Rets = mem::transmute(f); + unsafe fn call( + self, + f: *const c_void, + wasm: Wasm, + ctx: *mut Ctx, + ) -> Result { + // type Trampoline = extern "C" fn(*mut Ctx, *const c_void, *const u64, *mut u64); + // type Invoke = extern "C" fn(Trampoline, *mut Ctx, *const c_void, *const u64, *mut u64, &mut WasmTrapInfo) -> bool; + let (a,) = self; - f(ctx, a) + let args = [a.to_bits()]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + + if (wasm.invoke)( + wasm.trampoline, + ctx, + f, + args.as_ptr(), + rets.as_mut().as_mut_ptr(), + &mut trap, + ) { + Ok(Rets::from_ret_array(rets)) + } else { + Err(trap) + } } } -impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Safe> +impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm> where Rets: WasmTypeList, { pub fn call(&self, a: A) -> Result { - Ok(unsafe { ::call(a, self.f, self.ctx) }) + unsafe { ::call(a, self.f, self.inner, self.ctx) }.map_err(|e| { + RuntimeError::Trap { + msg: e.to_string().into(), + } + }) } } @@ -160,6 +261,14 @@ macro_rules! impl_traits { impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) { type CStruct = $struct_name<$( $x ),*>; + type RetArray = [u64; count_idents!( $( $x ),* )]; + fn from_ret_array(array: Self::RetArray) -> Self { + let [ $( $x ),* ] = array; + ( $( WasmExternType::from_bits($x) ),* ) + } + fn empty_ret_array() -> Self::RetArray { + [0; count_idents!( $( $x ),* )] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { #[allow(non_snake_case)] let $struct_name ( $( $x ),* ) = c_struct; @@ -174,18 +283,33 @@ macro_rules! impl_traits { &[$( $x::TYPE, )*] } #[allow(non_snake_case)] - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f); + unsafe fn call(self, f: *const c_void, wasm: Wasm, ctx: *mut Ctx) -> Result { + // type Trampoline = extern "C" fn(*mut Ctx, *const c_void, *const u64, *mut u64); + // type Invoke = extern "C" fn(Trampoline, *mut Ctx, *const c_void, *const u64, *mut u64, &mut WasmTrapInfo) -> bool; + #[allow(unused_parens)] let ( $( $x ),* ) = self; - let c_struct = f(ctx $( ,$x )*); - Rets::from_c_struct(c_struct) + let args = [ $( $x.to_bits() ),* ]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + + if (wasm.invoke)(wasm.trampoline, ctx, f, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap) { + Ok(Rets::from_ret_array(rets)) + } else { + Err(trap) + } + + // let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f); + // #[allow(unused_parens)] + // let ( $( $x ),* ) = self; + // let c_struct = f(ctx $( ,$x )*); + // Rets::from_c_struct(c_struct) } } impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN { #[allow(non_snake_case)] - fn to_raw(&self) -> *const () { + fn to_raw(&self) -> *const c_void { assert_eq!(mem::size_of::(), 0, "you cannot use a closure that captures state for `Func`."); extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: $x )* ) -> Rets::CStruct { @@ -209,23 +333,36 @@ macro_rules! impl_traits { } } - wrap::<$( $x, )* Rets, Trap, Self> as *const () + wrap::<$( $x, )* Rets, Trap, Self> as *const c_void } } - impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Safe> + impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> where Rets: WasmTypeList, { #[allow(non_snake_case)] pub fn call(&self, $( $x: $x, )* ) -> Result { #[allow(unused_parens)] - Ok(unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.ctx) }) + unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.inner, self.ctx) }.map_err(|e| { + RuntimeError::Trap { + msg: e.to_string().into(), + } + }) } } }; } +macro_rules! count_idents { + ( $($idents:ident),* ) => {{ + #[allow(dead_code, non_camel_case_types)] + enum Idents { $($idents,)* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + }}; +} + impl_traits!([C] S0,); impl_traits!([transparent] S1, A); impl_traits!([C] S2, A, B); @@ -240,11 +377,11 @@ impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J); impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K); impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L); -impl<'a, Args, Rets, Safety> IsExport for Func<'a, Args, Rets, Safety> +impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner> where Args: WasmTypeList, Rets: WasmTypeList, - Safety: Safeness, + Inner: Kind, { fn to_export(&self) -> Export { let func = unsafe { FuncPointer::new(self.f as _) }; diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 966bd4e39..79ef5da7f 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -76,37 +76,99 @@ where Self: Sized, { const TYPE: Type; + fn to_bits(self) -> u64; + fn from_bits(n: u64) -> Self; } unsafe impl WasmExternType for i8 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for u8 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for i16 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for u16 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for i32 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for u32 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for i64 { const TYPE: Type = Type::I64; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for u64 { const TYPE: Type = Type::I64; + fn to_bits(self) -> u64 { + self + } + fn from_bits(n: u64) -> Self { + n + } } unsafe impl WasmExternType for f32 { const TYPE: Type = Type::F32; + fn to_bits(self) -> u64 { + self.to_bits() as u64 + } + fn from_bits(n: u64) -> Self { + f32::from_bits(n as u32) + } } unsafe impl WasmExternType for f64 { const TYPE: Type = Type::F64; + fn to_bits(self) -> u64 { + self.to_bits() + } + fn from_bits(n: u64) -> Self { + f64::from_bits(n) + } } // pub trait IntegerAtomic