use crate::collections::HashMap;
use crate::context::{ContextError, Handler, Macro};
use crate::{
Future, GeneratorState, Hash, IntoComponent, Item, Named, Stack, StaticType, ToValue, Type,
TypeCheck, TypeInfo, TypeOf, UnsafeFromValue, Value, VmError, VmErrorKind,
};
use std::any;
use std::future;
use std::sync::Arc;
pub(crate) struct ModuleUnitType {
pub(crate) name: Box<str>,
}
pub(crate) struct ModuleInternalEnum {
pub(crate) name: &'static str,
pub(crate) base_type: Item,
pub(crate) static_type: &'static StaticType,
pub(crate) variants: Vec<ModuleInternalVariant>,
}
impl ModuleInternalEnum {
pub fn new<N>(name: &'static str, base_type: N, static_type: &'static StaticType) -> Self
where
N: IntoIterator,
N::Item: IntoComponent,
{
ModuleInternalEnum {
name,
base_type: Item::of(base_type),
static_type,
variants: Vec::new(),
}
}
fn variant<C, Args>(&mut self, name: &'static str, type_check: TypeCheck, constructor: C)
where
C: crate::module::Function<Args>,
C::Return: TypeOf,
{
let constructor: Arc<Handler> =
Arc::new(move |stack, args| constructor.fn_call(stack, args));
let type_of = C::Return::type_of();
self.variants.push(ModuleInternalVariant {
name,
type_check,
args: C::args(),
constructor,
type_of,
});
}
}
pub(crate) struct ModuleInternalVariant {
pub(crate) name: &'static str,
pub(crate) type_check: TypeCheck,
pub(crate) args: usize,
pub(crate) constructor: Arc<Handler>,
pub(crate) type_of: Type,
}
pub(crate) struct ModuleType {
pub(crate) name: Box<str>,
pub(crate) type_info: TypeInfo,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum ModuleAssociatedKind {
Getter,
Instance,
}
impl ModuleAssociatedKind {
pub fn into_hash_fn(self) -> fn(Type, Hash) -> Hash {
match self {
Self::Getter => Hash::getter,
Self::Instance => Hash::instance_function,
}
}
}
pub(crate) struct ModuleAssociatedFn {
pub(crate) handler: Arc<Handler>,
pub(crate) args: Option<usize>,
pub(crate) type_info: TypeInfo,
pub(crate) name: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ModuleAssocKey {
pub(crate) type_of: Type,
pub(crate) hash: Hash,
pub(crate) kind: ModuleAssociatedKind,
}
pub(crate) struct ModuleFn {
pub(crate) handler: Arc<Handler>,
pub(crate) args: Option<usize>,
}
pub(crate) struct ModuleMacro {
pub(crate) handler: Arc<Macro>,
}
#[derive(Default)]
pub struct Module {
pub(crate) path: Item,
pub(crate) functions: HashMap<Item, ModuleFn>,
pub(crate) macros: HashMap<Item, ModuleMacro>,
pub(crate) associated_functions: HashMap<ModuleAssocKey, ModuleAssociatedFn>,
pub(crate) types: HashMap<Type, ModuleType>,
pub(crate) unit_type: Option<ModuleUnitType>,
pub(crate) internal_enums: Vec<ModuleInternalEnum>,
}
impl Module {
pub fn new<I>(path: I) -> Self
where
I: IntoIterator,
I::Item: IntoComponent,
{
Self {
path: Item::of(path),
functions: Default::default(),
macros: Default::default(),
associated_functions: Default::default(),
types: Default::default(),
unit_type: None,
internal_enums: Vec::new(),
}
}
pub fn empty() -> Self {
Self::default()
}
pub fn ty<T>(&mut self) -> Result<(), ContextError>
where
T: Named + TypeOf,
{
let type_of = T::type_of();
let type_info = T::type_info();
let ty = ModuleType {
name: String::from(&*T::NAME).into_boxed_str(),
type_info,
};
if let Some(old) = self.types.insert(type_of, ty) {
return Err(ContextError::ConflictingType {
item: Item::of(&[T::NAME]),
existing: old.type_info,
});
}
Ok(())
}
pub fn unit<N>(&mut self, name: N) -> Result<(), ContextError>
where
N: AsRef<str>,
{
if self.unit_type.is_some() {
return Err(ContextError::UnitAlreadyPresent);
}
self.unit_type = Some(ModuleUnitType {
name: String::from(name.as_ref()).into_boxed_str(),
});
Ok(())
}
pub fn option<N>(&mut self, name: N) -> Result<(), ContextError>
where
N: IntoIterator,
N::Item: IntoComponent,
{
let mut enum_ = ModuleInternalEnum::new("Option", name, crate::OPTION_TYPE);
enum_.variant("Some", TypeCheck::Option(0), Option::<Value>::Some);
enum_.variant("None", TypeCheck::Option(1), || Option::<Value>::None);
self.internal_enums.push(enum_);
Ok(())
}
pub fn result<N>(&mut self, name: N) -> Result<(), ContextError>
where
N: IntoIterator,
N::Item: IntoComponent,
{
let mut enum_ = ModuleInternalEnum::new("Result", name, crate::RESULT_TYPE);
enum_.variant("Ok", TypeCheck::Result(0), Result::<Value, Value>::Ok);
enum_.variant("Err", TypeCheck::Result(1), Result::<Value, Value>::Err);
self.internal_enums.push(enum_);
Ok(())
}
pub fn generator_state<N>(&mut self, name: N) -> Result<(), ContextError>
where
N: IntoIterator,
N::Item: IntoComponent,
{
let mut enum_ =
ModuleInternalEnum::new("GeneratorState", name, crate::GENERATOR_STATE_TYPE);
enum_.variant(
"Complete",
TypeCheck::GeneratorState(0),
GeneratorState::Complete,
);
enum_.variant(
"Yielded",
TypeCheck::GeneratorState(1),
GeneratorState::Yielded,
);
self.internal_enums.push(enum_);
Ok(())
}
pub fn function<Func, Args, N>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
Func: Function<Args>,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
self.functions.insert(
name,
ModuleFn {
handler: Arc::new(move |stack, args| f.fn_call(stack, args)),
args: Some(Func::args()),
},
);
Ok(())
}
pub fn macro_<N, M, A, O>(&mut self, name: N, f: M) -> Result<(), ContextError>
where
M: 'static + Send + Sync + Copy + Fn(&A) -> Result<O, crate::Error>,
A: any::Any,
O: any::Any,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::of(name);
if self.macros.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
let handler: Arc<Macro> = Arc::new(move |a| {
let a = match a.downcast_ref::<A>() {
Some(a) => a,
None => {
return Err(crate::Error::msg(format!(
"expected argument #1 `{}`",
std::any::type_name::<A>()
)));
}
};
let output = f(a)?;
Ok(Box::new(output))
});
self.macros.insert(name, ModuleMacro { handler });
Ok(())
}
pub fn async_function<Func, Args, N>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
Func: AsyncFunction<Args>,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
self.functions.insert(
name,
ModuleFn {
handler: Arc::new(move |stack, args| f.fn_call(stack, args)),
args: Some(Func::args()),
},
);
Ok(())
}
pub fn raw_fn<F, N>(&mut self, name: N, f: F) -> Result<(), ContextError>
where
F: 'static + Copy + Fn(&mut Stack, usize) -> Result<(), VmError> + Send + Sync,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::of(name);
if self.functions.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
self.functions.insert(
name,
ModuleFn {
handler: Arc::new(move |stack, args| f(stack, args)),
args: None,
},
);
Ok(())
}
pub fn inst_fn<N, Func, Args>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
N: InstFnNameHash,
Func: InstFn<Args>,
{
self.assoc_fn(name, f, ModuleAssociatedKind::Instance)
}
pub fn getter<N, Func, Args>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
N: InstFnNameHash,
Func: InstFn<Args>,
{
self.assoc_fn(name, f, ModuleAssociatedKind::Getter)
}
fn assoc_fn<N, Func, Args>(
&mut self,
name: N,
f: Func,
kind: ModuleAssociatedKind,
) -> Result<(), ContextError>
where
N: InstFnNameHash,
Func: InstFn<Args>,
{
let type_of = Func::instance_type_of();
let type_info = Func::instance_type_of_info();
let key = ModuleAssocKey {
type_of,
hash: name.inst_fn_name_hash(),
kind,
};
let name = name.into_name();
if self.associated_functions.contains_key(&key) {
return Err(ContextError::ConflictingInstanceFunction { type_info, name });
}
let handler: Arc<Handler> = Arc::new(move |stack, args| f.fn_call(stack, args));
let instance_function = ModuleAssociatedFn {
handler,
args: Some(Func::args()),
type_info,
name,
};
self.associated_functions.insert(key, instance_function);
Ok(())
}
pub fn async_inst_fn<N, Func, Args>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
N: InstFnNameHash,
Func: AsyncInstFn<Args>,
{
let type_of = Func::instance_type_of();
let type_info = Func::instance_type_of_info();
let key = ModuleAssocKey {
type_of,
hash: name.inst_fn_name_hash(),
kind: ModuleAssociatedKind::Instance,
};
let name = name.into_name();
if self.associated_functions.contains_key(&key) {
return Err(ContextError::ConflictingInstanceFunction { type_info, name });
}
let handler: Arc<Handler> = Arc::new(move |stack, args| f.fn_call(stack, args));
let instance_function = ModuleAssociatedFn {
handler,
args: Some(Func::args()),
type_info,
name,
};
self.associated_functions.insert(key, instance_function);
Ok(())
}
}
pub trait InstFnNameHash: Copy {
fn inst_fn_name_hash(self) -> Hash;
fn into_name(self) -> String;
}
impl<'a> InstFnNameHash for &'a str {
fn inst_fn_name_hash(self) -> Hash {
Hash::of(self)
}
fn into_name(self) -> String {
self.to_owned()
}
}
impl<'a> InstFnNameHash for Hash {
fn inst_fn_name_hash(self) -> Hash {
self
}
fn into_name(self) -> String {
String::new()
}
}
pub trait Function<Args>: 'static + Copy + Send + Sync {
type Return;
fn args() -> usize;
fn fn_call(self, stack: &mut Stack, args: usize) -> Result<(), VmError>;
}
pub trait AsyncFunction<Args>: 'static + Copy + Send + Sync {
type Return;
fn args() -> usize;
fn fn_call(self, stack: &mut Stack, args: usize) -> Result<(), VmError>;
}
pub trait InstFn<Args>: 'static + Copy + Send + Sync {
type Instance;
type Return;
fn args() -> usize;
fn instance_type_of() -> Type;
fn instance_type_of_info() -> TypeInfo;
fn fn_call(self, stack: &mut Stack, args: usize) -> Result<(), VmError>;
}
pub trait AsyncInstFn<Args>: 'static + Copy + Send + Sync {
type Instance;
type Return;
fn args() -> usize;
fn instance_type_of() -> Type;
fn instance_type_of_info() -> TypeInfo;
fn fn_call(self, stack: &mut Stack, args: usize) -> 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, Return, $($ty,)*> Function<($($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn($($ty,)*) -> Return,
Return: ToValue,
$($ty: UnsafeFromValue,)*
{
type Return = Return;
fn args() -> usize {
$count
}
fn fn_call(
self,
stack: &mut Stack,
args: usize
) -> Result<(), VmError> {
impl_register!{@check-args $count, args}
#[allow(unused_mut)]
let mut it = stack.drain_stack_top($count)?;
$(let $var = it.next().unwrap();)*
drop(it);
#[allow(unused)]
let ret = unsafe {
impl_register!{@unsafe-vars $count, $($ty, $var, $num,)*}
self($(<$ty>::unsafe_coerce($var.0),)*)
};
impl_register!{@return stack, ret, Return}
Ok(())
}
}
impl<Func, Return, $($ty,)*> AsyncFunction<($($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn($($ty,)*) -> Return,
Return: future::Future,
Return::Output: ToValue,
$($ty: 'static + UnsafeFromValue,)*
{
type Return = Return;
fn args() -> usize {
$count
}
fn fn_call(
self,
stack: &mut Stack,
args: usize
) -> Result<(), VmError> {
impl_register!{@check-args $count, args}
#[allow(unused_mut)]
let mut it = stack.drain_stack_top($count)?;
$(let $var = it.next().unwrap();)*
drop(it);
#[allow(unused_unsafe)]
let ret = unsafe {
impl_register!{@unsafe-vars $count, $($ty, $var, $num,)*}
Future::new(async move {
let output = self($(<$ty>::unsafe_coerce($var.0),)*).await;
let value = output.to_value()?;
Ok(value)
})
};
impl_register!{@return stack, ret, Return}
Ok(())
}
}
impl<Func, Return, Instance, $($ty,)*> InstFn<(Instance, $($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn(Instance $(, $ty)*) -> Return,
Return: ToValue,
Instance: UnsafeFromValue + TypeOf,
$($ty: UnsafeFromValue,)*
{
type Instance = Instance;
type Return = Return;
fn args() -> usize {
$count + 1
}
fn instance_type_of() -> Type {
Instance::type_of()
}
fn instance_type_of_info() -> TypeInfo {
Instance::type_info()
}
fn fn_call(self, stack: &mut Stack, args: usize) -> Result<(), VmError> {
impl_register!{@check-args ($count + 1), args}
#[allow(unused_mut)]
let mut it = stack.drain_stack_top($count + 1)?;
let inst = it.next().unwrap();
$(let $var = it.next().unwrap();)*
drop(it);
#[allow(unused)]
let ret = unsafe {
impl_register!{@unsafe-inst-vars inst, $count, $($ty, $var, $num,)*}
self(Instance::unsafe_coerce(inst.0), $(<$ty>::unsafe_coerce($var.0),)*)
};
impl_register!{@return stack, ret, Return}
Ok(())
}
}
impl<Func, Return, Instance, $($ty,)*> AsyncInstFn<(Instance, $($ty,)*)> for Func
where
Func: 'static + Copy + Send + Sync + Fn(Instance $(, $ty)*) -> Return,
Return: future::Future,
Return::Output: ToValue,
Instance: UnsafeFromValue + TypeOf,
$($ty: UnsafeFromValue,)*
{
type Instance = Instance;
type Return = Return;
fn args() -> usize {
$count + 1
}
fn instance_type_of() -> Type {
Instance::type_of()
}
fn instance_type_of_info() -> TypeInfo {
Instance::type_info()
}
fn fn_call(self, stack: &mut Stack, args: usize) -> Result<(), VmError> {
impl_register!{@check-args ($count + 1), args}
#[allow(unused_mut)]
let mut it = stack.drain_stack_top($count + 1)?;
let inst = it.next().unwrap();
$(let $var = it.next().unwrap();)*
drop(it);
#[allow(unused)]
let ret = unsafe {
impl_register!{@unsafe-inst-vars inst, $count, $($ty, $var, $num,)*}
Future::new(async move {
let output = self(Instance::unsafe_coerce(inst.0), $(<$ty>::unsafe_coerce($var.0),)*).await;
let value = output.to_value()?;
Ok(value)
})
};
impl_register!{@return stack, ret, Return}
Ok(())
}
}
};
(@return $stack:ident, $ret:ident, $ty:ty) => {
let $ret = match $ret.to_value() {
Ok($ret) => $ret,
Err(e) => return Err(VmError::from(VmErrorKind::BadReturn {
error: e.unpack_critical()?,
})),
};
$stack.push($ret);
};
(@unsafe-vars $count:expr, $($ty:ty, $var:ident, $num:expr,)*) => {
$(
let $var = match <$ty>::from_value($var) {
Ok(v) => v,
Err(e) => return Err(VmError::from(VmErrorKind::BadArgument {
error: e.unpack_critical()?,
arg: $count - $num,
})),
};
)*
};
(@unsafe-inst-vars $inst:ident, $count:expr, $($ty:ty, $var:ident, $num:expr,)*) => {
let $inst = match Instance::from_value($inst) {
Ok(v) => v,
Err(e) => return Err(VmError::from(VmErrorKind::BadArgument {
error: e.unpack_critical()?,
arg: 0,
})),
};
$(
let $var = match <$ty>::from_value($var) {
Ok(v) => v,
Err(e) => return Err(VmError::from(VmErrorKind::BadArgument {
error: e.unpack_critical()?,
arg: 1 + $count - $num,
})),
};
)*
};
(@check-args $expected:expr, $actual:expr) => {
if $actual != $expected {
return Err(VmError::from(VmErrorKind::BadArgumentCount {
actual: $actual,
expected: $expected,
}));
}
};
}
repeat_macro!(impl_register);