use std::cell::RefCell;
use boa_ast::scope::Scope;
use boa_gc::{Finalize, GcRefCell, Trace};
use crate::{JsString, JsValue, module::Module};
#[derive(Debug, Clone)]
enum BindingAccessor {
Identifier(JsString),
Index(u32),
}
#[derive(Clone, Debug, Trace, Finalize)]
struct IndirectBinding {
module: Module,
#[unsafe_ignore_trace]
accessor: RefCell<BindingAccessor>,
}
#[derive(Clone, Debug, Trace, Finalize)]
enum BindingType {
Direct(Option<JsValue>),
Indirect(IndirectBinding),
}
#[derive(Debug, Trace, Finalize)]
pub(crate) struct ModuleEnvironment {
bindings: GcRefCell<Vec<BindingType>>,
#[unsafe_ignore_trace]
compile: Scope,
}
impl ModuleEnvironment {
pub(crate) fn new(bindings: u32, compile: Scope) -> Self {
Self {
bindings: GcRefCell::new(vec![BindingType::Direct(None); bindings as usize]),
compile,
}
}
pub(crate) const fn compile(&self) -> &Scope {
&self.compile
}
#[track_caller]
pub(crate) fn get(&self, index: u32) -> Option<JsValue> {
let bindings = self.bindings.borrow();
match &bindings[index as usize] {
BindingType::Direct(v) => v.clone(),
BindingType::Indirect(IndirectBinding { module, accessor }) => {
let env = module.environment()?;
match &*accessor.clone().borrow() {
BindingAccessor::Identifier(name) => {
let index = env
.kind()
.as_module()
.expect("must be module environment")
.compile()
.get_binding(name)
.expect("linking must ensure the binding exists");
let value = env.get(index.binding_index())?;
*accessor.borrow_mut() = BindingAccessor::Index(index.binding_index());
Some(value)
}
BindingAccessor::Index(index) => env.get(*index),
}
}
}
}
#[track_caller]
pub(crate) fn set(&self, index: u32, value: JsValue) {
let mut bindings = self.bindings.borrow_mut();
match &mut bindings[index as usize] {
BindingType::Direct(v) => *v = Some(value),
BindingType::Indirect(_) => {
panic!("cannot modify indirect references to other environments")
}
}
}
#[track_caller]
pub(crate) fn set_indirect(&self, index: u32, target_module: Module, target_binding: JsString) {
let mut bindings = self.bindings.borrow_mut();
bindings[index as usize] = BindingType::Indirect(IndirectBinding {
module: target_module,
accessor: RefCell::new(BindingAccessor::Identifier(target_binding)),
});
}
}