use crate::collections::HashMap;
use crate::hash::Hash;
use crate::reflection::{ReflectValueType, ToValue, UnsafeFromValue};
use crate::tls;
use crate::value::{ValueType, ValueTypeInfo};
use crate::vm::{Vm, VmError};
use std::any::type_name;
use std::future::Future;
use crate::context::item::Item;
use crate::context::{BoxFuture, ContextError, Handler, IntoInstFnHash};
#[derive(Default)]
pub struct Module {
pub(super) path: Item,
pub(super) functions: HashMap<Item, (Handler, Option<usize>)>,
pub(super) instance_functions:
HashMap<(ValueType, Hash), (Handler, Option<usize>, ValueTypeInfo, String)>,
pub(super) types: HashMap<ValueType, (ValueTypeInfo, Item)>,
}
impl Module {
pub fn new<I>(path: I) -> Self
where
I: IntoIterator,
I::Item: AsRef<str>,
{
Self {
path: Item::of(path),
functions: Default::default(),
instance_functions: Default::default(),
types: Default::default(),
}
}
pub fn ty<N>(&mut self, name: N) -> TypeBuilder<'_, N>
where
N: IntoIterator,
N::Item: AsRef<str>,
{
TypeBuilder {
name,
types: &mut self.types,
}
}
pub fn function<Func, Args, N>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
Func: Function<Args>,
N: IntoIterator,
N::Item: AsRef<str>,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
let handler = Handler::Regular(Box::new(move |vm, args| f.vm_call(vm, args)));
self.functions.insert(name, (handler, Some(Func::args())));
Ok(())
}
pub fn async_function<Func, Args, N>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
Func: AsyncFunction<Args>,
N: IntoIterator,
N::Item: AsRef<str>,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
let handler = Handler::Async(Box::new(move |vm, args| f.vm_call(vm, args)));
self.functions.insert(name, (handler, Some(Func::args())));
Ok(())
}
pub fn raw_fn<F, N>(&mut self, name: N, f: F) -> Result<(), ContextError>
where
for<'vm> F: 'static + Copy + Fn(&'vm mut Vm, usize) -> Result<(), VmError> + Send + Sync,
N: IntoIterator,
N::Item: AsRef<str>,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
let handler = Handler::Regular(Box::new(move |vm, args| f(vm, args)));
self.functions.insert(name, (handler, None));
Ok(())
}
pub fn async_raw_fn<F, O, N>(&mut self, name: N, f: F) -> Result<(), ContextError>
where
for<'vm> F: 'static + Copy + Fn(&'vm mut Vm, usize) -> O + Send + Sync,
O: Future<Output = Result<(), VmError>>,
N: IntoIterator,
N::Item: AsRef<str>,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
let handler = Handler::Async(Box::new(move |vm, args| {
Box::pin(async move { f(vm, args).await })
}));
self.functions.insert(name, (handler, None));
Ok(())
}
pub fn inst_fn<N, Func, Args>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
N: IntoInstFnHash,
Func: InstFn<Args>,
{
let ty = Func::instance_value_type();
let type_info = Func::instance_value_type_info();
let key = (ty, name.to_hash());
let name = name.to_name();
if self.instance_functions.contains_key(&key) {
return Err(ContextError::ConflictingInstanceFunction { type_info, name });
}
let handler = Handler::Regular(Box::new(move |vm, args| f.vm_call(vm, args)));
self.instance_functions
.insert(key.clone(), (handler, Some(Func::args()), type_info, name));
Ok(())
}
pub fn async_inst_fn<N, Func, Args>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
N: IntoInstFnHash,
Func: AsyncInstFn<Args>,
{
let ty = Func::instance_value_type();
let type_info = Func::instance_value_type_info();
let key = (ty, name.to_hash());
let name = name.to_name();
if self.instance_functions.contains_key(&key) {
return Err(ContextError::ConflictingInstanceFunction { type_info, name });
}
let handler = Handler::Async(Box::new(move |vm, args| f.vm_call(vm, args)));
self.instance_functions
.insert(key.clone(), (handler, Some(Func::args()), type_info, name));
Ok(())
}
}
#[must_use = "must be consumed with build::<T>() to construct a type"]
pub struct TypeBuilder<'a, N> {
name: N,
types: &'a mut HashMap<ValueType, (ValueTypeInfo, Item)>,
}
impl<N> TypeBuilder<'_, N>
where
N: IntoIterator,
N::Item: AsRef<str>,
{
pub fn build<T>(self) -> Result<(), ContextError>
where
T: ReflectValueType,
{
let name = Item::of(self.name);
let value_type = T::value_type();
let type_info = T::value_type_info();
if let Some((existing, _)) = self.types.insert(value_type, (type_info, name.clone())) {
return Err(ContextError::ConflictingType { name, existing });
}
Ok(())
}
}
pub trait IntoVmResult {
type Output: ToValue;
fn into_vm_result(self) -> Result<Self::Output, VmError>;
}
impl<T> IntoVmResult for T
where
T: ToValue,
{
type Output = T;
fn into_vm_result(self) -> Result<Self::Output, VmError> {
Ok(self)
}
}
impl<T, E> IntoVmResult for Result<T, E>
where
crate::Error: From<E>,
T: ToValue,
{
type Output = T;
fn into_vm_result(self) -> Result<Self::Output, VmError> {
use crate::error::Error;
self.map_err(|e| VmError::from(Error::from(e)))
}
}
pub trait Function<Args>: 'static + Copy + Send + Sync {
fn args() -> usize;
fn vm_call(self, vm: &mut Vm, args: usize) -> Result<(), VmError>;
}
pub trait AsyncFunction<Args>: 'static + Copy + Send + Sync {
fn args() -> usize;
fn vm_call<'vm>(self, vm: &'vm mut Vm, args: usize) -> BoxFuture<'vm, Result<(), VmError>>;
}
pub trait InstFn<Args>: 'static + Copy + Send + Sync {
fn args() -> usize;
fn instance_value_type() -> ValueType;
fn instance_value_type_info() -> ValueTypeInfo;
fn vm_call(self, vm: &mut Vm, args: usize) -> Result<(), VmError>;
}
pub trait AsyncInstFn<Args>: 'static + Copy + Send + Sync {
fn args() -> usize;
fn instance_value_type() -> ValueType;
fn instance_value_type_info() -> ValueTypeInfo;
fn vm_call<'vm>(self, vm: &'vm mut Vm, args: usize) -> BoxFuture<'vm, Result<(), VmError>>;
}
macro_rules! impl_register {
() => {
impl_register!{@impl 0,}
};
({$ty:ident, $var:ident, $num:expr}, $({$l_ty:ident, $l_var:ident, $l_num:expr},)*) => {
impl_register!{@impl $num, {$ty, $var, $num}, $({$l_ty, $l_var, $l_num},)*}
impl_register!{$({$l_ty, $l_var, $l_num},)*}
};
(@impl $count:expr, $({$ty:ident, $var:ident, $num:expr},)*) => {
impl<Func, Ret, $($ty,)*> Function<($($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn($($ty,)*) -> Ret,
Ret: IntoVmResult,
Ret::Output: ToValue,
$($ty: UnsafeFromValue,)*
{
fn args() -> usize {
$count
}
fn vm_call(
self,
vm: &mut Vm,
args: usize
) -> Result<(), VmError> {
impl_register!{@args $count, args}
$(let $var = vm.pop()?;)*
#[allow(unused_unsafe)]
let ret = unsafe {
impl_register!{@vars vm, $count, $($ty, $var, $num,)*}
tls::inject_vm(vm, || self($($var.0,)*)).into_vm_result()?
};
impl_register!{@return vm, ret, Ret}
Ok(())
}
}
impl<Func, Ret, $($ty,)*> AsyncFunction<($($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn($($ty,)*) -> Ret,
Ret: Future,
Ret::Output: IntoVmResult,
$($ty: UnsafeFromValue,)*
{
fn args() -> usize {
$count
}
fn vm_call<'vm>(
self,
vm: &'vm mut Vm,
args: usize
) -> BoxFuture<'vm, Result<(), VmError>> {
Box::pin(async move {
impl_register!{@args $count, args}
$(let $var = vm.pop()?;)*
#[allow(unused_unsafe)]
let ret = unsafe {
impl_register!{@vars vm, $count, $($ty, $var, $num,)*}
tls::InjectVm::new(vm, self($($var.0,)*)).await.into_vm_result()?
};
impl_register!{@return vm, ret, Ret}
Ok(())
})
}
}
impl<Func, Ret, Inst, $($ty,)*> InstFn<(Inst, $($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn(Inst $(, $ty)*) -> Ret,
Ret: IntoVmResult,
Inst: UnsafeFromValue + ReflectValueType,
$($ty: UnsafeFromValue,)*
{
fn args() -> usize {
$count
}
fn instance_value_type() -> ValueType {
Inst::value_type()
}
fn instance_value_type_info() -> ValueTypeInfo {
Inst::value_type_info()
}
fn vm_call(self, vm: &mut Vm, args: usize) -> Result<(), VmError> {
impl_register!{@args $count, args}
let inst = vm.pop()?;
$(let $var = vm.pop()?;)*
#[allow(unused_unsafe)]
let ret = unsafe {
impl_register!{@unsafeinstancevars inst, vm, $count, $($ty, $var, $num,)*}
tls::inject_vm(vm, || self(inst.0, $($var.0,)*)).into_vm_result()?
};
impl_register!{@return vm, ret, Ret}
Ok(())
}
}
impl<Func, Ret, Inst, $($ty,)*> AsyncInstFn<(Inst, $($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn(Inst $(, $ty)*) -> Ret,
Ret: Future,
Ret::Output: IntoVmResult,
Inst: UnsafeFromValue + ReflectValueType,
$($ty: UnsafeFromValue,)*
{
fn args() -> usize {
$count
}
fn instance_value_type() -> ValueType {
Inst::value_type()
}
fn instance_value_type_info() -> ValueTypeInfo {
Inst::value_type_info()
}
fn vm_call<'vm>(self, vm: &'vm mut Vm, args: usize) -> BoxFuture<'vm, Result<(), VmError>> {
Box::pin(async move {
impl_register!{@args $count, args}
let inst = vm.pop()?;
$(let $var = vm.pop()?;)*
#[allow(unused_unsafe)]
let ret = unsafe {
impl_register!{@unsafeinstancevars inst, vm, $count, $($ty, $var, $num,)*}
tls::InjectVm::new(vm, self(inst.0, $($var.0,)*)).await.into_vm_result()?
};
impl_register!{@return vm, ret, Ret}
Ok(())
})
}
}
};
(@return $vm:ident, $ret:ident, $ty:ty) => {
let $ret = match $ret.to_value($vm) {
Ok($ret) => $ret,
Err(error) => {
return Err(VmError::ReturnConversionError {
error,
ret: type_name::<$ty>()
});
}
};
$vm.push($ret);
};
(@vars $vm:expr, $count:expr, $($ty:ty, $var:ident, $num:expr,)*) => {
$(
let $var = match <$ty>::unsafe_from_value($var, $vm) {
Ok(v) => v,
Err(error) => {
let ty = $var.type_info($vm)?;
return Err(VmError::ArgumentConversionError {
error,
arg: $count - $num,
from: ty,
to: type_name::<$ty>(),
});
}
};
)*
};
(@unsafeinstancevars $inst:ident, $vm:expr, $count:expr, $($ty:ty, $var:ident, $num:expr,)*) => {
let $inst = match Inst::unsafe_from_value($inst, $vm) {
Ok(v) => v,
Err(error) => {
let ty = $inst.type_info($vm)?;
return Err(VmError::ArgumentConversionError {
error,
arg: 0,
from: ty,
to: type_name::<Inst>()
});
}
};
$(
let $var = match <$ty>::unsafe_from_value($var, $vm) {
Ok(v) => v,
Err(error) => {
let ty = $var.type_info($vm)?;
return Err(VmError::ArgumentConversionError {
error,
arg: 1 + $count - $num,
from: ty,
to: type_name::<$ty>()
});
}
};
)*
};
(@args $expected:expr, $actual:expr) => {
if $actual != $expected {
return Err(VmError::ArgumentCountMismatch {
actual: $actual,
expected: $expected,
});
}
};
}
impl_register!(
{H, h, 8},
{G, g, 7},
{F, f, 6},
{E, e, 5},
{D, d, 4},
{C, c, 3},
{B, b, 2},
{A, a, 1},
);