use std::{
any::{Any, TypeId},
collections::{HashMap, HashSet},
rc::Rc,
};
use super::{BuiltinDispatchTables, DispatchTable, Function, Opr24, Prototype, TraitPrototype};
use crate::ll::{
bytecode::MethodParameterCount,
error::{LanguageErrorKind, RenderedSignature},
gc::Gc,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct FunctionIndex(Opr24);
impl FunctionIndex {
pub(crate) fn from_opr24(x: Opr24) -> Self {
Self(x)
}
pub(crate) fn to_opr24(self) -> Opr24 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct MethodIndex(u16);
impl MethodIndex {
pub(crate) fn from_u16(x: u16) -> MethodIndex {
Self(x)
}
pub(crate) fn to_u16(self) -> u16 {
self.0
}
pub(crate) fn to_usize(self) -> usize {
usize::from(self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MethodSignature {
pub name: Rc<str>,
pub parameter_count: MethodParameterCount,
pub trait_id: Option<TraitIndex>,
}
impl MethodSignature {
pub fn new(name: Rc<str>, parameter_count: MethodParameterCount) -> Self {
Self { name, parameter_count, trait_id: None }
}
pub fn render(&self, env: &Environment) -> RenderedSignature {
RenderedSignature {
name: Rc::clone(&self.name),
parameter_count: self.parameter_count.to_count_without_self(),
trait_name: self
.trait_id
.and_then(|index| env.get_trait(index))
.map(|prototype| Rc::clone(&prototype.name)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct GlobalIndex(Opr24);
impl GlobalIndex {
pub(crate) fn to_usize(self) -> usize {
usize::from(self.0)
}
pub(crate) fn from_opr24(x: Opr24) -> Self {
Self(x)
}
pub(crate) fn to_opr24(self) -> Opr24 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct PrototypeIndex(Opr24);
impl PrototypeIndex {
pub(crate) fn from_opr24(x: Opr24) -> Self {
Self(x)
}
pub(crate) fn to_opr24(self) -> Opr24 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct TraitIndex(Opr24);
impl TraitIndex {
pub(crate) fn from_opr24(x: Opr24) -> Self {
Self(x)
}
pub(crate) fn to_opr24(self) -> Opr24 {
self.0
}
}
#[derive(Debug)]
pub struct Environment {
globals: HashMap<String, GlobalIndex>,
functions: Vec<Function>,
method_indices: HashMap<MethodSignature, MethodIndex>,
method_signatures: Vec<MethodSignature>,
pub builtin_dtables: BuiltinDispatchTables,
user_dtables: HashMap<TypeId, Gc<DispatchTable>>,
prototypes: Vec<Option<Prototype>>,
traits: Vec<TraitPrototype>,
}
impl Environment {
pub fn new(builtin_dtables: BuiltinDispatchTables) -> Self {
Self {
globals: HashMap::new(),
functions: Vec::new(),
method_indices: HashMap::new(),
method_signatures: Vec::new(),
builtin_dtables,
user_dtables: HashMap::new(),
prototypes: Vec::new(),
traits: Vec::new(),
}
}
pub fn create_global(&mut self, name: &str) -> Result<GlobalIndex, LanguageErrorKind> {
if self.globals.contains_key(name) {
Ok(*self.globals.get(name).unwrap())
} else {
let slot = Opr24::try_from(self.globals.len())
.map_err(|_| LanguageErrorKind::TooManyGlobals)?;
let slot = GlobalIndex(slot);
self.globals.insert(name.to_owned(), slot);
Ok(slot)
}
}
pub fn get_global(&self, name: &str) -> Option<GlobalIndex> {
self.globals.get(name).copied()
}
pub fn create_function(
&mut self,
function: Function,
) -> Result<FunctionIndex, LanguageErrorKind> {
let slot = Opr24::try_from(self.functions.len())
.map_err(|_| LanguageErrorKind::TooManyFunctions)?;
let slot = FunctionIndex(slot);
self.functions.push(function);
Ok(slot)
}
pub(crate) unsafe fn get_function_unchecked(&self, id: FunctionIndex) -> &Function {
let FunctionIndex(id) = id;
self.functions.get_unchecked(u32::from(id) as usize)
}
pub fn get_or_create_method_index(
&mut self,
signature: &MethodSignature,
) -> Result<MethodIndex, LanguageErrorKind> {
if let Some(&index) = self.method_indices.get(signature) {
Ok(index)
} else {
let index = u16::try_from(self.method_indices.len())
.map_err(|_| LanguageErrorKind::TooManyMethods)?;
let index = MethodIndex(index);
self.method_indices.insert(signature.clone(), index);
self.method_signatures.push(signature.clone());
Ok(index)
}
}
pub(crate) fn get_method_index(&self, signature: &MethodSignature) -> Option<MethodIndex> {
self.method_indices.get(signature).copied()
}
pub fn get_method_signature(&self, method_index: MethodIndex) -> Option<&MethodSignature> {
self.method_signatures.get(usize::from(method_index.0))
}
pub(crate) fn create_prototype(
&mut self,
proto: Prototype,
) -> Result<PrototypeIndex, LanguageErrorKind> {
let slot =
Opr24::try_from(self.prototypes.len()).map_err(|_| LanguageErrorKind::TooManyImpls)?;
let slot = PrototypeIndex(slot);
self.prototypes.push(Some(proto));
Ok(slot)
}
pub(crate) unsafe fn get_prototype_unchecked(&self, id: PrototypeIndex) -> &Prototype {
let PrototypeIndex(id) = id;
let proto = self.prototypes.get_unchecked(usize::from(id)).as_ref().unwrap_unchecked();
proto
}
pub fn create_trait(&mut self, name: Rc<str>) -> Result<TraitIndex, LanguageErrorKind> {
let slot_index = self.traits.len();
let slot = Opr24::try_from(slot_index).map_err(|_| LanguageErrorKind::TooManyTraits)?;
let slot = TraitIndex(slot);
self.traits.push(TraitPrototype { name, required: HashSet::new(), shims: vec![] });
Ok(slot)
}
pub fn get_trait(&self, id: TraitIndex) -> Option<&TraitPrototype> {
let TraitIndex(id) = id;
self.traits.get(usize::from(id))
}
pub fn get_trait_mut(&mut self, id: TraitIndex) -> Option<&mut TraitPrototype> {
let TraitIndex(id) = id;
self.traits.get_mut(usize::from(id))
}
pub fn add_user_dtable<T>(&mut self, dtable: Gc<DispatchTable>)
where
T: Any,
{
let type_id = TypeId::of::<T>();
self.user_dtables.insert(type_id, dtable);
}
pub fn get_user_dtable<T>(&self) -> Option<&Gc<DispatchTable>>
where
T: Any,
{
self.user_dtables.get(&TypeId::of::<T>())
}
}