use std::{cell::RefCell, rc::Rc};
use crate::{environments::runtime::BindingLocator, JsString};
use boa_gc::{empty_trace, Finalize, Trace};
use rustc_hash::FxHashMap;
use super::runtime::BindingLocatorError;
#[derive(Debug)]
struct CompileTimeBinding {
index: u32,
mutable: bool,
lex: bool,
strict: bool,
}
#[derive(Debug, Finalize)]
pub(crate) struct CompileTimeEnvironment {
outer: Option<Rc<Self>>,
environment_index: u32,
bindings: RefCell<FxHashMap<JsString, CompileTimeBinding>>,
function_scope: bool,
}
unsafe impl Trace for CompileTimeEnvironment {
empty_trace!();
}
impl CompileTimeEnvironment {
pub(crate) fn new_global() -> Self {
Self {
outer: None,
environment_index: 0,
bindings: RefCell::default(),
function_scope: true,
}
}
pub(crate) fn new(parent: Rc<Self>, function_scope: bool) -> Self {
let index = parent.environment_index + 1;
Self {
outer: Some(parent),
environment_index: index,
bindings: RefCell::default(),
function_scope,
}
}
pub(crate) fn has_lex_binding(&self, name: &JsString) -> bool {
self.bindings
.borrow()
.get(name)
.map_or(false, |binding| binding.lex)
}
pub(crate) fn has_binding(&self, name: &JsString) -> bool {
self.bindings.borrow().contains_key(name)
}
pub(crate) fn get_identifier_reference(&self, name: JsString) -> IdentifierReference {
if let Some(binding) = self.bindings.borrow().get(&name) {
IdentifierReference::new(
BindingLocator::declarative(name, self.environment_index, binding.index),
binding.lex,
)
} else if let Some(outer) = &self.outer {
outer.get_identifier_reference(name)
} else {
IdentifierReference::new(BindingLocator::global(name), false)
}
}
pub(crate) fn num_bindings(&self) -> u32 {
self.bindings.borrow().len() as u32
}
pub(crate) fn environment_index(&self) -> u32 {
self.environment_index
}
pub(crate) const fn is_function(&self) -> bool {
self.function_scope
}
pub(crate) const fn is_global(&self) -> bool {
self.outer.is_none()
}
pub(crate) fn get_binding(&self, name: &JsString) -> Option<BindingLocator> {
self.bindings.borrow().get(name).map(|binding| {
BindingLocator::declarative(name.clone(), self.environment_index, binding.index)
})
}
pub(crate) fn create_mutable_binding(
&self,
name: JsString,
function_scope: bool,
) -> BindingLocator {
let binding_index = self.bindings.borrow().len() as u32;
self.bindings.borrow_mut().insert(
name.clone(),
CompileTimeBinding {
index: binding_index,
mutable: true,
lex: !function_scope,
strict: false,
},
);
BindingLocator::declarative(name, self.environment_index, binding_index)
}
pub(crate) fn create_immutable_binding(&self, name: JsString, strict: bool) -> BindingLocator {
let binding_index = self.bindings.borrow().len() as u32;
self.bindings.borrow_mut().insert(
name.clone(),
CompileTimeBinding {
index: binding_index,
mutable: false,
lex: true,
strict,
},
);
BindingLocator::declarative(name, self.environment_index, binding_index)
}
pub(crate) fn set_mutable_binding(
&self,
name: JsString,
) -> Result<BindingLocator, BindingLocatorError> {
Ok(match self.bindings.borrow().get(&name) {
Some(binding) if binding.mutable => {
BindingLocator::declarative(name, self.environment_index, binding.index)
}
Some(binding) if binding.strict => return Err(BindingLocatorError::MutateImmutable),
Some(_) => return Err(BindingLocatorError::Silent),
None => self.outer.as_ref().map_or_else(
|| Ok(BindingLocator::global(name.clone())),
|outer| outer.set_mutable_binding(name.clone()),
)?,
})
}
#[cfg(feature = "annex-b")]
pub(crate) fn set_mutable_binding_var(
&self,
name: JsString,
) -> Result<BindingLocator, BindingLocatorError> {
if !self.is_function() {
return self.outer.as_ref().map_or_else(
|| Ok(BindingLocator::global(name.clone())),
|outer| outer.set_mutable_binding_var(name.clone()),
);
}
Ok(match self.bindings.borrow().get(&name) {
Some(binding) if binding.mutable => {
BindingLocator::declarative(name, self.environment_index, binding.index)
}
Some(binding) if binding.strict => return Err(BindingLocatorError::MutateImmutable),
Some(_) => return Err(BindingLocatorError::Silent),
None => self.outer.as_ref().map_or_else(
|| Ok(BindingLocator::global(name.clone())),
|outer| outer.set_mutable_binding_var(name.clone()),
)?,
})
}
pub(crate) fn outer(&self) -> Option<Rc<Self>> {
self.outer.clone()
}
}
pub(crate) struct IdentifierReference {
locator: BindingLocator,
lexical: bool,
}
impl IdentifierReference {
pub(crate) fn new(locator: BindingLocator, lexical: bool) -> Self {
Self { locator, lexical }
}
pub(crate) fn locator(&self) -> BindingLocator {
self.locator.clone()
}
pub(crate) fn is_lexical(&self) -> bool {
self.lexical
}
}