use core::marker::PhantomData;
use crate::{
ecmascript::Agent,
engine::{HeapRootData, HeapRootRef, NoGcScope, Rootable},
};
#[derive(Debug, PartialEq)]
#[allow(private_bounds)]
pub struct Global<T: 'static + Rootable>(T::RootRepr, PhantomData<T>);
#[allow(private_bounds)]
impl<T: Rootable> Global<T> {
#[must_use]
pub fn new(agent: &Agent, value: T) -> Self {
let heap_root_data = match T::to_root_repr(value) {
Ok(stack_repr) => {
return Self(stack_repr, PhantomData);
}
Err(heap_data) => heap_data,
};
let value = heap_root_data;
let mut globals = agent.heap.globals.borrow_mut();
let reused_index = globals.iter_mut().enumerate().find_map(|(index, entry)| {
if entry == &HeapRootData::Empty {
*entry = value;
Some(index)
} else {
None
}
});
let heap_ref = if let Some(reused_index) = reused_index {
HeapRootRef::from_index(reused_index)
} else {
let next_index = globals.len();
globals.push(value);
HeapRootRef::from_index(next_index)
};
Self(T::from_heap_ref(heap_ref), Default::default())
}
pub fn take(self, agent: &Agent) -> T {
let heap_ref = match T::from_root_repr(&self.0) {
Ok(value) => {
return value;
}
Err(heap_ref) => heap_ref,
};
let heap_data = core::mem::replace(
agent
.heap
.globals
.borrow_mut()
.get_mut(heap_ref.to_index())
.unwrap(),
HeapRootData::Empty,
);
let Some(value) = T::from_heap_data(heap_data) else {
panic!("Invalid Global returned different type than expected");
};
value
}
pub fn get(&self, agent: &Agent, _: NoGcScope) -> T {
let heap_ref = match T::from_root_repr(&self.0) {
Ok(value) => {
return value;
}
Err(heap_ref) => heap_ref,
};
let heap_data = *agent
.heap
.globals
.borrow()
.get(heap_ref.to_index())
.unwrap();
let Some(value) = T::from_heap_data(heap_data) else {
panic!("Invalid Global returned different type than expected");
};
value
}
#[must_use]
pub fn clone(&self, agent: &Agent, gc: NoGcScope) -> Self {
let value = self.get(agent, gc);
Self::new(agent, value)
}
}