use crate::collections::{HashMap, HashSet};
use crate::compile::{ContextError, IntoComponent, Item, Named};
use crate::macros::{MacroContext, TokenStream};
use crate::runtime::{
ConstValue, FromValue, FunctionHandler, Future, GeneratorState, MacroHandler, Protocol, Stack,
StaticType, ToValue, TypeCheck, TypeInfo, TypeOf, UnsafeFromValue, Value, VmError, VmErrorKind,
};
use crate::{Hash, InstFnInfo, InstFnKind, InstFnName};
use std::fmt;
use std::future;
use std::sync::Arc;
pub trait InstallWith {
fn install_with(_: &mut Module) -> Result<(), ContextError> {
Ok(())
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AssocType {
pub hash: Hash,
pub type_info: TypeInfo,
}
pub(crate) struct UnitType {
pub(crate) name: Box<str>,
}
pub(crate) struct InternalEnum {
pub(crate) name: &'static str,
pub(crate) base_type: Item,
pub(crate) static_type: &'static StaticType,
pub(crate) variants: Vec<InternalVariant>,
}
impl InternalEnum {
fn new<N>(name: &'static str, base_type: N, static_type: &'static StaticType) -> Self
where
N: IntoIterator,
N::Item: IntoComponent,
{
InternalEnum {
name,
base_type: Item::with_item(base_type),
static_type,
variants: Vec::new(),
}
}
fn variant<C, Args>(&mut self, name: &'static str, type_check: TypeCheck, constructor: C)
where
C: Function<Args>,
{
let constructor: Arc<FunctionHandler> =
Arc::new(move |stack, args| constructor.fn_call(stack, args));
self.variants.push(InternalVariant {
name,
type_check,
args: C::args(),
constructor,
});
}
}
pub(crate) struct InternalVariant {
pub(crate) name: &'static str,
pub(crate) type_check: TypeCheck,
pub(crate) args: usize,
pub(crate) constructor: Arc<FunctionHandler>,
}
pub(crate) struct Type {
pub(crate) name: Box<str>,
pub(crate) type_info: TypeInfo,
pub(crate) spec: Option<TypeSpecification>,
}
pub struct Variant {
pub(crate) kind: VariantKind,
pub(crate) constructor: Option<Arc<FunctionHandler>>,
}
impl fmt::Debug for Variant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Variant")
.field("kind", &self.kind)
.field("constructor", &self.constructor.is_some())
.finish()
}
}
#[derive(Debug)]
pub(crate) enum VariantKind {
Tuple(Tuple),
Struct(Struct),
Unit,
}
impl Variant {
#[inline]
pub fn tuple(args: usize) -> Self {
Self {
kind: VariantKind::Tuple(Tuple { args }),
constructor: None,
}
}
#[inline]
pub fn st<const N: usize>(fields: [&'static str; N]) -> Self {
Self {
kind: VariantKind::Struct(Struct {
fields: fields.into_iter().map(Box::<str>::from).collect(),
}),
constructor: None,
}
}
#[inline]
pub fn unit() -> Self {
Self {
kind: VariantKind::Unit,
constructor: None,
}
}
}
#[derive(Debug)]
pub struct Tuple {
pub(crate) args: usize,
}
#[derive(Debug)]
pub(crate) struct Struct {
pub(crate) fields: HashSet<Box<str>>,
}
pub(crate) struct Enum {
pub(crate) variants: Vec<(Box<str>, Variant)>,
}
pub(crate) enum TypeSpecification {
Struct(Struct),
Enum(Enum),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum AssocKind {
IndexFn(Protocol),
FieldFn(Protocol),
Instance,
}
impl AssocKind {
pub(crate) fn hash(self, instance_type: Hash, key: Hash) -> Hash {
match self {
Self::IndexFn(protocol) => Hash::index_fn(protocol, instance_type, key),
Self::FieldFn(protocol) => Hash::field_fn(protocol, instance_type, key),
Self::Instance => Hash::instance_function(instance_type, key),
}
}
}
pub(crate) struct AssocFn {
pub(crate) handler: Arc<FunctionHandler>,
pub(crate) args: Option<usize>,
pub(crate) type_info: TypeInfo,
pub(crate) name: InstFnKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct AssocKey {
pub(crate) type_hash: Hash,
pub(crate) hash: Hash,
pub(crate) parameters: Hash,
pub(crate) kind: AssocKind,
}
pub(crate) struct ModuleFn {
pub(crate) handler: Arc<FunctionHandler>,
pub(crate) args: Option<usize>,
}
pub(crate) struct Macro {
pub(crate) handler: Arc<MacroHandler>,
}
#[derive(Default)]
pub struct Module {
pub(crate) item: Item,
pub(crate) functions: HashMap<Item, ModuleFn>,
pub(crate) macros: HashMap<Item, Macro>,
pub(crate) constants: HashMap<Item, ConstValue>,
pub(crate) associated_functions: HashMap<AssocKey, AssocFn>,
pub(crate) types: HashMap<Hash, Type>,
pub(crate) unit_type: Option<UnitType>,
pub(crate) internal_enums: Vec<InternalEnum>,
}
impl Module {
pub fn new() -> Self {
Self::default()
}
pub fn with_item<I>(iter: I) -> Self
where
I: IntoIterator,
I::Item: IntoComponent,
{
Self::inner_new(Item::with_item(iter))
}
pub fn with_crate(name: &str) -> Self {
Self::inner_new(Item::with_crate(name))
}
pub fn with_crate_item<I>(name: &str, iter: I) -> Self
where
I: IntoIterator,
I::Item: IntoComponent,
{
Self::inner_new(Item::with_crate_item(name, iter))
}
fn inner_new(item: Item) -> Self {
Self {
item,
functions: Default::default(),
macros: Default::default(),
associated_functions: Default::default(),
types: Default::default(),
unit_type: None,
internal_enums: Vec::new(),
constants: Default::default(),
}
}
pub fn ty<T>(&mut self) -> Result<(), ContextError>
where
T: Named + TypeOf + InstallWith,
{
let type_hash = T::type_hash();
let type_info = T::type_info();
let ty = Type {
name: T::full_name(),
type_info,
spec: None,
};
if let Some(old) = self.types.insert(type_hash, ty) {
return Err(ContextError::ConflictingType {
item: Item::with_item(&[T::full_name()]),
type_info: old.type_info,
});
}
T::install_with(self)?;
Ok(())
}
pub fn struct_meta<T, const N: usize>(
&mut self,
fields: [&'static str; N],
) -> Result<(), ContextError>
where
T: Named + TypeOf,
{
let type_hash = T::type_hash();
let ty = match self.types.get_mut(&type_hash) {
Some(ty) => ty,
None => {
return Err(ContextError::MissingType {
item: Item::with_item(&[T::full_name()]),
type_info: T::type_info(),
});
}
};
let old = ty.spec.replace(TypeSpecification::Struct(Struct {
fields: fields.into_iter().map(Box::<str>::from).collect(),
}));
if old.is_some() {
return Err(ContextError::ConflictingTypeMeta {
item: Item::with_item(&[T::full_name()]),
type_info: ty.type_info.clone(),
});
}
Ok(())
}
pub fn enum_meta<T, const N: usize>(
&mut self,
variants: [(&'static str, Variant); N],
) -> Result<(), ContextError>
where
T: Named + TypeOf,
{
let type_hash = T::type_hash();
let ty = match self.types.get_mut(&type_hash) {
Some(ty) => ty,
None => {
return Err(ContextError::MissingType {
item: Item::with_item(&[T::full_name()]),
type_info: T::type_info(),
});
}
};
let old = ty.spec.replace(TypeSpecification::Enum(Enum {
variants: variants
.into_iter()
.map(|(name, variant)| (Box::from(name), variant))
.collect(),
}));
if old.is_some() {
return Err(ContextError::ConflictingTypeMeta {
item: Item::with_item(&[T::full_name()]),
type_info: ty.type_info.clone(),
});
}
Ok(())
}
pub fn variant_constructor<Func, Args, T>(
&mut self,
index: usize,
constructor: Func,
) -> Result<(), ContextError>
where
T: Named + TypeOf,
Func: Function<Args, Return = T>,
{
let type_hash = T::type_hash();
let ty = match self.types.get_mut(&type_hash) {
Some(ty) => ty,
None => {
return Err(ContextError::MissingType {
item: Item::with_item(&[T::full_name()]),
type_info: T::type_info(),
});
}
};
let en = match &mut ty.spec {
Some(TypeSpecification::Enum(en)) => en,
_ => {
return Err(ContextError::MissingEnum {
item: Item::with_item(&[T::full_name()]),
type_info: T::type_info(),
});
}
};
let variant = match en.variants.get_mut(index) {
Some((_, variant)) => variant,
_ => {
return Err(ContextError::MissingVariant {
type_info: T::type_info(),
index,
});
}
};
if variant.constructor.is_some() {
return Err(ContextError::VariantConstructorConflict {
type_info: T::type_info(),
index,
});
}
variant.constructor = Some(Arc::new(move |stack, args| {
constructor.fn_call(stack, args)
}));
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(UnitType {
name: <Box<str>>::from(name.as_ref()),
});
Ok(())
}
pub fn generator_state<N>(&mut self, name: N) -> Result<(), ContextError>
where
N: IntoIterator,
N::Item: IntoComponent,
{
let mut enum_ =
InternalEnum::new("GeneratorState", name, crate::runtime::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 option<N>(&mut self, name: N) -> Result<(), ContextError>
where
N: IntoIterator,
N::Item: IntoComponent,
{
let mut enum_ = InternalEnum::new("Option", name, crate::runtime::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_ = InternalEnum::new("Result", name, crate::runtime::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 function<Func, Args, N>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
Func: Function<Args>,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::with_item(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 constant<N, V>(&mut self, name: N, value: V) -> Result<(), ContextError>
where
N: IntoIterator,
N::Item: IntoComponent,
V: ToValue,
{
let name = Item::with_item(name);
if self.constants.contains_key(&name) {
return Err(ContextError::ConflictingConstantName { name });
}
let value = match value.to_value() {
Ok(v) => v,
Err(e) => return Err(ContextError::ValueError { error: e }),
};
let constant_value = match <ConstValue as FromValue>::from_value(value) {
Ok(v) => v,
Err(e) => return Err(ContextError::ValueError { error: e }),
};
self.constants.insert(name, constant_value);
Ok(())
}
pub fn macro_<N, M>(&mut self, name: N, f: M) -> Result<(), ContextError>
where
M: 'static
+ Send
+ Sync
+ Fn(&mut MacroContext<'_>, &TokenStream) -> crate::Result<TokenStream>,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::with_item(name);
if self.macros.contains_key(&name) {
return Err(ContextError::ConflictingFunctionName { name });
}
let handler: Arc<MacroHandler> = Arc::new(f);
self.macros.insert(name, Macro { 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::with_item(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 + Fn(&mut Stack, usize) -> Result<(), VmError> + Send + Sync,
N: IntoIterator,
N::Item: IntoComponent,
{
let name = Item::with_item(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: InstFnName,
Func: InstFn<Args>,
{
let name = name.info();
let handler: Arc<FunctionHandler> = Arc::new(move |stack, args| f.fn_call(stack, args));
let ty = Func::ty();
let args = Some(Func::args());
self.assoc_fn(name, handler, ty, args, AssocKind::Instance)
}
pub fn field_fn<N, Func, Args>(
&mut self,
protocol: Protocol,
name: N,
f: Func,
) -> Result<(), ContextError>
where
N: InstFnName,
Func: InstFn<Args>,
{
let name = name.info();
let handler: Arc<FunctionHandler> = Arc::new(move |stack, args| f.fn_call(stack, args));
let ty = Func::ty();
let args = Some(Func::args());
self.assoc_fn(name, handler, ty, args, AssocKind::FieldFn(protocol))
}
pub fn index_fn<Func, Args>(
&mut self,
protocol: Protocol,
index: usize,
f: Func,
) -> Result<(), ContextError>
where
Func: InstFn<Args>,
{
let name = InstFnInfo::index(protocol, index);
let handler: Arc<FunctionHandler> = Arc::new(move |stack, args| f.fn_call(stack, args));
let ty = Func::ty();
let args = Some(Func::args());
self.assoc_fn(name, handler, ty, args, AssocKind::IndexFn(protocol))
}
pub fn async_inst_fn<N, Func, Args>(&mut self, name: N, f: Func) -> Result<(), ContextError>
where
N: InstFnName,
Func: AsyncInstFn<Args>,
{
let name = name.info();
let handler: Arc<FunctionHandler> = Arc::new(move |stack, args| f.fn_call(stack, args));
let ty = Func::ty();
let args = Some(Func::args());
self.assoc_fn(name, handler, ty, args, AssocKind::Instance)
}
fn assoc_fn(
&mut self,
name: InstFnInfo,
handler: Arc<FunctionHandler>,
ty: AssocType,
args: Option<usize>,
kind: AssocKind,
) -> Result<(), ContextError> {
let key = AssocKey {
type_hash: ty.hash,
hash: name.hash,
kind,
parameters: name.parameters,
};
if self.associated_functions.contains_key(&key) {
return Err(match name.kind {
InstFnKind::Protocol(protocol) => ContextError::ConflictingProtocolFunction {
type_info: ty.type_info,
name: protocol.name.into(),
},
InstFnKind::Instance(name) => ContextError::ConflictingInstanceFunction {
type_info: ty.type_info,
name,
},
InstFnKind::Hash(hash) => ContextError::ConflictingInstanceFunctionHash {
type_info: ty.type_info,
hash,
},
});
}
let assoc_fn = AssocFn {
handler,
args,
type_info: ty.type_info,
name: name.kind,
};
self.associated_functions.insert(key, assoc_fn);
Ok(())
}
}
pub trait Function<Args>: 'static + Send + Sync {
type Return;
fn args() -> usize;
fn fn_call(&self, stack: &mut Stack, args: usize) -> Result<(), VmError>;
}
pub trait AsyncFunction<Args>: 'static + Send + Sync {
type Return;
fn args() -> usize;
fn fn_call(&self, stack: &mut Stack, args: usize) -> Result<(), VmError>;
}
pub trait InstFn<Args>: 'static + Send + Sync {
type Instance;
type Return;
fn args() -> usize;
fn ty() -> AssocType;
fn fn_call(&self, stack: &mut Stack, args: usize) -> Result<(), VmError>;
}
pub trait AsyncInstFn<Args>: 'static + Send + Sync {
type Instance;
type Return;
fn args() -> usize;
fn ty() -> AssocType;
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 + 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($count)?;
$(let $var = it.next().unwrap();)*
drop(it);
#[allow(unused)]
let ret = unsafe {
impl_register!{@unsafe-vars $count, $($ty, $var, $num,)*}
let ret = self($(<$ty>::unsafe_coerce($var.0),)*);
impl_register!{@drop-stack-guards $($var),*}
ret
};
impl_register!{@return stack, ret, Return}
Ok(())
}
}
impl<Func, Return, $($ty,)*> AsyncFunction<($($ty,)*)> for Func
where
Func: 'static + Send + Sync + Fn($($ty,)*) -> Return,
Return: 'static + 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($count)?;
$(let $var = it.next().unwrap();)*
drop(it);
#[allow(unused_unsafe)]
let ret = unsafe {
impl_register!{@unsafe-vars $count, $($ty, $var, $num,)*}
let fut = self($(<$ty>::unsafe_coerce($var.0),)*);
Future::new(async move {
let output = fut.await;
impl_register!{@drop-stack-guards $($var),*}
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 + 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 ty() -> AssocType {
AssocType {
hash: Instance::type_hash(),
type_info: 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($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,)*}
let ret = self(Instance::unsafe_coerce(inst.0), $(<$ty>::unsafe_coerce($var.0),)*);
impl_register!{@drop-stack-guards inst, $($var),*}
ret
};
impl_register!{@return stack, ret, Return}
Ok(())
}
}
impl<Func, Return, Instance, $($ty,)*> AsyncInstFn<(Instance, $($ty,)*)> for Func
where
Func: 'static + Send + Sync + Fn(Instance $(, $ty)*) -> Return,
Return: 'static + future::Future,
Return::Output: ToValue,
Instance: UnsafeFromValue + TypeOf,
$($ty: UnsafeFromValue,)*
{
type Instance = Instance;
type Return = Return;
fn args() -> usize {
$count + 1
}
fn ty() -> AssocType {
AssocType {
hash: Instance::type_hash(),
type_info: 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($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,)*}
let fut = self(Instance::unsafe_coerce(inst.0), $(<$ty>::unsafe_coerce($var.0),)*);
Future::new(async move {
let output = fut.await;
impl_register!{@drop-stack-guards inst, $($var),*}
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(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,
})),
};
)*
};
(@drop-stack-guards $($var:ident),* $(,)?) => {{
$(drop(($var.1));)*
}};
(@check-args $expected:expr, $actual:expr) => {
if $actual != $expected {
return Err(VmError::from(VmErrorKind::BadArgumentCount {
actual: $actual,
expected: $expected,
}));
}
};
}
repeat_macro!(impl_register);