use crate::{
environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue,
};
use boa_gc::{Cell, Finalize, Gc, Trace};
use boa_interner::Sym;
use rustc_hash::FxHashMap;
#[derive(Debug)]
struct CompileTimeBinding {
index: usize,
mutable: bool,
lex: bool,
}
#[derive(Debug, Finalize, Trace)]
pub(crate) struct CompileTimeEnvironment {
outer: Option<Gc<Cell<Self>>>,
environment_index: usize,
#[unsafe_ignore_trace]
bindings: FxHashMap<Sym, CompileTimeBinding>,
function_scope: bool,
}
impl CompileTimeEnvironment {
#[inline]
pub(crate) fn new_global() -> Self {
Self {
outer: None,
environment_index: 0,
bindings: FxHashMap::default(),
function_scope: true,
}
}
#[inline]
pub(crate) fn has_lex_binding(&self, name: Sym) -> bool {
self.bindings
.get(&name)
.map_or(false, |binding| binding.lex)
}
#[inline]
pub(crate) fn num_bindings(&self) -> usize {
self.bindings.len()
}
#[inline]
pub(crate) fn is_function(&self) -> bool {
self.function_scope
}
#[inline]
pub(crate) fn get_binding(&self, name: Sym) -> Option<BindingLocator> {
self.bindings
.get(&name)
.map(|binding| BindingLocator::declarative(name, self.environment_index, binding.index))
}
#[inline]
pub(crate) fn get_binding_recursive(&self, name: Sym) -> BindingLocator {
if let Some(binding) = self.bindings.get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else if let Some(outer) = &self.outer {
outer.borrow().get_binding_recursive(name)
} else {
BindingLocator::global(name)
}
}
#[inline]
pub(crate) fn has_binding_recursive(&self, name: Sym) -> bool {
if self.bindings.contains_key(&name) {
true
} else if let Some(outer) = &self.outer {
outer.borrow().has_binding_recursive(name)
} else {
false
}
}
#[inline]
pub(crate) fn create_mutable_binding(&mut self, name: Sym, function_scope: bool) -> bool {
if let Some(outer) = &self.outer {
if !function_scope || self.function_scope {
if !self.bindings.contains_key(&name) {
let binding_index = self.bindings.len();
self.bindings.insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: true,
lex: !function_scope,
},
);
}
true
} else {
return outer
.borrow_mut()
.create_mutable_binding(name, function_scope);
}
} else if function_scope {
false
} else {
if !self.bindings.contains_key(&name) {
let binding_index = self.bindings.len();
self.bindings.insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: true,
lex: !function_scope,
},
);
}
true
}
}
#[inline]
pub(crate) fn create_immutable_binding(&mut self, name: Sym) {
let binding_index = self.bindings.len();
self.bindings.insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: false,
lex: true,
},
);
}
#[inline]
pub(crate) fn initialize_mutable_binding(
&self,
name: Sym,
function_scope: bool,
) -> BindingLocator {
if let Some(outer) = &self.outer {
if function_scope && !self.function_scope {
return outer
.borrow()
.initialize_mutable_binding(name, function_scope);
}
if let Some(binding) = self.bindings.get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else {
outer
.borrow()
.initialize_mutable_binding(name, function_scope)
}
} else if let Some(binding) = self.bindings.get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else {
BindingLocator::global(name)
}
}
#[inline]
pub(crate) fn initialize_immutable_binding(&self, name: Sym) -> BindingLocator {
let binding = self.bindings.get(&name).expect("binding must exist");
BindingLocator::declarative(name, self.environment_index, binding.index)
}
#[inline]
pub(crate) fn set_mutable_binding_recursive(&self, name: Sym) -> BindingLocator {
match self.bindings.get(&name) {
Some(binding) if binding.mutable => {
BindingLocator::declarative(name, self.environment_index, binding.index)
}
Some(_) => BindingLocator::mutate_immutable(name),
None => {
if let Some(outer) = &self.outer {
outer.borrow().set_mutable_binding_recursive(name)
} else {
BindingLocator::global(name)
}
}
}
}
}
impl Context {
#[inline]
pub(crate) fn push_compile_time_environment(&mut self, function_scope: bool) {
let environment_index = self.realm.compile_env.borrow().environment_index + 1;
let outer = self.realm.compile_env.clone();
self.realm.compile_env = Gc::new(Cell::new(CompileTimeEnvironment {
outer: Some(outer),
environment_index,
bindings: FxHashMap::default(),
function_scope,
}));
}
#[inline]
pub(crate) fn pop_compile_time_environment(
&mut self,
) -> (usize, Gc<Cell<CompileTimeEnvironment>>) {
let current_env_borrow = self.realm.compile_env.borrow();
if let Some(outer) = ¤t_env_borrow.outer {
let outer_clone = outer.clone();
let num_bindings = current_env_borrow.num_bindings();
drop(current_env_borrow);
let current = self.realm.compile_env.clone();
self.realm.compile_env = outer_clone;
(num_bindings, current)
} else {
panic!("cannot pop global environment")
}
}
#[inline]
pub(crate) fn get_binding_number(&self) -> usize {
self.realm.compile_env.borrow().num_bindings()
}
#[inline]
pub(crate) fn get_binding_value(&self, name: Sym) -> BindingLocator {
self.realm.compile_env.borrow().get_binding_recursive(name)
}
#[inline]
pub(crate) fn has_binding(&self, name: Sym) -> bool {
self.realm.compile_env.borrow().has_binding_recursive(name)
}
#[inline]
pub(crate) fn create_mutable_binding(&mut self, name: Sym, function_scope: bool) {
if !self
.realm
.compile_env
.borrow_mut()
.create_mutable_binding(name, function_scope)
{
let name_str = JsString::from(self.interner().resolve_expect(name));
let desc = self
.realm
.global_property_map
.string_property_map()
.get(&name_str);
if desc.is_none() {
self.global_bindings_mut().insert(
name_str,
PropertyDescriptor::builder()
.value(JsValue::Undefined)
.writable(true)
.enumerable(true)
.configurable(true)
.build(),
);
}
}
}
#[inline]
pub(crate) fn initialize_mutable_binding(
&self,
name: Sym,
function_scope: bool,
) -> BindingLocator {
self.realm
.compile_env
.borrow()
.initialize_mutable_binding(name, function_scope)
}
#[inline]
pub(crate) fn create_immutable_binding(&mut self, name: Sym) {
self.realm
.compile_env
.borrow_mut()
.create_immutable_binding(name);
}
#[inline]
pub(crate) fn initialize_immutable_binding(&self, name: Sym) -> BindingLocator {
self.realm
.compile_env
.borrow()
.initialize_immutable_binding(name)
}
#[inline]
pub(crate) fn set_mutable_binding(&self, name: Sym) -> BindingLocator {
self.realm
.compile_env
.borrow()
.set_mutable_binding_recursive(name)
}
}