pub(crate) mod env;
pub(crate) mod typed;
use rusty_jsc::{
JSContext, JSObject, JSObjectCallAsFunctionCallback, JSValue, callback, callback_closure,
};
use std::marker::PhantomData;
use std::panic::{self, AssertUnwindSafe};
use wasmer_types::{FunctionType, RawValue};
use crate::{
AsStoreMut, AsStoreRef, BackendFunction, BackendFunctionEnvMut, FromToNativeWasmType,
FunctionEnv, FunctionEnvMut, HostFunction, HostFunctionKind, IntoResult, NativeWasmType,
NativeWasmTypeInto, RuntimeError, StoreMut, Value, WasmTypeList, WithEnv, WithoutEnv,
jsc::{
store::{InternalStoreHandle, StoreHandle},
utils::convert::{AsJsc, jsc_value_to_wasmer},
vm::{VMFuncRef, VMFunction, VMFunctionEnvironment},
},
vm::VMExtern,
};
use super::engine::IntoJSC;
pub(crate) use env::*;
pub(crate) use typed::*;
#[derive(Clone, PartialEq, Eq)]
pub struct Function {
pub(crate) handle: VMFunction,
}
impl From<VMFunction> for Function {
fn from(handle: VMFunction) -> Self {
Self { handle }
}
}
impl Function {
pub fn to_vm_extern(&self) -> VMExtern {
VMExtern::Jsc(crate::backend::jsc::vm::VMExtern::Function(
self.handle.clone(),
))
}
#[allow(clippy::cast_ptr_alignment)]
pub fn new_with_env<FT, F, T: Send + 'static>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
ty: FT,
func: F,
) -> Self
where
FT: Into<FunctionType>,
F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result<Vec<Value>, RuntimeError>
+ 'static
+ Send
+ Sync,
{
let store = store.as_store_mut();
let context = store.jsc().context();
let function_type = ty.into();
let new_function_type = function_type.clone();
let raw_env = env.clone();
let callback = callback_closure!(&context, move |ctx: JSContext,
function: JSObject,
this: JSObject,
args: &[JSValue]|
-> Result<JSValue, JSValue> {
let global = ctx.get_global_object();
let store_ptr = global
.get_property(&ctx, "__store_ptr".to_string())
.to_number(&ctx)
.unwrap();
let mut store = unsafe { StoreMut::from_raw(store_ptr as usize as *mut _) };
let env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let wasm_arguments = new_function_type
.params()
.iter()
.enumerate()
.map(|(i, param)| jsc_value_to_wasmer(&ctx, param, &args[i]))
.collect::<Vec<_>>();
let results = func(env, &wasm_arguments).map_err(|e| {
let value = format!("{e}");
JSValue::string(&ctx, value)
})?;
match new_function_type.results().len() {
0 => Ok(JSValue::undefined(&ctx)),
1 => Ok(results[0].as_jsc_value(&store)),
_ => Ok(JSObject::new_array(
&ctx,
&results
.into_iter()
.map(|result| result.as_jsc_value(&store))
.collect::<Vec<_>>(),
)?
.to_jsvalue()),
}
});
let vm_function = VMFunction::new(callback, function_type);
Self {
handle: vm_function,
}
}
pub fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
where
F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
let store = store.as_store_mut();
let function = WasmFunction::<Args, Rets>::new(func);
let callback = function.callback(store.jsc().context());
let ty = function.ty();
let vm_function = VMFunction::new(callback, ty);
Self {
handle: vm_function,
}
}
pub fn new_typed_with_env<T, F, Args, Rets>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
func: F,
) -> Self
where
F: HostFunction<T, Args, Rets, WithEnv>,
Args: WasmTypeList,
Rets: WasmTypeList,
{
let store = store.as_store_mut();
let context = store.jsc().context();
let function = WasmFunction::<Args, Rets>::new(func);
let callback = function.callback(store.jsc().context());
let bind = callback
.get_property(context, "bind".to_string())
.to_object(context)
.unwrap();
let callback_with_env = bind
.call(
context,
Some(&callback),
&[
JSValue::undefined(context),
JSValue::number(
context,
env.as_jsc().handle.internal_handle().index() as f64,
),
],
)
.unwrap()
.to_object(context)
.unwrap();
let ty = function.ty();
let vm_function = VMFunction::new(callback_with_env, ty);
Self {
handle: vm_function,
}
}
pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType {
self.handle.ty.clone()
}
pub fn call_raw(
&self,
_store: &mut impl AsStoreMut,
_params: Vec<RawValue>,
) -> Result<Box<[Value]>, RuntimeError> {
unimplemented!();
}
pub fn call(
&self,
store: &mut impl AsStoreMut,
params: &[Value],
) -> Result<Box<[Value]>, RuntimeError> {
let store_mut = store.as_store_mut();
let engine = store_mut.engine();
let context = engine.as_jsc().context();
let mut global = context.get_global_object();
let store_ptr = store_mut.as_raw() as usize;
global.set_property(
context,
"__store_ptr".to_string(),
JSValue::number(context, store_ptr as _),
);
let params_list = params
.iter()
.map(|v| v.as_jsc_value(&store_mut))
.collect::<Vec<_>>();
let result = {
let mut r;
loop {
let store_mut = store.as_store_mut();
let engine = store_mut.engine();
let context = engine.as_jsc().context();
r = self.handle.function.call(context, None, ¶ms_list);
if let Some(callback) = store_mut.inner.on_called.take() {
match callback(store_mut) {
Ok(wasmer_types::OnCalledAction::InvokeAgain) => {
continue;
}
Ok(wasmer_types::OnCalledAction::Finish) => {
break;
}
Ok(wasmer_types::OnCalledAction::Trap(trap)) => {
return Err(RuntimeError::user(trap));
}
Err(trap) => return Err(RuntimeError::user(trap)),
}
}
break;
}
r?
};
let result_types = self.handle.ty.results();
let store_mut = store.as_store_mut();
let engine = store_mut.engine();
let context = engine.as_jsc().context();
match result_types.len() {
0 => Ok(Box::new([])),
1 => {
let value = jsc_value_to_wasmer(context, &result_types[0], &result);
Ok(vec![value].into_boxed_slice())
}
n => {
let result = result.to_object(context).unwrap();
Ok((0..n)
.map(|i| {
let js_val = result.get_property_at_index(context, i as _).unwrap();
jsc_value_to_wasmer(context, &result_types[i], &js_val)
})
.collect::<Vec<_>>()
.into_boxed_slice())
}
}
}
pub(crate) fn from_vm_extern(
_store: &mut impl AsStoreMut,
internal: crate::vm::VMExternFunction,
) -> Self {
Self {
handle: internal.into_jsc(),
}
}
pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef {
unimplemented!();
}
pub(crate) unsafe fn from_vm_funcref(
_store: &mut impl AsStoreMut,
_funcref: VMFuncRef,
) -> Self {
unimplemented!();
}
pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true
}
}
impl std::fmt::Debug for Function {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.debug_struct("Function").finish()
}
}
#[derive(Clone, Debug, Hash)]
pub struct WasmFunction<Args = (), Rets = ()> {
callback: JSObjectCallAsFunctionCallback,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for WasmFunction<Args, Rets> {}
impl<Args, Rets> WasmFunction<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
#[allow(dead_code)]
pub fn new<F, T, Kind: HostFunctionKind>(function: F) -> Self
where
F: HostFunction<T, Args, Rets, Kind>,
T: Sized,
{
Self {
callback: function
.function_callback(crate::BackendKind::Jsc)
.into_jsc(),
_phantom: PhantomData,
}
}
#[allow(dead_code)]
pub fn ty(&self) -> FunctionType {
FunctionType::new(Args::wasm_types(), Rets::wasm_types())
}
#[allow(dead_code)]
pub fn callback(&self, context: &JSContext) -> JSObject {
JSObject::new_function_with_callback(context, "FunctionCallback".to_string(), self.callback)
}
}
impl crate::Function {
pub fn into_jsc(self) -> crate::backend::jsc::function::Function {
match self.0 {
BackendFunction::Jsc(s) => s,
_ => panic!("Not a `jsc` function!"),
}
}
pub fn as_jsc(&self) -> &crate::backend::jsc::function::Function {
match self.0 {
BackendFunction::Jsc(ref s) => s,
_ => panic!("Not a `jsc` function!"),
}
}
pub fn as_jsc_mut(&mut self) -> &mut crate::backend::jsc::function::Function {
match self.0 {
BackendFunction::Jsc(ref mut s) => s,
_ => panic!("Not a `jsc` function!"),
}
}
}
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
}
};
}
macro_rules! impl_host_function {
([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => {
paste::paste! {
#[allow(non_snake_case)]
pub(crate) fn [<gen_fn_callback_ $c_struct_name:lower _no_env>]
<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult<Rets>, Func: Fn($( $x , )*) -> RetsAsResult + 'static>
(this: &Func) -> crate::backend::jsc::vm::VMFunctionCallback {
#[callback]
fn fn_callback<$( $x, )* Rets, RetsAsResult, Func>(
ctx: JSContext,
function: JSObject,
this_object: JSObject,
arguments: &[JSValue],
) -> Result<JSValue, JSValue>
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn($( $x , )*) -> RetsAsResult + 'static,
{
use std::convert::TryInto;
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let global = ctx.get_global_object();
let store_ptr = global.get_property(&ctx, "__store_ptr".to_string()).to_number(&ctx).unwrap();
if store_ptr.is_nan() {
panic!("Store pointer is invalid. Received {}", store_ptr as usize)
}
let mut store = unsafe { StoreMut::from_raw(store_ptr as usize as *mut _) };
let result = panic::catch_unwind(AssertUnwindSafe(|| {
type JSArray<'a> = &'a [JSValue; count_idents!( $( $x ),* )];
let args_without_store: JSArray = arguments.try_into().unwrap();
let [ $( $x ),* ] = args_without_store;
func($( FromToNativeWasmType::from_native( unsafe {
$x::Native::from_raw(&mut store, RawValue {
u128: {
if $x.is_number(&ctx) {
$x.to_number(&ctx).unwrap() as _
} else {
$x.to_string(&ctx).unwrap().to_string().parse::<u128>().unwrap()
}
}
})
}) ),* ).into_result()
}));
match result {
Ok(Ok(result)) => {
match Rets::size() {
0 => {Ok(JSValue::undefined(&ctx))},
1 => {
let ty = Rets::wasm_types()[0];
let mut arr = unsafe { result.into_array(&mut store) };
let val = unsafe { Value::from_raw(&mut store, ty, arr.as_mut()[0]) };
let value: JSValue = val.as_jsc_value(&store);
Ok(value)
}
_n => {
let mut arr = unsafe { result.into_array(&mut store) };
let result_values = Rets::wasm_types().iter().enumerate().map(|(i, ret_type)| {
let raw = arr.as_mut()[i];
let value = unsafe { Value::from_raw(&mut store, *ret_type, raw) };
value.as_jsc_value(&mut store)
}).collect::<Vec<_>>();
Ok(JSObject::new_array(&ctx, &result_values).unwrap().to_jsvalue())
}
}
},
Ok(Err(err)) => {
let trap = crate::backend::jsc::error::Trap::user(Box::new(err));
Err(trap.into_jsc_value(&ctx))
},
Err(panic) => {
Err(JSValue::string(&ctx, format!("panic: {:?}", panic)))
},
}
}
Some(fn_callback::< $( $x, )* Rets, RetsAsResult, Func> as _)
}
#[allow(non_snake_case)]
pub(crate) fn [<gen_fn_callback_ $c_struct_name:lower>]
<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult<Rets>, T: Send + 'static, Func: Fn(FunctionEnvMut<T>, $( $x , )*) -> RetsAsResult + 'static>
(this: &Func) -> crate::backend::jsc::vm::VMFunctionCallback {
#[callback]
fn fn_callback<T, $( $x, )* Rets, RetsAsResult, Func>(
ctx: JSContext,
function: JSObject,
this_object: JSObject,
arguments: &[JSValue],
) -> Result<JSValue, JSValue>
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static,
T: Send + 'static,
{
use std::convert::TryInto;
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let global = ctx.get_global_object();
let store_ptr = global.get_property(&ctx, "__store_ptr".to_string()).to_number(&ctx).unwrap();
if store_ptr.is_nan() {
panic!("Store pointer is invalid. Received {}", store_ptr as usize)
}
let mut store = unsafe { StoreMut::from_raw(store_ptr as usize as *mut _) };
let handle_index = arguments[0].to_number(&ctx).unwrap() as usize;
let handle: StoreHandle<VMFunctionEnvironment> = unsafe {
StoreHandle::from_internal(
store.objects_mut().id(),
InternalStoreHandle::from_index(handle_index).unwrap(),
)
};
let env = crate::backend::jsc::function::env::FunctionEnv::from_handle(handle).into_mut(&mut store);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
type JSArray<'a> = &'a [JSValue; count_idents!( $( $x ),* )];
let args_without_store: JSArray = arguments[1..].try_into().unwrap();
let [ $( $x ),* ] = args_without_store;
let mut store = unsafe { StoreMut::from_raw(store_ptr as usize as *mut _) };
func(
BackendFunctionEnvMut::Jsc(env).into(),
$( FromToNativeWasmType::from_native( unsafe {
$x::Native::from_raw(&mut store, RawValue {
u128: {
if $x.is_number(&ctx) {
$x.to_number(&ctx).unwrap() as _
} else {
$x.to_string(&ctx).unwrap().to_string().parse::<u128>().unwrap()
}
}
})
}) ),*
).into_result()
}));
match result {
Ok(Ok(result)) => {
match Rets::size() {
0 => {Ok(JSValue::undefined(&ctx))},
1 => {
let ty = Rets::wasm_types()[0];
let mut arr = unsafe { result.into_array(&mut store) };
let val = unsafe { Value::from_raw(&mut store, ty, arr.as_mut()[0]) };
let value: JSValue = val.as_jsc_value(&store);
Ok(value)
}
_n => {
let mut arr = unsafe { result.into_array(&mut store) };
let result_values = Rets::wasm_types().iter().enumerate().map(|(i, ret_type)| {
let raw = arr.as_mut()[i];
let value = unsafe { Value::from_raw(&mut store, *ret_type, raw) };
value.as_jsc_value(&mut store)
}).collect::<Vec<_>>();
Ok(JSObject::new_array(&ctx, &result_values).unwrap().to_jsvalue())
}
}
},
Ok(Err(err)) => {
let trap = crate::backend::jsc::error::Trap::user(Box::new(err));
Err(trap.into_jsc_value(&ctx))
},
Err(panic) => {
Err(JSValue::string(&ctx, format!("panic: {:?}", panic)))
},
}
}
Some(fn_callback::<T, $( $x, )* Rets, RetsAsResult, Func> as _)
}
}
};
}
impl_host_function!([C] S0,);
impl_host_function!([transparent] S1, A1);
impl_host_function!([C] S2, A1, A2);
impl_host_function!([C] S3, A1, A2, A3);
impl_host_function!([C] S4, A1, A2, A3, A4);
impl_host_function!([C] S5, A1, A2, A3, A4, A5);
impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6);
impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7);
impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);