use std::{any::Any, fmt, marker::PhantomData, rc::Rc};
use crate::{
builtin_traits::{BuiltinTrait, BuiltinTraitFunction},
ffvariants,
hl::userdata::Type,
ll::{
bytecode::{
BuiltinTraits, DispatchTable, Environment, Function, FunctionKind, MethodSignature,
},
gc::{Gc, Memory},
value::{self, Closure},
},
Error, ForeignFunction, FunctionParameterCount, MethodParameterCount, Value,
};
struct UnresolvedMethodSignature {
name: Rc<str>,
parameter_count: MethodParameterCount,
builtin_trait: BuiltinTrait,
}
impl UnresolvedMethodSignature {
fn resolve(self, builtin_traits: &BuiltinTraits) -> MethodSignature {
MethodSignature {
name: self.name,
parameter_count: self.parameter_count,
trait_id: match self.builtin_trait {
BuiltinTrait::None => None,
BuiltinTrait::Iterator => Some(builtin_traits.iterator),
},
}
}
}
#[derive(Default)]
pub(crate) struct DispatchTableDescriptor {
methods: Vec<(UnresolvedMethodSignature, FunctionKind)>,
}
impl DispatchTableDescriptor {
fn add_function_to_dtable(
env: &mut Environment,
gc: &mut Memory,
dtable: &mut DispatchTable,
builtin_traits: &BuiltinTraits,
signature: UnresolvedMethodSignature,
f: FunctionKind,
) -> Result<(), Error> {
let name = Rc::from(format!("{}.{}", &dtable.pretty_name, signature.name));
let function_id = env
.create_function(Function {
name: Rc::clone(&name),
parameter_count: FunctionParameterCount::Fixed(u16::from(
signature.parameter_count.to_count_without_self(),
)),
kind: f,
hidden_in_stack_traces: false,
})
.map_err(|_| Error::TooManyFunctions)?;
let signature = signature.resolve(builtin_traits);
let index =
env.get_or_create_method_index(&signature).map_err(|_| Error::TooManyMethods)?;
dtable.set_method(index, gc.allocate(Closure { name, function_id, captures: Vec::new() }));
Ok(())
}
pub(crate) fn build_dtable(
self,
mut dtable: DispatchTable,
env: &mut Environment,
gc: &mut Memory,
builtin_traits: &BuiltinTraits,
) -> Result<DispatchTable, Error> {
for (signature, f) in self.methods {
Self::add_function_to_dtable(env, gc, &mut dtable, builtin_traits, signature, f)?;
}
Ok(dtable)
}
}
pub struct TypeBuilder<T>
where
T: ?Sized,
{
type_name: Rc<str>,
type_dtable: DispatchTableDescriptor,
instance_dtable: DispatchTableDescriptor,
_data: PhantomData<T>,
}
impl<T> TypeBuilder<T>
where
T: ?Sized,
{
pub fn new(type_name: impl Into<Rc<str>>) -> Self
where
T: Any,
{
let type_name = type_name.into();
Self {
type_dtable: Default::default(),
instance_dtable: Default::default(),
type_name,
_data: PhantomData,
}
}
fn function_to_method_parameter_count(count: FunctionParameterCount) -> MethodParameterCount {
MethodParameterCount::from_count_without_self(
count.to_fixed().expect("BareExactArgs functions are never varargs"),
)
.expect("generated ForeignFunction variants only support up to 8 arguments") }
pub fn add_static<F, V>(self, name: &str, f: F) -> Self
where
V: ffvariants::BareExactArgs,
F: ForeignFunction<V, ParameterCount = FunctionParameterCount>,
{
self.add_raw_static(
name,
Self::function_to_method_parameter_count(F::PARAMETER_COUNT),
FunctionKind::Foreign(f.into_raw_foreign_function()),
)
}
pub fn add_function<F, V>(self, name: &str, f: F) -> Self
where
V: ffvariants::Method<T>,
F: ForeignFunction<V, ParameterCount = MethodParameterCount>,
{
self.add_raw_function(
name,
F::PARAMETER_COUNT,
FunctionKind::Foreign(f.into_raw_foreign_function()),
)
}
pub fn add_builtin_trait_function<S, B, F>(mut self, which: B, f: F) -> Self
where
B: BuiltinTraitFunction<S>,
F: ForeignFunction<S, ParameterCount = MethodParameterCount>,
{
self.instance_dtable.methods.push((
UnresolvedMethodSignature {
name: Rc::from(B::NAME),
parameter_count: F::PARAMETER_COUNT,
builtin_trait: which.owning_trait(),
},
FunctionKind::Foreign(f.into_raw_foreign_function()),
));
self
}
pub fn add_raw_function(
mut self,
name: &str,
parameter_count: MethodParameterCount,
f: FunctionKind,
) -> Self {
self.instance_dtable.methods.push((
UnresolvedMethodSignature {
name: Rc::from(name),
parameter_count,
builtin_trait: BuiltinTrait::None,
},
f,
));
self
}
pub fn add_raw_static(
mut self,
name: &str,
parameter_count: MethodParameterCount,
f: FunctionKind,
) -> Self {
self.type_dtable.methods.push((
UnresolvedMethodSignature {
name: Rc::from(name),
parameter_count,
builtin_trait: BuiltinTrait::None,
},
f,
));
self
}
pub(crate) fn build(
self,
env: &mut Environment,
gc: &mut Memory,
builtin_traits: &BuiltinTraits,
) -> Result<BuiltType<T>, Error>
where
T: Any + Sized,
{
let mut type_dtable = self.type_dtable.build_dtable(
DispatchTable::new_for_type(Rc::clone(&self.type_name)),
env,
gc,
builtin_traits,
)?;
let instance_dtable = self.instance_dtable.build_dtable(
DispatchTable::new_for_instance(Rc::clone(&self.type_name)),
env,
gc,
builtin_traits,
)?;
let instance_dtable = Gc::new(instance_dtable);
env.add_user_dtable::<T>(Gc::clone(&instance_dtable));
type_dtable.instance = Some(Gc::as_raw(&instance_dtable));
let type_dtable = Gc::new(type_dtable);
Ok(BuiltType {
type_dtable,
instance_dtable,
type_name: self.type_name,
_data: PhantomData,
})
}
}
impl<T> fmt::Debug for TypeBuilder<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypeBuilder").finish_non_exhaustive()
}
}
pub(crate) struct BuiltType<T>
where
T: ?Sized,
{
pub(crate) type_name: Rc<str>,
pub(crate) type_dtable: Gc<DispatchTable>,
pub(crate) instance_dtable: Gc<DispatchTable>,
_data: PhantomData<T>,
}
impl<T> BuiltType<T> {
pub(crate) fn make_type(&self, gc: &mut Memory) -> Value
where
T: Any,
{
gc.manage(&self.type_dtable);
gc.manage(&self.instance_dtable);
let user_data: Box<dyn value::UserData> =
Box::new(Type::<T>::new(Gc::clone(&self.type_dtable)));
Value::UserData(Gc::new(user_data))
}
}