use fnv::{FnvHashMap, FnvHashSet};
use smallvec::{SmallVec};
use super::code::{GFn};
use super::collections::{Arr, DequeAccess, DequeOps, Tab};
use super::engine::{glsp, Guard, Sym, stock_syms::*, ToSym, with_heap, with_vm};
use super::error::{GResult};
use super::gc::{Allocate, Raw, Header, Slot, Root, Visitor};
use super::iter::{GIter, GIterState};
use super::val::{Val};
use super::wrap::{CallableOps, FromVal, IntoCallArgs, IntoVal};
use std::{u16, str};
use std::cell::{RefCell, RefMut};
use std::cmp::{Ord};
use std::collections::{HashMap, HashSet, VecDeque};
use std::iter::{FromIterator};
use std::mem::{forget, size_of};
pub struct Obj {
header: Header,
class: Raw<Class>,
storage: RefCell<Option<ObjStorage>>
}
struct ObjStorage {
fields: Vec<Slot>,
states_enabled: u32,
raw_self: Raw<Obj>
}
pub struct Class {
header: Header,
name: Option<Sym>,
is_mixin: bool,
bindings: FnvHashMap<Sym, Binding>,
field_count: usize,
field_stack: Vec<FieldStackEntry>,
met_stack: Vec<MetStackEntry>,
is: FnvHashSet<Raw<Class>>,
states: FnvHashMap<Sym, State>,
raw_class: Option<Box<RawClass>>
}
#[derive(Clone)]
struct State {
index: u8,
name: Sym,
enabled_by_default: bool,
fsm_siblings: Vec<Sym>,
parent: Option<Sym>,
children: Vec<Sym>,
requires: u32,
required_by: u32,
excludes: u32,
init: Option<MetBinding>,
finis: Vec<MetBinding>,
}
#[derive(Clone)]
enum Binding {
SimpleField(u8, u16),
SimpleConst(u8, Slot),
StackableField(u16),
Met(MetBinding),
Prop(Option<MetBinding>, Option<MetBinding>),
}
#[derive(Clone)]
enum MetBinding {
Simple(u8, Raw<GFn>, bool),
Stackable(u8, u16),
}
#[derive(Clone)]
enum FieldStackEntry {
Field(u8, u16),
Const(u8, Slot),
End
}
#[derive(Clone)]
enum MetStackEntry {
Met(u8, Raw<GFn>, bool),
End
}
impl Binding {
fn to_field_stack_entry(&self) -> FieldStackEntry {
match *self {
Binding::SimpleField(state_i, field_i) => {
FieldStackEntry::Field(state_i, field_i)
}
Binding::SimpleConst(state_i, ref slot) => {
FieldStackEntry::Const(state_i, slot.clone())
}
_ => unreachable!()
}
}
fn to_met_stack_entry(&self) -> MetStackEntry {
match *self {
Binding::Met(ref met_binding) => met_binding.to_met_stack_entry(),
_ => unreachable!()
}
}
}
impl MetBinding {
fn to_met_stack_entry(&self) -> MetStackEntry {
match *self {
MetBinding::Simple(state_i, ref gfn, rni) => {
MetStackEntry::Met(state_i, gfn.clone(), rni)
}
_ => unreachable!()
}
}
}
impl Class {
pub(crate) fn new(tab: &Tab) -> GResult<Class> {
let mut raw_class = RawClass::from_tab(tab)?;
if raw_class.is_mixin {
Ok(Class {
header: Header::new(),
name: raw_class.name,
is_mixin: true,
bindings: FnvHashMap::default(),
field_count: 0,
field_stack: Vec::new(),
met_stack: Vec::new(),
is: FnvHashSet::default(),
states: FnvHashMap::default(),
raw_class: Some(Box::new(raw_class))
})
} else {
raw_class.mix()?;
ClassBuilder::new(raw_class)?.build()
}
}
#[inline(always)]
fn lookup(&self, key: Sym) -> Option<Slot> {
if let Some(binding) = self.bindings.get(&key) {
match *binding {
Binding::SimpleConst(0, ref slot) => Some(slot.clone()),
Binding::StackableField(mut stack_index) => {
loop {
match &self.field_stack[stack_index as usize] {
FieldStackEntry::Field(0, _) => return None,
FieldStackEntry::Const(0, ref slot) => return Some(slot.clone()),
FieldStackEntry::End => return None,
_ => stack_index += 1
}
}
}
_ => None
}
} else {
None
}
}
pub fn get<S: ToSym, V: FromVal>(&self, key: S) -> GResult<V> {
let sym = key.to_sym()?;
match self.lookup(sym) {
Some(slot) => Ok(V::from_slot(&slot)?),
None => {
bail!("attempted to access nonexistent const '{}'", sym)
}
}
}
pub fn get_if_present<S: ToSym, V: FromVal>(&self, key: S) -> GResult<Option<V>> {
let sym = key.to_sym()?;
match self.lookup(sym) {
Some(slot) => Ok(Some(V::from_slot(&slot)?)),
None => Ok(None)
}
}
pub(crate) fn get_method(&self, key: Sym) -> Option<(Slot, bool, bool, Slot)> {
match self.lookup(key) {
Some(slot @ Slot::GFn(_)) | Some(slot @ Slot::RFn(_)) | Some(slot @ Slot::Class(_)) => {
Some((slot, false, false, Slot::Nil))
}
Some(_) => None,
None => None
}
}
pub fn call<S, A, R>(&self, key: S, args: A) -> GResult<R>
where
S: ToSym,
A: IntoCallArgs,
R: FromVal
{
let sym = key.to_sym()?;
match self.call_if_present(sym, args)? {
Some(r) => Ok(r),
None => bail!("attempted to call nonexistent method '{}'", sym)
}
}
pub fn call_if_present<S, A, R>(&self, key: S, args: A) -> GResult<Option<R>>
where
S: ToSym,
A: IntoCallArgs,
R: FromVal
{
let sym = key.to_sym()?;
match self.lookup(sym) {
Some(Slot::GFn(gfn)) => Ok(Some(glsp::call(&gfn, args)?)),
Some(Slot::RFn(rfn)) => Ok(Some(glsp::call(&rfn, args)?)),
Some(Slot::Class(class_to_call)) => Ok(Some(glsp::call(&class_to_call, args)?)),
Some(_) => Ok(None),
None => Ok(None)
}
}
pub fn has_met<S: ToSym>(&self, key: S) -> GResult<bool> {
let sym = key.to_sym()?;
match self.lookup(sym) {
Some(Slot::GFn(_)) => Ok(true),
Some(Slot::RFn(_)) => Ok(true),
Some(Slot::Class(_)) => Ok(true),
Some(_) => Ok(false),
None => Ok(false)
}
}
pub fn access_giter(class: &Root<Class>, giter: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::AccessClass(class.to_raw(), giter.to_raw()))
}
pub fn has<S: ToSym>(&self, key: S) -> GResult<bool> {
let sym = key.to_sym()?;
Ok(self.lookup(sym).is_some())
}
pub fn name(&self) -> Option<Sym> {
self.name
}
pub fn has_state<S: ToSym>(&self, state_name: S) -> GResult<bool> {
let sym = state_name.to_sym()?;
Ok(self.states.contains_key(&sym))
}
pub fn has_mixin(&self, mixin: &Root<Class>) -> bool {
self.is.contains(mixin.as_raw())
}
pub fn is_mixin(&self) -> bool {
self.is_mixin
}
pub fn mixins(&self) -> Root<Arr> {
glsp::arr_from_iter(self.is.iter().map(|mixin| {
Slot::Class(mixin.clone())
})).unwrap()
}
}
impl CallableOps for Root<Class> {
fn receive_call(&self, arg_count: usize) -> GResult<Val> {
Ok(Val::Obj(glsp::call_class(self, arg_count)?))
}
fn arg_limits(&self) -> (usize, Option<usize>) {
let (gfn, has_nbi) = match self.states.get(&MAIN_SYM).unwrap().init {
Some(MetBinding::Simple(_, ref gfn, has_nbi)) => (gfn.clone(), has_nbi),
Some(MetBinding::Stackable(_, i)) => {
match self.met_stack[i as usize] {
MetStackEntry::Met(_, ref gfn, has_nbi) => (gfn.clone(), has_nbi),
_ => unreachable!()
}
}
None => {
return (0, Some(0))
}
};
let (raw_min_args, raw_max_args) = gfn.arg_limits();
let difference = if has_nbi { 2 } else { 1 };
let min_args = raw_min_args.checked_sub(difference).unwrap();
let max_args = raw_max_args.map(|max_args| max_args.checked_sub(difference).unwrap());
(min_args, max_args)
}
fn name(&self) -> Option<Sym> {
self.name
}
}
impl CallableOps for Raw<Class> {
fn receive_call(&self, arg_count: usize) -> GResult<Val> {
self.root().receive_call(arg_count)
}
fn arg_limits(&self) -> (usize, Option<usize>) {
self.root().arg_limits()
}
fn name(&self) -> Option<Sym> {
self.root().name()
}
}
enum Lookup {
FieldOrConst(Slot),
Met(MetLookup),
PropGetter(MetLookup),
NotBound
}
enum LookupMut<'a> {
Field(RefMut<'a, Slot>),
PropSetter(MetLookup),
Error(&'static str)
}
pub(crate) struct MetLookup {
pub(crate) gfn: Raw<GFn>,
pub(crate) requires_next_index: bool,
pub(crate) next_index: Option<u16>
}
impl Obj {
pub(crate) fn new<A>(class: &Root<Class>, args: A) -> GResult<Root<Obj>>
where
A: Clone + IntoCallArgs
{
ensure!(!class.is_mixin, "{} is a mixin; mixins cannot be instantiated",
class.name.unwrap());
let obj = Obj {
header: Header::new(),
class: Raw::from_root(class),
storage: RefCell::new(None)
};
let root = glsp::alloc(obj);
let prev_usage = root.memory_usage();
*root.storage.borrow_mut() = Some(ObjStorage {
fields: vec![Slot::Nil; class.field_count],
states_enabled: 0,
raw_self: root.to_raw()
});
with_heap(|heap| heap.memory_usage_barrier(&*root, prev_usage, root.memory_usage()));
root.enab(MAIN_SYM, args)?;
Ok(root)
}
pub fn kill(&self) -> GResult<()> {
if self.storage.borrow().is_some() {
self.disab_impl(MAIN_SYM)?;
self.kill_impl();
}
Ok(())
}
fn kill_impl(&self) {
let prev_usage = self.memory_usage();
*self.storage.borrow_mut() = None;
with_heap(|heap| {
heap.memory_usage_barrier(self, prev_usage, self.memory_usage());
});
}
pub fn is_killed(&self) -> bool {
self.storage.borrow().is_none()
}
pub fn freeze(&self) {
self.header.freeze()
}
pub fn is_frozen(&self) -> bool {
self.header.frozen()
}
pub fn class(&self) -> Root<Class> {
self.class.root()
}
pub fn is(&self, class: &Root<Class>) -> bool {
Root::ptr_eq(&self.class.root(), class) || self.class.has_mixin(class)
}
#[inline(always)]
fn lookup(&self, key: Sym) -> Lookup {
let storage_ref = self.storage.borrow();
let storage = match storage_ref.as_ref() {
Some(storage) => storage,
None => return Lookup::NotBound
};
let states_enabled = storage.states_enabled;
let state_is_accessible = |state_index: u8| {
states_enabled & (1 << state_index as u32) != 0
};
if let Some(binding) = self.class.bindings.get(&key) {
match *binding {
Binding::SimpleField(state_index, field_index) => {
if state_is_accessible(state_index) {
Lookup::FieldOrConst(storage.fields[field_index as usize].clone())
} else {
Lookup::NotBound
}
}
Binding::SimpleConst(state_index, ref slot) => {
if state_is_accessible(state_index) {
Lookup::FieldOrConst(slot.clone())
} else {
Lookup::NotBound
}
}
Binding::StackableField(mut stack_index) => {
loop {
match self.class.field_stack[stack_index as usize] {
FieldStackEntry::Field(state_index, field_index) => {
if state_is_accessible(state_index) {
let i = field_index as usize;
return Lookup::FieldOrConst(storage.fields[i].clone())
}
}
FieldStackEntry::Const(state_index, ref slot) => {
if state_is_accessible(state_index) {
return Lookup::FieldOrConst(slot.clone())
}
}
FieldStackEntry::End => return Lookup::NotBound
}
stack_index += 1;
}
}
Binding::Met(ref met_binding) => {
match self.lookup_met(storage, met_binding) {
Some(met_lookup) => Lookup::Met(met_lookup),
None => Lookup::NotBound
}
}
Binding::Prop(ref get_binding, _) => {
if let Some(ref get_binding) = *get_binding {
match self.lookup_met(storage, get_binding) {
Some(get_lookup) => Lookup::PropGetter(get_lookup),
None => Lookup::NotBound
}
} else {
Lookup::NotBound
}
}
}
} else {
Lookup::NotBound
}
}
#[inline(always)]
fn lookup_met(&self, storage: &ObjStorage, binding: &MetBinding) -> Option<MetLookup> {
let states_enabled = storage.states_enabled;
let state_is_accessible = |state_index: u8| {
states_enabled & (1 << state_index as u32) != 0
};
match *binding {
MetBinding::Simple(state_index, ref gfn, requires_next_index) => {
if state_is_accessible(state_index) {
Some(MetLookup {
gfn: gfn.clone(),
requires_next_index,
next_index: None
})
} else {
None
}
}
MetBinding::Stackable(state_index, mut stack_index) => {
if state_is_accessible(state_index) {
loop {
match self.class.met_stack[stack_index as usize] {
MetStackEntry::Met(state_index, ref gfn, requires_next_index) => {
if state_is_accessible(state_index) {
let i = (stack_index + 1) as usize;
let next_index = match self.class.met_stack[i] {
MetStackEntry::Met(..) => Some((stack_index + 1) as u16),
MetStackEntry::End => None
};
return Some(MetLookup {
gfn: gfn.clone(),
requires_next_index,
next_index
})
} else {
stack_index += 1;
}
}
MetStackEntry::End => return None
}
}
} else {
None
}
}
}
}
#[inline(always)]
fn lookup_mut(&self, key: Sym) -> LookupMut {
let mut storage_ref = self.storage.borrow_mut();
let storage = match storage_ref.as_mut() {
Some(storage) => storage,
None => return LookupMut::Error("not bound")
};
let states_enabled = storage.states_enabled;
let state_is_accessible = |state_index: u8| {
states_enabled & (1 << state_index as u32) != 0
};
if let Some(binding) = self.class.bindings.get(&key) {
match *binding {
Binding::SimpleField(state_index, field_index) => {
if state_is_accessible(state_index) {
let rm = RefMut::map(storage_ref, |s| {
&mut s.as_mut().unwrap().fields[field_index as usize]
});
LookupMut::Field(rm)
} else {
LookupMut::Error("not bound")
}
}
Binding::StackableField(mut stack_index) => {
loop {
match self.class.field_stack[stack_index as usize] {
FieldStackEntry::Field(state_index, field_index) => {
if state_is_accessible(state_index) {
let rm = RefMut::map(storage_ref, |s| {
&mut s.as_mut().unwrap().fields[field_index as usize]
});
return LookupMut::Field(rm)
}
}
FieldStackEntry::Const(state_index, _) => {
if state_is_accessible(state_index) {
return LookupMut::Error("a const")
}
}
FieldStackEntry::End => return LookupMut::Error("not bound")
}
stack_index += 1;
}
}
Binding::SimpleConst(state_index, _) => {
if state_is_accessible(state_index) {
LookupMut::Error("a const")
} else {
LookupMut::Error("not bound")
}
}
Binding::Prop(_, ref set_binding) => {
if let Some(ref set_binding) = *set_binding {
match self.lookup_met(storage, set_binding) {
Some(set_lookup) => LookupMut::PropSetter(set_lookup),
None => LookupMut::Error("not bound")
}
} else {
LookupMut::Error("a readonly prop")
}
}
Binding::Met(_) => LookupMut::Error("a method")
}
} else {
LookupMut::Error("not bound")
}
}
pub fn has<S: ToSym>(&self, key: S) -> GResult<bool> {
match self.lookup(key.to_sym()?) {
Lookup::FieldOrConst(_) | Lookup::PropGetter(_) => Ok(true),
Lookup::Met(_) | Lookup::NotBound => Ok(false)
}
}
pub fn has_met<S: ToSym>(&self, key: S) -> GResult<bool> {
match self.lookup(key.to_sym()?) {
Lookup::Met(..) => Ok(true),
Lookup::FieldOrConst(Slot::GFn(_)) => Ok(true),
Lookup::FieldOrConst(Slot::RFn(_)) => Ok(true),
Lookup::FieldOrConst(Slot::Class(_)) => Ok(true),
Lookup::FieldOrConst(_) => Ok(false),
Lookup::PropGetter(getter) => {
match self.invoke_method(&getter, &())? {
Slot::GFn(_) | Slot::RFn(_) | Slot::Class(_) => Ok(true),
_ => Ok(false)
}
}
Lookup::NotBound => Ok(false)
}
}
pub fn get<S: ToSym, V: FromVal>(&self, key: S) -> GResult<V> {
ensure!(self.storage.borrow().is_some(),
"attempted to access a field on a killed obj");
let sym = key.to_sym()?;
match self.lookup(sym) {
Lookup::FieldOrConst(slot) => Ok(V::from_slot(&slot)?),
Lookup::PropGetter(getter) => {
self.invoke_method(&getter, &())
}
Lookup::Met(..) => bail!("attempted to access method '{}' as a field", sym),
Lookup::NotBound => bail!("attempted to access nonexistent field '{}'", sym)
}
}
pub fn get_if_present<S: ToSym, V: FromVal>(&self, key: S) -> GResult<Option<V>> {
if self.storage.borrow().is_some() {
match self.lookup(key.to_sym()?) {
Lookup::FieldOrConst(slot) => Ok(Some(V::from_slot(&slot)?)),
Lookup::PropGetter(getter) => {
Ok(Some(self.invoke_method(&getter, &())?))
}
Lookup::Met(..) | Lookup::NotBound => Ok(None)
}
} else {
Ok(None)
}
}
pub fn access_giter(obj: &Root<Obj>, giter: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::AccessObj(obj.to_raw(), giter.to_raw()))
}
pub(crate) fn get_method(
&self,
method_name: Sym
) -> GResult<Option<(Slot, bool, bool, Slot)>>
{
Ok(match self.lookup(method_name) {
Lookup::FieldOrConst(slot) => {
match slot {
Slot::GFn(_) | Slot::RFn(_) | Slot::Class(_) => {
Some((slot, false, false, Slot::Nil))
}
_ => None
}
}
Lookup::PropGetter(getter) => {
let slot = self.invoke_method(&getter, &())?;
match slot {
Slot::GFn(_) | Slot::RFn(_) | Slot::Class(_) => {
Some((slot, false, false, Slot::Nil))
}
_ => None
}
}
Lookup::Met(MetLookup { gfn, requires_next_index, next_index }) => {
let ni_slot = match next_index {
Some(ni) => Slot::Int(ni as i32),
None => Slot::Nil
};
Some((Slot::GFn(gfn), true, requires_next_index, ni_slot))
}
Lookup::NotBound => None
})
}
pub(crate) fn get_base_raw_method(&self, mut index: usize) -> Option<MetLookup> {
let states_enabled = self.storage.borrow().as_ref().unwrap().states_enabled;
loop {
match self.class.met_stack[index] {
MetStackEntry::End => {
return None
}
MetStackEntry::Met(state_id, ref gfn, requires_next_index) => {
if states_enabled & (1 << state_id as u32) != 0 {
let next_index = match self.class.met_stack[index + 1] {
MetStackEntry::End => None,
_ => Some((index + 1) as u16)
};
return Some(MetLookup {
gfn: gfn.clone(),
requires_next_index,
next_index
})
} else {
index += 1
}
}
}
}
}
#[doc(hidden)]
pub fn raw_call<A>(&self, index: usize, args: A) -> GResult<Val>
where
A: IntoCallArgs
{
match self.get_base_raw_method(index) {
Some(met_lookup) => self.invoke_method(&met_lookup, args),
None => Ok(Val::Nil)
}
}
pub fn call<S, A, R>(&self, key: S, args: A) -> GResult<R>
where
S: ToSym,
A: IntoCallArgs,
R: FromVal
{
let sym = key.to_sym()?;
match self.call_if_present(sym, args)? {
Some(r) => Ok(r),
None => bail!("attempted to call nonexistent method '{}'", sym)
}
}
pub fn call_if_present<S, A, R>(&self, key: S, args: A) -> GResult<Option<R>>
where
S: ToSym,
A: IntoCallArgs,
R: FromVal
{
let sym = key.to_sym()?;
if self.storage.borrow().is_some() {
let slot = match self.lookup(sym) {
Lookup::Met(met_lookup) => {
return Ok(Some(self.invoke_method(&met_lookup, args)?))
}
Lookup::FieldOrConst(slot) => slot,
Lookup::PropGetter(getter) => self.invoke_method(&getter, &())?,
Lookup::NotBound => return Ok(None)
};
match slot {
Slot::GFn(gfn) => Ok(Some(glsp::call(&gfn, args)?)),
Slot::RFn(rfn) => Ok(Some(glsp::call(&rfn, args)?)),
Slot::Class(class_to_call) => Ok(Some(glsp::call(&class_to_call, args)?)),
_ => Ok(None)
}
} else {
Ok(None)
}
}
fn invoke_method<A, R>(&self, met_lookup: &MetLookup, args: A) -> GResult<R>
where
A: IntoCallArgs,
R: FromVal
{
with_vm(|vm| {
let mut stacks = vm.stacks.borrow_mut();
let starting_len = stacks.regs.len();
stacks.regs.push(Slot::Obj(self.storage.borrow().as_ref().unwrap().raw_self.clone()));
if met_lookup.requires_next_index {
let next_index_slot = match met_lookup.next_index {
Some(next_index) => Slot::Int(next_index as i32),
None => Slot::Nil
};
stacks.regs.push(next_index_slot);
}
args.into_call_args(&mut stacks.regs)?;
let arg_count = stacks.regs.len() - starting_len;
drop(stacks);
let val = met_lookup.gfn.root().receive_call(arg_count)?;
R::from_val(&val)
})
}
fn set_impl<S: ToSym, V: IntoVal>(&self, key: S, value: V) -> GResult<Option<&'static str>> {
ensure!(!self.header.frozen(), "attempted to mutate a frozen obj");
ensure!(self.storage.borrow().is_some(), "attempted to mutate a field on a killed obj");
let sym = key.to_sym()?;
match self.lookup_mut(sym) {
LookupMut::Field(mut field) => {
let slot = value.into_slot()?;
with_heap(|heap| heap.write_barrier_slot(self, &slot));
*field = slot;
Ok(None)
}
LookupMut::PropSetter(setter) => {
let _: Slot = self.invoke_method(&setter, (value,))?;
Ok(None)
}
LookupMut::Error(msg) => Ok(Some(msg))
}
}
pub fn set<S: ToSym, V: IntoVal>(&self, key: S, value: V) -> GResult<()> {
let key_sym = key.to_sym()?;
match self.set_impl(key_sym, value)? {
Some(msg) => bail!("attempted to set field '{}', which is {}", key_sym, msg),
None => Ok(())
}
}
pub fn set_if_present<S: ToSym, V: IntoVal>(&self, key: S, value: V) -> GResult<bool> {
Ok(self.set_impl(key, value)?.is_none())
}
pub fn has_state<S: ToSym>(&self, state_name: S) -> GResult<bool> {
Ok(self.class.states.contains_key(&state_name.to_sym()?))
}
pub fn is_enab<S: ToSym>(&self, state_name: S) -> GResult<bool> {
let sym = state_name.to_sym()?;
let storage_ref = self.storage.borrow();
let storage = match storage_ref.as_ref() {
Some(storage) => storage,
None => bail!("attempted to query the state '{}' on a killed obj", sym)
};
match self.class.states.get(&sym) {
Some(state_ref) => Ok(storage.states_enabled & (1 << state_ref.index as u32) != 0),
None => bail!("attempted to query a nonexistent state '{}'", sym)
}
}
pub fn enab<S, A>(&self, state_name: S, args: A) -> GResult<()>
where
S: ToSym,
A: Clone + IntoCallArgs
{
let sym = state_name.to_sym()?;
ensure!(self.class.states.contains_key(&sym),
"attempted to enable the nonexistent state '{}'", sym);
ensure!(!self.header.frozen(),
"attempted to enable the state '{}' on a frozen obj", sym);
ensure!(self.storage.borrow().is_some(),
"attempted to enable the state '{}' on a killed obj", sym);
self.recursively_enable_state(sym, args)
}
fn recursively_enable_state<A: Clone + IntoCallArgs>(
&self,
state_name: Sym,
args: A
) -> GResult<()> {
let guard = Guard::new(|| self.kill_impl());
let state_ref = self.class.states.get(&state_name).unwrap();
let mut states_enabled = self.storage.borrow().as_ref().unwrap().states_enabled;
if let Some(parent_name) = state_ref.parent {
let parent_ref = self.class.states.get(&parent_name).unwrap();
if states_enabled & (1 << parent_ref.index as u32) == 0 {
self.recursively_enable_state(parent_name, args.clone())?;
states_enabled = self.storage.borrow().as_ref().unwrap().states_enabled;
if states_enabled & (1 << state_ref.index as u32) != 0 {
forget(guard);
return Ok(())
}
}
}
for sibling_name in &state_ref.fsm_siblings {
let sibling_ref = self.class.states.get(&sibling_name).unwrap();
if states_enabled & (1 << sibling_ref.index as u32) != 0 {
self.disab(*sibling_name)?;
break
}
}
self.enable_state(state_name, args)?;
states_enabled = self.storage.borrow().as_ref().unwrap().states_enabled;
for child_name in &state_ref.children {
let child_ref = self.class.states.get(child_name).unwrap();
if child_ref.enabled_by_default {
if states_enabled & (1 << child_ref.index as u32) == 0 {
let empty_args: &[Slot] = &[];
self.recursively_enable_state(*child_name, empty_args)?;
}
}
}
forget(guard);
Ok(())
}
fn enable_state<A: IntoCallArgs>(
&self,
state_name: Sym,
args: A
) -> GResult<()> {
let mut storage_ref = self.storage.borrow_mut();
let storage = storage_ref.as_mut().unwrap();
let state_ref = self.class.states.get(&state_name).unwrap();
let state_bit = 1 << state_ref.index as u32;
ensure!(storage.states_enabled & state_bit == 0,
"attempted to enable the state {}, which is already enabled", state_name);
storage.states_enabled |= state_bit;
let states_enabled = storage.states_enabled;
ensure!((states_enabled & state_ref.requires) == state_ref.requires,
"attempted to enable {}, but it wraps a method in a state which is not enabled",
state_ref.name);
ensure!((states_enabled & state_ref.excludes) == 0, "attempted to enable {}, but \
this caused a method name collision with another state", state_ref.name);
if let Some(ref init_binding) = state_ref.init {
let init_lookup = self.lookup_met(storage, init_binding).unwrap();
drop(storage_ref);
let _: Slot = self.invoke_method(&init_lookup, args)?;
} else {
ensure!(args.arg_count() == 0, "too many arguments to state {}'s initializer: \
expected 0, received {}", state_name, args.arg_count());
}
Ok(())
}
pub fn disab<S: ToSym>(&self, state_name: S) -> GResult<()> {
let state_name_sym = state_name.to_sym()?;
ensure!(state_name_sym != MAIN_SYM, "the `Main` state cannot be disabled");
self.disab_impl(state_name_sym)
}
fn disab_impl<S: ToSym>(&self, state_name: S) -> GResult<()> {
let state_name_sym = state_name.to_sym()?;
ensure!(!self.header.frozen(),
"attempted to disable the state '{}' on a frozen obj", state_name_sym);
ensure!(self.storage.borrow().is_some(),
"attempted to disable the state '{}' on a killed obj", state_name_sym);
let guard = Guard::new(|| self.kill_impl());
let states_enabled = self.storage.borrow().as_ref().unwrap().states_enabled;
let state_ref = match self.class.states.get(&state_name_sym) {
Some(state_ref) => state_ref,
None => bail!("attempted to disable the nonexistent state '{}'", state_name_sym)
};
for child_name in state_ref.children.iter().rev() {
let child_ref = self.class.states.get(&child_name).unwrap();
if states_enabled & (1 << child_ref.index as u32) != 0 {
self.disab(*child_name)?;
}
}
for fini_binding in state_ref.finis.iter().rev() {
let storage = self.storage.borrow();
let fini_lookup = self.lookup_met(
&storage.as_ref().unwrap(),
&fini_binding
).unwrap();
drop(storage);
let _: Slot = self.invoke_method(&fini_lookup, &())?;
}
if state_name_sym != MAIN_SYM {
self.disable_state(state_name_sym)?;
}
forget(guard);
Ok(())
}
fn disable_state(&self, state_name: Sym) -> GResult<()> {
let mut storage_ref = self.storage.borrow_mut();
let storage = storage_ref.as_mut().unwrap();
let state_ref = self.class.states.get(&state_name).unwrap();
let state_bit = 1 << state_ref.index as u32;
ensure!(storage.states_enabled & state_bit != 0,
"attempted to disable the state {}, which is already disabled", state_name);
ensure!((storage.states_enabled & state_ref.required_by) == 0, "attempted to disable {}, \
but an active state is currently wrapping one of its methods", state_ref.name);
for binding in self.class.bindings.values() {
if let Binding::SimpleField(field_state_index, field_index) = *binding {
if field_state_index == state_ref.index as u8 {
storage.fields[field_index as usize] = Slot::Nil;
}
}
}
for entry in &self.class.field_stack {
if let FieldStackEntry::Field(field_state_index, field_index) = *entry {
if field_state_index == state_ref.index as u8 {
storage.fields[field_index as usize] = Slot::Nil;
}
}
}
storage.states_enabled &= !state_bit;
Ok(())
}
pub fn try_eq(&self, other: &Root<Obj>) -> GResult<bool> {
if !Root::ptr_eq(&self.class(), &other.class()) {
return Ok(false)
}
let val: Option<Val> = self.call_if_present(OP_EQP_SYM, (other,))?;
match val {
Some(val) => Ok(val.is_truthy()),
None => Ok(false)
}
}
}
impl PartialEq<Root<Obj>> for Obj {
fn eq(&self, other: &Root<Obj>) -> bool {
self.try_eq(other).unwrap()
}
}
impl Allocate for Obj {
fn header(&self) -> &Header {
&self.header
}
fn visit_raws<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_raw(&self.class);
let storage_ref = self.storage.borrow();
if let Some(ref storage) = *storage_ref {
for field in &storage.fields {
visitor.visit_slot(field);
}
visitor.visit_raw(&storage.raw_self);
}
}
fn clear_raws(&self) {
*self.storage.borrow_mut() = None;
}
fn owned_memory_usage(&self) -> usize {
let storage_ref = self.storage.borrow();
if let Some(ref storage) = *storage_ref {
storage.fields.capacity() * size_of::<Slot>()
} else {
0
}
}
}
impl Allocate for Class {
fn header(&self) -> &Header {
&self.header
}
fn visit_raws<V: Visitor>(&self, visitor: &mut V) {
for binding in self.bindings.values() {
match *binding {
Binding::SimpleField(_, _) => (),
Binding::SimpleConst(_, ref val) => visitor.visit_slot(val),
Binding::StackableField(_) => (),
Binding::Met(MetBinding::Simple(_, ref gfn, _)) => visitor.visit_raw(gfn),
Binding::Met(MetBinding::Stackable(_, _)) => (),
Binding::Prop(ref getter, ref setter) => {
if let Some(MetBinding::Simple(_, ref gfn, _)) = *getter {
visitor.visit_raw(gfn);
}
if let Some(MetBinding::Simple(_, ref gfn, _)) = *setter {
visitor.visit_raw(gfn);
}
}
}
}
for entry in &self.field_stack {
match *entry {
FieldStackEntry::Field(_, _) => (),
FieldStackEntry::Const(_, ref val) => visitor.visit_slot(val),
FieldStackEntry::End => ()
}
}
for entry in &self.met_stack {
match *entry {
MetStackEntry::Met(_, ref gfn, _) => visitor.visit_raw(gfn),
MetStackEntry::End => ()
}
}
for class in &self.is {
visitor.visit_raw(class);
}
for state in self.states.values() {
for met_binding in state.init.iter().chain(state.finis.iter()) {
match *met_binding {
MetBinding::Simple(_, ref gfn, _) => visitor.visit_raw(gfn),
MetBinding::Stackable(_, _) => ()
}
}
}
if let Some(raw_class) = self.raw_class.as_ref() {
for class in &raw_class.mixins {
visitor.visit_raw(class);
}
for raw_init in raw_class.inits.iter().chain(raw_class.finis.iter()) {
visitor.visit_raw(&raw_init.gfn);
}
for binding in &raw_class.bindings {
match binding.bindee {
RawBindee::Field => (),
RawBindee::PreConst(ref gfn) => {
if let Some(ref gfn) = *gfn {
visitor.visit_raw(gfn)
}
}
RawBindee::Const(ref slot) => visitor.visit_slot(slot),
RawBindee::Met(ref gfn) => visitor.visit_raw(gfn),
RawBindee::Wrap(_, ref gfn) => visitor.visit_raw(gfn),
RawBindee::WildcardWrap(ref gfn) => visitor.visit_raw(gfn),
RawBindee::Prop(_, _, ref get, ref set) |
RawBindee::WrapProp(_, _, _, ref get, ref set) |
RawBindee::WildcardWrapProp(_, _, ref get, ref set) => {
if let Some(ref get) = *get {
visitor.visit_raw(get);
}
if let Some(ref set) = *set {
visitor.visit_raw(set);
}
}
}
}
}
}
fn clear_raws(&self) {
}
fn owned_memory_usage(&self) -> usize {
let basic = self.bindings.capacity() * size_of::<(Sym, Binding)>()
+ self.field_stack.capacity() * size_of::<FieldStackEntry>()
+ self.met_stack.capacity() * size_of::<MetStackEntry>()
+ self.is.capacity() * size_of::<Raw<Class>>()
+ self.states.capacity() * size_of::<(Sym, State)>();
let mut states = 0usize;
for state in self.states.values() {
states += state.fsm_siblings.capacity() * size_of::<Sym>();
states += state.children.capacity() * size_of::<Sym>();
states += state.finis.capacity() * size_of::<MetBinding>();
}
let raw_class = if let Some(raw_class) = self.raw_class.as_ref() {
let mut raw = size_of::<RawClass>() +
raw_class.mixins.capacity() * size_of::<Vec<Raw<Class>>>() +
raw_class.states.capacity() * size_of::<State>() +
raw_class.bindings.capacity() * size_of::<RawBinding>() +
raw_class.inits.capacity() * size_of::<RawInit>() +
raw_class.finis.capacity() * size_of::<RawInit>();
for state in &raw_class.states {
raw += state.fsm_siblings.capacity() * size_of::<Sym>();
raw += state.children.capacity() * size_of::<Sym>();
raw += state.finis.capacity() * size_of::<MetBinding>();
}
raw
} else {
0
};
basic + states + raw_class
}
}
struct RawClass {
name: Option<Sym>,
is_mixin: bool,
mixins: Vec<Raw<Class>>,
states: Vec<State>,
bindings: Vec<RawBinding>,
inits: Vec<RawInit>,
finis: Vec<RawInit>,
}
#[derive(Clone)]
struct RawBinding {
unqualified: Sym,
qualified: Sym,
state_name: Sym,
state_i: u8,
bindee: RawBindee
}
#[derive(Clone)]
enum RawBindee {
Field,
PreConst(Option<Raw<GFn>>),
Const(Slot),
Met(Raw<GFn>),
Wrap(Sym, Raw<GFn>),
WildcardWrap(Raw<GFn>),
Prop(Sym, Sym, Option<Raw<GFn>>, Option<Raw<GFn>>),
WrapProp(Sym, Sym, Sym, Option<Raw<GFn>>, Option<Raw<GFn>>),
WildcardWrapProp(Sym, Sym, Option<Raw<GFn>>, Option<Raw<GFn>>),
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Category {
FieldLike,
MetLike,
PropLike
}
impl RawBindee {
fn category(&self) -> Category {
use RawBindee::*;
match self {
Field | PreConst(_) | Const(_) => Category::FieldLike,
Met(_) | Wrap(_, _) | WildcardWrap(_) => Category::MetLike,
Prop(..) | WrapProp(..) | WildcardWrapProp(..) => Category::PropLike
}
}
}
#[derive(Clone)]
struct RawInit {
state_name: Sym,
gfn: Raw<GFn>,
requires_next_index: bool
}
impl RawClass {
fn from_tab(tab: &Tab) -> GResult<RawClass> {
let name = tab.get_if_present::<_, Sym>(NAME_SYM)?;
let is_mixin = tab.get::<_, bool>(MIXINP_SYM)?;
let mixins = tab.get::<_, Vec<Raw<Class>>>(MIXIN_SYM)?;
let mut states = Vec::<State>::new();
let states_arr = tab.get::<_, Raw<Arr>>(STATES_SYM)?;
for maybe_state_tab in states_arr.iter_to::<Raw<Tab>>() {
let state_tab = maybe_state_tab?;
ensure!(states.len() <= 32, "{} states in a class, but the limit is 31",
states.len() - 1);
let state_i: u8 = states.len() as u8;
states.push(State::from_tab(&state_tab, state_i)?);
}
ensure!(!states.is_empty() && states[0].name == MAIN_SYM);
ensure!(states[0].enabled_by_default);
ensure!(states[0].fsm_siblings.is_empty());
ensure!(states[0].parent.is_none());
let state_ids = HashMap::<Sym, u8>::from_iter(states.iter()
.enumerate()
.map(|(i, state)| (state.name, i as u8)));
let mut bindings = Vec::<RawBinding>::new();
let bindings_arr: Raw<Arr> = tab.get(BINDINGS_SYM)?;
for maybe_arr in bindings_arr.iter_to::<Raw<Arr>>() {
let arr = maybe_arr?;
ensure!(arr.len() >= 4);
let unqualified: Sym = arr.get(0)?;
let qualified: Sym = arr.get(1)?;
let state_name: Sym = arr.get(2)?;
let tag: Sym = arr.get(3)?;
let bindee = match tag {
FIELD_SYM => RawBindee::Field,
CONST_SYM => RawBindee::PreConst(if arr.len() >= 5 {
Some(arr.get(4)?)
} else {
None
}),
MET_SYM => RawBindee::Met(arr.get(4)?),
WRAP_SYM => RawBindee::Wrap(arr.get(4)?, arr.get(5)?),
WILDCARD_WRAP_SYM => RawBindee::WildcardWrap(arr.get(4)?),
PROP_SYM | WRAP_PROP_SYM | WILDCARD_WRAP_PROP_SYM => {
let qualified_get: Sym = arr.get(4)?;
let qualified_set: Sym = arr.get(5)?;
let getter = match arr.get(-2)? {
Slot::Nil => None,
Slot::GFn(gfn) => Some(gfn),
slot => bail!("expected nil or a fn, received {}", slot.a_type_name())
};
let setter = match arr.get(-1)? {
Slot::Nil => None,
Slot::GFn(gfn) => Some(gfn),
slot => bail!("expected nil or a fn, received {}", slot.a_type_name())
};
match tag {
PROP_SYM => RawBindee::Prop(qualified_get, qualified_set, getter, setter),
WRAP_PROP_SYM => {
RawBindee::WrapProp(qualified_get, qualified_set, arr.get(6)?,
getter, setter)
}
WILDCARD_WRAP_PROP_SYM => {
RawBindee::WildcardWrapProp(qualified_get, qualified_set,
getter, setter)
}
_ => unreachable!()
}
}
tag => bail!("unrecognized bindee tag {}", tag)
};
bindings.push(RawBinding {
unqualified,
qualified,
state_name,
state_i: state_ids[&state_name],
bindee
});
}
let mut inits = Vec::<RawInit>::new();
let inits_arr: Raw<Arr> = tab.get(INITS_SYM)?;
for i in 0 .. inits_arr.len() {
let (state_name, gfn, requires_next_index): (Sym, Raw<GFn>, bool) = inits_arr.get(i)?;
inits.push(RawInit { state_name, gfn, requires_next_index });
}
let mut finis = Vec::<RawInit>::new();
let finis_arr: Raw<Arr> = tab.get(FINIS_SYM)?;
for i in 0 .. finis_arr.len() {
let (state_name, gfn): (Sym, Raw<GFn>) = finis_arr.get(i)?;
finis.push(RawInit { state_name, gfn, requires_next_index: false });
}
Ok(RawClass {
name,
is_mixin,
mixins,
states,
bindings,
inits,
finis
})
}
fn mix(&mut self) -> GResult<()> {
if !self.is_mixin && self.mixins.len() > 0 {
let mut names = HashSet::<Sym>::new();
for state in &self.states {
let state_name = state.name;
ensure!(names.insert(state_name), "duplicate state name {}", state_name);
}
for mixin in &self.mixins {
if !mixin.is_mixin {
match mixin.name {
Some(name) => bail!("{} is not a mixin", name),
None => bail!("attempted to use a non-mixin class as a mixin")
}
}
let mixin_name = mixin.name.unwrap();
ensure!(names.insert(mixin_name), "duplicate state or mixin name {}", mixin_name);
let raw_mixin = mixin.raw_class.as_ref().unwrap();
for state in &raw_mixin.states[1..] {
let state_name = state.name;
if state_name == mixin_name {
let msg = "a mixin-state must only contain a single (state)/(state*) form";
ensure!(raw_mixin.states.iter()
.filter(|s| s.parent == Some(MAIN_SYM))
.count() == 1, msg);
for binding in &raw_mixin.bindings {
ensure!(binding.state_name != MAIN_SYM, msg)
}
for raw_init in raw_mixin.inits.iter().chain(raw_mixin.finis.iter()) {
ensure!(raw_init.state_name != MAIN_SYM, msg)
}
} else {
ensure!(names.insert(state_name), "duplicate state name {}", state_name);
}
}
}
for mixin in self.mixins.clone().into_iter().rev() {
self.apply_mixin(mixin)?;
}
}
Ok(())
}
fn apply_mixin(&mut self, mixin: Raw<Class>) -> GResult<()> {
let raw_mixin = mixin.raw_class.as_ref().unwrap();
self.inits.splice(0..0, raw_mixin.inits.iter().cloned());
self.finis.splice(0..0, raw_mixin.finis.iter().cloned());
let old_mains = self.bindings.iter().filter(|rb| rb.state_name == MAIN_SYM).count();
let new_mains = raw_mixin.bindings.iter().filter(|rb| rb.state_name == MAIN_SYM).count();
assert!(self.bindings[old_mains..].iter().all(|rb| rb.state_name != MAIN_SYM));
assert!(raw_mixin.bindings[new_mains..].iter().all(|rb| rb.state_name != MAIN_SYM));
self.bindings.splice(0..0, raw_mixin.bindings[..new_mains].iter().cloned());
self.bindings.splice(new_mains + old_mains .. new_mains + old_mains,
raw_mixin.bindings[new_mains..].iter().cloned());
self.states.splice(1..1, raw_mixin.states[1..].iter().cloned());
ensure!(self.states.len() <= 32, "a single class may not contain more than 31 states");
for i in 1 .. self.states.len() {
self.states[i].index = i as u8;
}
let i_offset = (raw_mixin.states.len() - 1) as u8;
for rb in old_mains + raw_mixin.bindings.len() .. self.bindings.len() {
assert!(self.bindings[rb].state_i != 0);
self.bindings[rb].state_i += i_offset;
}
self.states[0].children.splice(0..0, raw_mixin.states.iter()
.skip(1)
.filter(|state| state.parent == Some(MAIN_SYM))
.map(|state| state.name));
Ok(())
}
}
struct ClassBuilder {
raw_class: Box<RawClass>,
is: FnvHashSet<Raw<Class>>,
states: FnvHashMap<Sym, State>,
bindings: FnvHashMap<Sym, Binding>,
field_count: u16,
field_stack: Vec<FieldStackEntry>,
met_stack: Vec<MetStackEntry>
}
impl ClassBuilder {
fn new(raw_class: RawClass) -> GResult<ClassBuilder> {
let is = FnvHashSet::from_iter(raw_class.mixins.iter().cloned());
let mut states = FnvHashMap::with_capacity_and_hasher(
raw_class.states.len(),
Default::default()
);
for state in &raw_class.states {
ensure!(states.insert(state.name, state.clone()).is_none(),
"duplicate state {}", state.name);
}
Ok(ClassBuilder {
raw_class: Box::new(raw_class),
is,
states,
bindings: FnvHashMap::default(),
field_count: 0,
field_stack: Vec::new(),
met_stack: Vec::new()
})
}
fn build(mut self) -> GResult<Class> {
self.evaluate_consts()?;
let mut main_inits = Vec::<RawInit>::new();
let mut main_finis = Vec::<RawInit>::new();
for init in &self.raw_class.inits {
if init.state_name == MAIN_SYM {
main_inits.push(init.clone());
} else {
let state = self.states.get_mut(&init.state_name).unwrap();
ensure!(state.init.is_none(), "multiple (init-state) forms in {}", state.name);
state.init = Some(MetBinding::Simple(
state.index,
init.gfn.clone(),
false
));
}
}
if main_inits.len() == 1 {
let state = self.states.get_mut(&MAIN_SYM).unwrap();
state.init = Some(MetBinding::Simple(
state.index,
main_inits[0].gfn.clone(),
main_inits[0].requires_next_index
));
} else if main_inits.len() > 1 {
let state = self.states.get_mut(&MAIN_SYM).unwrap();
state.init = Some(MetBinding::Stackable(state.index, self.met_stack.len() as u16));
for init in &main_inits {
self.met_stack.push(MetStackEntry::Met(
state.index,
init.gfn.clone(),
init.requires_next_index
));
}
self.met_stack.push(MetStackEntry::End);
}
for fini in &self.raw_class.finis {
if fini.state_name == MAIN_SYM {
main_finis.push(fini.clone());
} else {
let state = self.states.get_mut(&fini.state_name).unwrap();
ensure!(state.finis.is_empty(), "multiple (fini-state) forms in {}", state.name);
state.finis.push(MetBinding::Simple(
state.index,
fini.gfn.clone(),
false
));
}
}
for fini in &main_finis {
let state = self.states.get_mut(&MAIN_SYM).unwrap();
state.finis.push(MetBinding::Simple(
state.index,
fini.gfn.clone(),
false
));
}
let mut grouped = self.raw_class.bindings.clone();
grouped.sort_by(|a, b| a.unqualified.0.cmp(&b.unqualified.0));
let mut group = VecDeque::<RawBinding>::with_capacity(16);
while grouped.len() > 0 {
group.clear();
group.push_front(grouped.pop().unwrap());
let qualified = group[0].qualified;
let unqualified = group[0].unqualified;
let category = group[0].bindee.category();
while grouped.len() > 0 && grouped.last().unwrap().unqualified == unqualified {
group.push_front(grouped.pop().unwrap());
}
for i in 0 .. group.len() - 1 {
ensure!(group[i].bindee.category() == category, "the name {} is bound to class \
clauses of different types", unqualified);
}
match category {
Category::FieldLike => {
let mut seen_states = 0u32;
for binding in &group {
if (seen_states & (1 << binding.state_i as u32)) != 0 {
bail!("duplicate field/const {} in the {} state",
binding.qualified, binding.state_name);
}
seen_states |= 1 << binding.state_i as u32;
}
if group.len() > 1 {
let field_stack_start = self.field_stack.len() as u16;
for raw_binding in group.iter().rev() {
let binding = self.bind_field_like(raw_binding)?;
ensure!(self.bindings.insert(
raw_binding.qualified,
binding.clone()).is_none()
);
self.field_stack.push(binding.to_field_stack_entry());
}
self.field_stack.push(FieldStackEntry::End);
ensure!(self.field_stack.len() <= u16::MAX as usize,
"too many shadowing fields/consts");
ensure!(self.bindings.insert(
unqualified,
Binding::StackableField(field_stack_start)
).is_none());
} else {
let binding = self.bind_field_like(&group[0])?;
ensure!(self.bindings.insert(unqualified, binding.clone()).is_none());
ensure!(self.bindings.insert(qualified, binding).is_none());
}
}
Category::MetLike => {
let stacked = self.stack_mets(&group)?;
if stacked.len() > 1 {
let met_stack_start = self.met_stack.len() as u16;
for raw_binding in stacked.iter().rev() {
ensure!(self.met_stack.len() <= u16::MAX as usize,
"too many wrapper methods");
let stack_i = self.met_stack.len() as u16;
if !matches!(raw_binding.bindee, RawBindee::WildcardWrap(_)) {
ensure!(self.bindings.insert(
raw_binding.qualified,
Binding::Met(MetBinding::Stackable(
raw_binding.state_i,
stack_i
))
).is_none());
}
let binding = self.bind_met_like(raw_binding);
let stack_entry = binding.to_met_stack_entry();
self.met_stack.push(stack_entry);
}
self.met_stack.push(MetStackEntry::End);
ensure!(self.met_stack.len() <= u16::MAX as usize,
"too many wrapper methods");
ensure!(self.bindings.insert(
unqualified,
Binding::Met(MetBinding::Stackable(0, met_stack_start))
).is_none());
} else {
let binding = self.bind_met_like(&stacked[0]);
ensure!(self.bindings.insert(unqualified, binding.clone()).is_none());
ensure!(self.bindings.insert(qualified, binding).is_none());
}
}
Category::PropLike => {
let stacked = self.stack_mets(&group)?;
if stacked.len() > 1 {
let bindings = SmallVec::<[Binding; 8]>::from_iter(stacked.iter().rev()
.map(|raw_binding| self.bind_prop_like(raw_binding)));
let (has_getter, has_setter) = match &bindings[0] {
Binding::Prop(ref get, ref set) => (get.is_some(), set.is_some()),
_ => unreachable!()
};
for i in 1 .. bindings.len() {
match bindings[i] {
Binding::Prop(ref get, ref set) => {
ensure!(has_getter == get.is_some() &&
has_setter == set.is_some(),
"getter/setter mismatch between wrap-prop {} and \
prop {}", stacked[i].qualified, stacked[0].qualified);
}
_ => unreachable!()
}
}
let getter_binding = if has_getter {
let met_stack_start = self.met_stack.len() as u16;
for binding in bindings.iter() {
ensure!(self.met_stack.len() <= u16::MAX as usize,
"too many wrapper methods");
let stack_entry = match *binding {
Binding::Prop(ref get, _) => {
get.as_ref().unwrap().to_met_stack_entry()
}
_ => unreachable!()
};
self.met_stack.push(stack_entry);
}
self.met_stack.push(MetStackEntry::End);
Some(MetBinding::Stackable(0, met_stack_start))
} else {
None
};
let setter_binding = if has_setter {
let met_stack_start = self.met_stack.len() as u16;
for binding in bindings.iter() {
ensure!(self.met_stack.len() <= u16::MAX as usize,
"too many wrapper methods");
let stack_entry = match *binding {
Binding::Prop(_, ref set) => {
set.as_ref().unwrap().to_met_stack_entry()
}
_ => unreachable!()
};
self.met_stack.push(stack_entry);
}
self.met_stack.push(MetStackEntry::End);
Some(MetBinding::Stackable(0, met_stack_start))
} else {
None
};
ensure!(self.met_stack.len() <= u16::MAX as usize,
"too many wrapper methods");
for (i, raw_binding) in stacked.iter().rev().enumerate() {
if !matches!(raw_binding.bindee, RawBindee::WildcardWrapProp(..)) {
let getter_met_binding = match getter_binding {
Some(MetBinding::Stackable(_, stack_start)) => {
Some(MetBinding::Stackable(
raw_binding.state_i,
stack_start + i as u16
))
}
None => None,
_ => unreachable!()
};
let setter_met_binding = match setter_binding {
Some(MetBinding::Stackable(_, stack_start)) => {
Some(MetBinding::Stackable(
raw_binding.state_i,
stack_start + i as u16
))
}
None => None,
_ => unreachable!()
};
ensure!(self.bindings.insert(
raw_binding.qualified,
Binding::Prop(
getter_met_binding,
setter_met_binding
)
).is_none());
}
}
ensure!(self.bindings.insert(
unqualified,
Binding::Prop(getter_binding, setter_binding)
).is_none());
} else {
let binding = self.bind_prop_like(&stacked[0]);
ensure!(self.bindings.insert(unqualified, binding.clone()).is_none());
ensure!(self.bindings.insert(qualified, binding).is_none());
}
}
}
}
Ok(self.into_class())
}
fn bind_field_like(&mut self, raw_binding: &RawBinding) -> GResult<Binding> {
let state_i = raw_binding.state_i;
match raw_binding.bindee {
RawBindee::Field => {
ensure!(self.field_count < u16::MAX, "class has too many fields");
self.field_count += 1;
Ok(Binding::SimpleField(state_i, self.field_count as u16 - 1))
}
RawBindee::Const(ref slot) => {
Ok(Binding::SimpleConst(state_i, slot.clone()))
}
_ => unreachable!()
}
}
fn bind_met_like(&mut self, raw_binding: &RawBinding) -> Binding {
let state_i = raw_binding.state_i;
let (gfn, rni) = match raw_binding.bindee {
RawBindee::Met(ref gfn) => (gfn.clone(), false),
RawBindee::Wrap(_, ref gfn) => (gfn.clone(), true),
RawBindee::WildcardWrap(ref gfn) => (gfn.clone(), true),
_ => unreachable!()
};
Binding::Met(MetBinding::Simple(state_i, gfn.clone(), rni))
}
fn bind_prop_like(&mut self, raw_binding: &RawBinding) -> Binding {
let state_i = raw_binding.state_i;
let (_qualified_get, _qualified_set, getter_gfn, setter_gfn, rni) = {
match raw_binding.bindee {
RawBindee::Prop(qg, qs, ref get, ref set) => {
(qg, qs, get.clone(), set.clone(), false)
}
RawBindee::WrapProp(qg, qs, _, ref get, ref set) => {
(qg, qs, get.clone(), set.clone(), true)
}
RawBindee::WildcardWrapProp(qg, qs, ref get, ref set) => {
(qg, qs, get.clone(), set.clone(), true)
}
_ => unreachable!()
}
};
let getter_binding = getter_gfn.as_ref().map(|getter_gfn| {
MetBinding::Simple(state_i, getter_gfn.clone(), rni)
});
let setter_binding = setter_gfn.as_ref().map(|setter_gfn| {
MetBinding::Simple(state_i, setter_gfn.clone(), rni)
});
Binding::Prop(getter_binding, setter_binding)
}
fn stack_mets(
&mut self,
raw_bindings: &VecDeque<RawBinding>
) -> GResult<Vec<RawBinding>> {
let mut src = Vec::from_iter(raw_bindings.iter().cloned());
let mut dst = Vec::<RawBinding>::with_capacity(src.len());
let mut filtered_src = Vec::with_capacity(src.len());
for rb in src {
match rb.bindee {
RawBindee::Met(_) | RawBindee::Prop(..) => dst.push(rb),
_ => filtered_src.push(rb)
}
}
src = filtered_src;
for i in 0 .. dst.len() {
for j in i + 1 .. dst.len() {
let state_i = dst[i].state_i;
let state_j = dst[j].state_i;
ensure!(state_i != state_j, "duplicate `met` form {}", dst[i].qualified);
self.states.get_mut(&dst[i].state_name)
.unwrap().excludes |= 1 << (state_j as u32);
self.states.get_mut(&dst[j].state_name)
.unwrap().excludes |= 1 << (state_i as u32);
}
}
let mut dst_i = 0;
while dst_i < dst.len() {
let dst_qualified = dst[dst_i].qualified;
let dst_state = dst[dst_i].state_name;
let dst_state_i = dst[dst_i].state_i;
let starting_len = dst.len();
let mut filtered_src = Vec::with_capacity(src.len());
for src_rb in src {
match src_rb.bindee {
RawBindee::Met(_) | RawBindee::Prop(..) => unreachable!(),
RawBindee::Wrap(target_qualified, _) |
RawBindee::WrapProp(_, _, target_qualified, _, _) => {
if target_qualified == dst_qualified {
let src_state = src_rb.state_name;
let src_state_i = src_rb.state_i;
if src_state_i != dst_state_i {
self.states.get_mut(&src_state)
.unwrap().requires |= 1 << (dst_state_i as u32);
self.states.get_mut(&dst_state)
.unwrap().required_by |= 1 << (src_state_i as u32);
}
dst.push(src_rb);
} else {
filtered_src.push(src_rb);
}
}
RawBindee::WildcardWrap(_) | RawBindee::WildcardWrapProp(..) => {
filtered_src.push(src_rb);
}
_ => unreachable!()
}
}
src = filtered_src;
for i in starting_len .. dst.len() {
for j in i + 1 .. dst.len() {
let state_i = dst[i].state_i;
let state_j = dst[j].state_i;
ensure!(state_i != state_j, "duplicate `wrap` form {}", dst[i].qualified);
self.states.get_mut(&dst[i].state_name)
.unwrap().excludes |= 1 << (state_j as u32);
self.states.get_mut(&dst[j].state_name)
.unwrap().excludes |= 1 << (state_i as u32);
}
}
dst_i += 1;
}
for rb in &src {
match rb.bindee {
RawBindee::Met(_) | RawBindee::Prop(..) => unreachable!(),
RawBindee::Wrap(target, _) => {
bail!("the wrapped method {} is nonexistent or cyclical", target)
}
RawBindee::WrapProp(_, _, target, _, _) => {
bail!("the wrapped property {} is nonexistent or cyclical", target)
}
RawBindee::WildcardWrap(_) | RawBindee::WildcardWrapProp(..) => (),
_ => unreachable!()
}
}
dst.extend(src.into_iter());
Ok(dst)
}
fn evaluate_consts(&mut self) -> GResult<()> {
let const_tab = glsp::tab();
const_tab.set(CLASS_NAME_SYM, match self.raw_class.name {
Some(class_name) => Val::Sym(class_name),
None => Val::Nil
})?;
self.evaluate_consts_for_state(MAIN_SYM, const_tab)
}
fn evaluate_consts_for_state(
&mut self,
state_name: Sym,
parent_consts: Root<Tab>
) -> GResult<()> {
let own_consts = parent_consts.shallow_clone();
own_consts.set(STATE_NAME_SYM, state_name)?;
let mut i = 0;
while i < self.raw_class.bindings.len() {
if matches!(self.raw_class.bindings[i].bindee, RawBindee::PreConst(_)) &&
self.raw_class.bindings[i].state_name == state_name {
let mut j = i;
while j < self.raw_class.bindings.len() &&
matches!(self.raw_class.bindings[j].bindee, RawBindee::PreConst(None)) {
j += 1;
}
assert!(j < self.raw_class.bindings.len());
let gfn = match self.raw_class.bindings[j].bindee {
RawBindee::PreConst(Some(ref gfn)) => gfn.clone(),
_ => unreachable!()
};
let results: Root<Arr> = if gfn.min_args() == 0 {
glsp::call(&gfn, &())?
} else {
let const_tab = own_consts.shallow_clone();
const_tab.freeze();
glsp::call(&gfn, &[const_tab])?
};
assert!(results.len() == (j - i) + 1);
for n in i ..= j {
let result: Val = results.get(n - i)?;
let raw_binding = &mut self.raw_class.bindings[n];
assert!(raw_binding.state_name == state_name);
own_consts.set(raw_binding.unqualified, &result)?;
own_consts.set(raw_binding.qualified, &result)?;
raw_binding.bindee = RawBindee::Const(Slot::from_val(&result));
}
i = j + 1;
} else {
i += 1;
}
}
for child in self.states[&state_name].children.clone() {
self.evaluate_consts_for_state(child, own_consts.clone())?;
}
Ok(())
}
fn into_class(self) -> Class {
Class {
header: Header::new(),
name: self.raw_class.name,
is_mixin: self.raw_class.is_mixin,
bindings: self.bindings,
field_count: self.field_count as usize,
field_stack: self.field_stack,
met_stack: self.met_stack,
is: self.is,
states: self.states,
raw_class: if self.raw_class.is_mixin {
Some(self.raw_class)
} else {
None
}
}
}
}
impl State {
fn from_tab(tab: &Tab, state_i: u8) -> GResult<State> {
let state_name: Sym = tab.get(NAME_SYM)?;
let enabled_by_default: bool = tab.get(ENABLED_BY_DEFAULTP_SYM)?;
let parent: Option<Sym> = tab.get_if_present(PARENT_SYM)?;
let children: Vec<Sym> = tab.get(CHILDREN_SYM)?;
let fsm_siblings: Vec<Sym> = tab.get(FSM_SIBLINGS_SYM)?;
Ok(State {
index: state_i,
name: state_name,
enabled_by_default,
parent,
children,
fsm_siblings,
requires: 0,
required_by: 0,
excludes: 0,
init: None,
finis: Vec::new()
})
}
}