#[macro_use]
mod macros;
use core::future::Future;
use core::mem::replace;
use crate::alloc;
use crate::compile::meta;
use crate::hash::Hash;
use crate::runtime::{
self, AnyTypeInfo, FromValue, InstAddress, MaybeTypeOf, Memory, Output, RuntimeError, ToReturn,
TypeHash, TypeOf, UnsafeToMut, UnsafeToRef, Value, VmErrorKind, VmResult,
};
macro_rules! access_memory {
($count:expr, $add:expr, $memory:ident, $addr:ident, $args:ident, $($from_fn:path, $var:ident, $num:expr),* $(,)?) => {
if $args != $count + $add {
return VmResult::err(VmErrorKind::BadArgumentCount {
actual: $args,
expected: $count + $add,
});
}
let [$($var,)*] = vm_try!($memory.slice_at_mut($addr, $args)) else {
unreachable!();
};
$(let $var = replace($var, Value::empty());)*
$(
let $var = match $from_fn($var) {
Ok($var) => $var,
Err(error) => {
return VmResult::err(error).with_error(|| VmErrorKind::BadArgument {
arg: $num,
});
}
};
)*
};
}
pub trait FunctionKind {
const IS_ASYNC: bool;
}
#[non_exhaustive]
pub struct Plain;
impl FunctionKind for Plain {
const IS_ASYNC: bool = false;
}
#[non_exhaustive]
pub struct Async;
impl FunctionKind for Async {
const IS_ASYNC: bool = true;
}
#[diagnostic::on_unimplemented(
label = "#[derive(Any)] could be missing on the arguments or return value of `{Self}`"
)]
pub trait Function<A, K>: 'static + Send + Sync {
#[doc(hidden)]
type Return;
#[doc(hidden)]
const ARGS: usize;
#[doc(hidden)]
fn fn_call(
&self,
memory: &mut dyn Memory,
addr: InstAddress,
args: usize,
out: Output,
) -> VmResult<()>;
}
#[diagnostic::on_unimplemented(
label = "#[derive(Any)] could be missing on the arguments or return value of `{Self}`"
)]
pub trait InstanceFunction<A, K>: 'static + Send + Sync {
#[doc(hidden)]
type Instance: TypeOf;
#[doc(hidden)]
type Return;
#[doc(hidden)]
const ARGS: usize;
#[doc(hidden)]
fn fn_call(
&self,
memory: &mut dyn Memory,
addr: InstAddress,
args: usize,
out: Output,
) -> VmResult<()>;
}
macro_rules! impl_instance_function_traits {
($count:expr $(, $ty:ident $var:ident $num:expr)*) => {
impl<T, Instance, Kind, $($ty,)*> InstanceFunction<(Instance, $($ty,)*), Kind> for T
where
Instance: TypeOf,
T: Function<(Instance, $($ty,)*), Kind>,
{
type Instance = Instance;
type Return = T::Return;
const ARGS: usize = <T as Function<(Instance, $($ty,)*), Kind>>::ARGS;
#[inline]
fn fn_call(&self, memory: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> {
Function::fn_call(self, memory, addr, args, out)
}
}
};
}
use core::marker::PhantomData;
pub struct Ref<T: ?Sized>(PhantomData<T>);
impl<T> MaybeTypeOf for Ref<T>
where
T: ?Sized + MaybeTypeOf,
{
#[inline]
fn maybe_type_of() -> alloc::Result<meta::DocType> {
T::maybe_type_of()
}
}
impl<T> TypeHash for Ref<T>
where
T: ?Sized + TypeHash,
{
const HASH: Hash = T::HASH;
}
impl<T> TypeOf for Ref<T>
where
T: ?Sized + TypeOf,
{
const PARAMETERS: Hash = T::PARAMETERS;
const STATIC_TYPE_INFO: AnyTypeInfo = T::STATIC_TYPE_INFO;
}
pub struct Mut<T: ?Sized>(PhantomData<T>);
impl<T> MaybeTypeOf for Mut<T>
where
T: ?Sized + MaybeTypeOf,
{
#[inline]
fn maybe_type_of() -> alloc::Result<meta::DocType> {
T::maybe_type_of()
}
}
impl<T> TypeHash for Mut<T>
where
T: ?Sized + TypeHash,
{
const HASH: Hash = T::HASH;
}
impl<T> TypeOf for Mut<T>
where
T: ?Sized + TypeOf,
{
const PARAMETERS: Hash = T::PARAMETERS;
const STATIC_TYPE_INFO: AnyTypeInfo = T::STATIC_TYPE_INFO;
}
struct Guard;
#[inline(always)]
fn from_value<T>(value: Value) -> Result<(T, Guard), RuntimeError>
where
T: FromValue,
{
Ok((T::from_value(value)?, Guard))
}
#[inline(always)]
fn unsafe_to_ref<'a, T>(value: Value) -> Result<(&'a T, T::Guard), RuntimeError>
where
T: ?Sized + UnsafeToRef,
{
unsafe { T::unsafe_to_ref(value) }
}
#[inline(always)]
fn unsafe_to_mut<'a, T>(value: Value) -> Result<(&'a mut T, T::Guard), RuntimeError>
where
T: ?Sized + UnsafeToMut,
{
unsafe { T::unsafe_to_mut(value) }
}
macro_rules! impl_function_traits {
($count:expr $(, {$ty:ident, $var:ident, $place:ty, $num:expr, {$($mut:tt)*}, {$($trait:tt)*}, $from_fn:path})*) => {
impl<T, U, $($ty,)*> Function<($($place,)*), Plain> for T
where
T: 'static + Send + Sync + Fn($($($mut)* $ty),*) -> U,
U: ToReturn,
$($ty: $($trait)*,)*
{
type Return = U;
const ARGS: usize = $count;
#[allow(clippy::drop_non_drop)]
fn fn_call(&self, memory: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> {
access_memory!($count, 0, memory, addr, args, $($from_fn, $var, $num,)*);
let ret = self($($var.0),*);
$(drop($var.1);)*
let value = vm_try!(ToReturn::to_return(ret));
vm_try!(out.store(memory, value));
VmResult::Ok(())
}
}
impl<T, U, $($ty,)*> Function<($($place,)*), Async> for T
where
T: 'static + Send + Sync + Fn($($($mut)* $ty),*) -> U,
U: 'static + Future,
U::Output: ToReturn,
$($ty: $($trait)*,)*
{
type Return = U::Output;
const ARGS: usize = $count;
#[allow(clippy::drop_non_drop)]
fn fn_call(&self, memory: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> {
access_memory!($count, 0, memory, addr, args, $($from_fn, $var, $num,)*);
let fut = self($($var.0),*);
$(drop($var.1);)*
let ret = vm_try!(runtime::Future::new(async move {
let output = fut.await;
VmResult::Ok(vm_try!(ToReturn::to_return(output)))
}));
let value = vm_try!(Value::try_from(ret));
vm_try!(out.store(memory, value));
VmResult::Ok(())
}
}
};
}
permute!(impl_function_traits);
repeat_macro!(impl_instance_function_traits);