mod function;
mod global;
mod lexical;
mod module;
use std::{
cell::{Cell, RefCell},
rc::Rc,
};
use boa_gc::{Finalize, GcRefCell, Trace};
pub(crate) use function::{FunctionEnvironment, FunctionSlots, ThisBindingStatus};
pub(crate) use global::GlobalEnvironment;
pub(crate) use lexical::LexicalEnvironment;
pub(crate) use module::ModuleEnvironment;
use crate::{environments::CompileTimeEnvironment, JsObject, JsResult, JsValue};
#[derive(Debug, Trace, Finalize)]
pub(crate) struct DeclarativeEnvironment {
kind: DeclarativeEnvironmentKind,
#[unsafe_ignore_trace]
compile: Rc<RefCell<CompileTimeEnvironment>>,
}
impl DeclarativeEnvironment {
pub(crate) fn global(global_this: JsObject) -> Self {
Self {
kind: DeclarativeEnvironmentKind::Global(GlobalEnvironment::new(global_this)),
compile: Rc::new(RefCell::new(CompileTimeEnvironment::new_global())),
}
}
pub(crate) fn new(
kind: DeclarativeEnvironmentKind,
compile: Rc<RefCell<CompileTimeEnvironment>>,
) -> Self {
Self { kind, compile }
}
pub(crate) fn compile_env(&self) -> Rc<RefCell<CompileTimeEnvironment>> {
self.compile.clone()
}
pub(crate) const fn kind(&self) -> &DeclarativeEnvironmentKind {
&self.kind
}
#[track_caller]
pub(crate) fn get(&self, index: u32) -> Option<JsValue> {
self.kind.get(index)
}
#[track_caller]
pub(crate) fn set(&self, index: u32, value: JsValue) {
self.kind.set(index, value);
}
pub(crate) fn get_this_binding(&self) -> JsResult<Option<JsValue>> {
self.kind.get_this_binding()
}
pub(crate) fn has_this_binding(&self) -> bool {
self.kind.has_this_binding()
}
pub(crate) fn poisoned(&self) -> bool {
self.kind.poisoned()
}
pub(crate) fn with(&self) -> bool {
self.kind.with()
}
pub(crate) fn poison(&self) {
self.kind.poison();
}
}
#[derive(Debug, Trace, Finalize)]
pub(crate) enum DeclarativeEnvironmentKind {
Lexical(LexicalEnvironment),
Global(GlobalEnvironment),
Function(FunctionEnvironment),
Module(ModuleEnvironment),
}
impl DeclarativeEnvironmentKind {
pub(crate) const fn as_function(&self) -> Option<&FunctionEnvironment> {
if let Self::Function(fun) = &self {
Some(fun)
} else {
None
}
}
pub(crate) const fn as_global(&self) -> Option<&GlobalEnvironment> {
if let Self::Global(fun) = &self {
Some(fun)
} else {
None
}
}
pub(crate) const fn as_module(&self) -> Option<&ModuleEnvironment> {
if let Self::Module(module) = &self {
Some(module)
} else {
None
}
}
#[track_caller]
pub(crate) fn get(&self, index: u32) -> Option<JsValue> {
match self {
Self::Lexical(inner) => inner.get(index),
Self::Global(inner) => inner.get(index),
Self::Function(inner) => inner.get(index),
Self::Module(inner) => inner.get(index),
}
}
#[track_caller]
pub(crate) fn set(&self, index: u32, value: JsValue) {
match self {
Self::Lexical(inner) => inner.set(index, value),
Self::Global(inner) => inner.set(index, value),
Self::Function(inner) => inner.set(index, value),
Self::Module(inner) => inner.set(index, value),
}
}
pub(crate) fn get_this_binding(&self) -> JsResult<Option<JsValue>> {
match self {
Self::Lexical(_) => Ok(None),
Self::Global(g) => Ok(Some(g.get_this_binding().into())),
Self::Function(f) => f.get_this_binding(),
Self::Module(_) => Ok(Some(JsValue::undefined())),
}
}
pub(crate) fn has_this_binding(&self) -> bool {
match self {
Self::Lexical(_) => false,
Self::Function(f) => f.has_this_binding(),
Self::Global(_) | Self::Module(_) => true,
}
}
pub(crate) fn poisoned(&self) -> bool {
match self {
Self::Lexical(lex) => lex.poisonable_environment().poisoned(),
Self::Global(g) => g.poisonable_environment().poisoned(),
Self::Function(f) => f.poisonable_environment().poisoned(),
Self::Module(_) => false,
}
}
pub(crate) fn with(&self) -> bool {
match self {
Self::Lexical(lex) => lex.poisonable_environment().with(),
Self::Global(g) => g.poisonable_environment().with(),
Self::Function(f) => f.poisonable_environment().with(),
Self::Module(_) => false,
}
}
pub(crate) fn poison(&self) {
match self {
Self::Lexical(lex) => lex.poisonable_environment().poison(),
Self::Global(g) => g.poisonable_environment().poison(),
Self::Function(f) => f.poisonable_environment().poison(),
Self::Module(_) => {
unreachable!("modules are always run in strict mode")
}
}
}
}
#[derive(Debug, Trace, Finalize)]
pub(crate) struct PoisonableEnvironment {
bindings: GcRefCell<Vec<Option<JsValue>>>,
#[unsafe_ignore_trace]
poisoned: Cell<bool>,
#[unsafe_ignore_trace]
with: Cell<bool>,
}
impl PoisonableEnvironment {
pub(crate) fn new(bindings_count: u32, poisoned: bool, with: bool) -> Self {
Self {
bindings: GcRefCell::new(vec![None; bindings_count as usize]),
poisoned: Cell::new(poisoned),
with: Cell::new(with),
}
}
pub(crate) const fn bindings(&self) -> &GcRefCell<Vec<Option<JsValue>>> {
&self.bindings
}
#[track_caller]
fn get(&self, index: u32) -> Option<JsValue> {
self.bindings.borrow()[index as usize].clone()
}
#[track_caller]
pub(crate) fn set(&self, index: u32, value: JsValue) {
self.bindings.borrow_mut()[index as usize] = Some(value);
}
fn poisoned(&self) -> bool {
self.poisoned.get()
}
fn with(&self) -> bool {
self.with.get()
}
fn poison(&self) {
self.poisoned.set(true);
}
}