use core::cell::Cell;
use core::ptr;
use bun_alloc::Arena;
use bun_alloc::ast_alloc::{self, AstAllocState};
use crate::expr;
use crate::stmt;
#[thread_local]
static ARENA_POOL: Cell<Option<Arena>> = Cell::new(None);
#[inline]
fn take_pooled_arena() -> Arena {
ARENA_POOL.take().unwrap_or_default()
}
#[inline]
fn return_pooled_arena(arena: Arena) {
drop(ARENA_POOL.replace(Some(arena)));
}
pub struct ASTMemoryAllocator {
arena: Arena,
external_arena: *const Arena,
arena_dirty: bool,
ast_state: Option<Box<AstAllocState>>,
ast_pushed: bool,
previous: *mut ASTMemoryAllocator,
previous_logger: *const Arena,
previous_ast_state: Option<Box<AstAllocState>>,
}
impl Default for ASTMemoryAllocator {
fn default() -> Self {
Self {
arena: take_pooled_arena(),
external_arena: ptr::null(),
arena_dirty: false,
ast_state: None,
ast_pushed: false,
previous: ptr::null_mut(),
previous_logger: ptr::null(),
previous_ast_state: None,
}
}
}
impl Drop for ASTMemoryAllocator {
fn drop(&mut self) {
debug_assert!(
!self.ast_pushed,
"ASTMemoryAllocator dropped while its AstAllocState is still installed"
);
if let Some(state) = self.ast_state.take() {
ast_alloc::release_state(state);
}
if !self.external_arena.is_null() {
return;
}
if self.arena_dirty {
self.arena.reset();
}
let arena = core::mem::replace(&mut self.arena, Arena::borrowing_default());
return_pooled_arena(arena);
}
}
impl ASTMemoryAllocator {
pub fn new(_fallback: &Arena) -> Self {
Self::default()
}
pub fn borrowing(arena: &Arena) -> Self {
Self {
arena: Arena::borrowing_default(),
external_arena: ptr::from_ref(arena),
arena_dirty: false,
ast_state: None,
ast_pushed: false,
previous: ptr::null_mut(),
previous_logger: ptr::null(),
previous_ast_state: None,
}
}
pub fn new_without_stack(_fallback: &Arena) -> Self {
Self::default()
}
#[inline]
fn arena(&self) -> &Arena {
if self.external_arena.is_null() {
&self.arena
} else {
unsafe { &*self.external_arena }
}
}
#[inline]
fn arena_raw(&self) -> *const Arena {
if self.external_arena.is_null() {
&raw const self.arena
} else {
self.external_arena
}
}
fn reset_ast_state(&mut self) {
if let Some(state) = self.ast_state.as_deref_mut() {
state.reset();
} else if self.ast_pushed {
debug_assert!(
self.ast_state_is_active(),
"ASTMemoryAllocator::reset while another AstAllocState is installed"
);
ast_alloc::reset_active_state();
}
}
pub fn release_ast_state(&mut self) {
debug_assert!(
!self.ast_pushed,
"release_ast_state while the AstAllocState is still installed"
);
if self.ast_pushed {
return;
}
if let Some(state) = self.ast_state.take() {
ast_alloc::release_state(state);
}
}
pub fn take_ast_state(&mut self) -> Option<Box<AstAllocState>> {
debug_assert!(
!self.ast_pushed,
"take_ast_state while the AstAllocState is still installed"
);
if self.ast_pushed {
return None;
}
self.ast_state.take()
}
fn ast_state_is_active(&self) -> bool {
!ast_alloc::active_state_id().is_null()
}
pub fn enter(&mut self) -> Scope<'_> {
if self.arena_dirty {
self.reset_ast_state();
if self.external_arena.is_null() {
self.arena.reset();
}
}
self.arena_dirty = true;
self.previous = ptr::null_mut();
let mut ast_scope = Scope {
current: Some(self),
previous: Some(stmt::data::Store::memory_allocator()),
previous_logger: ptr::null(),
previous_ast_state: None,
entered: false,
};
ast_scope.enter();
ast_scope
}
pub fn reset(&mut self) {
if self.arena_dirty {
self.reset_ast_state();
if self.external_arena.is_null() {
self.arena.reset();
}
self.arena_dirty = false;
}
}
pub fn reset_retain_with_limit(&mut self, limit: usize) {
if self.arena_dirty {
debug_assert!(
self.external_arena.is_null(),
"reset_retain_with_limit on a borrowing ASTMemoryAllocator"
);
if !self.arena.reset_retain_with_limit(limit) {
self.reset_ast_state();
}
self.arena_dirty = false;
}
}
pub fn push(&mut self) {
self.arena_dirty = true;
let arena: *const Arena = self.arena_raw();
stmt::data::Store::set_memory_allocator(std::ptr::from_mut::<Self>(self));
expr::data::Store::set_memory_allocator(std::ptr::from_mut::<Self>(self));
let spill = self.arena().heap_ptr();
if !self.ast_pushed {
self.previous_logger = crate::data_store_override();
crate::set_data_store_override(arena);
let mut state = self
.ast_state
.take()
.unwrap_or_else(ast_alloc::acquire_state);
state.set_spill_heap(spill);
self.previous_ast_state = ast_alloc::swap_state(Some(state));
self.ast_pushed = true;
} else {
crate::set_data_store_override(arena);
ast_alloc::set_active_spill_heap(spill);
}
}
pub fn pop(&mut self) {
let prev = self.previous;
debug_assert!(prev != std::ptr::from_mut::<Self>(self));
stmt::data::Store::set_memory_allocator(prev);
expr::data::Store::set_memory_allocator(prev);
crate::set_data_store_override(self.previous_logger);
if self.ast_pushed {
self.ast_state = ast_alloc::swap_state(self.previous_ast_state.take());
self.ast_pushed = false;
debug_assert!(
self.ast_state.is_some(),
"ASTMemoryAllocator::pop: the pushed AstAllocState was uninstalled by someone else"
);
}
self.previous = ptr::null_mut();
self.previous_logger = ptr::null();
}
#[inline]
pub fn append<T>(&self, value: T) -> crate::StoreRef<T> {
crate::StoreRef::from_bump(self.arena().alloc(value))
}
#[inline]
pub fn stack_allocator(&self) -> &Arena {
self.arena()
}
#[inline]
pub fn bump_allocator(&self) -> &Arena {
self.arena()
}
pub fn init_without_stack(&mut self) {
self.arena = Arena::new();
self.external_arena = ptr::null();
self.arena_dirty = false;
}
}
pub struct Scope<'a> {
current: Option<&'a mut ASTMemoryAllocator>,
previous: Option<*mut ASTMemoryAllocator>,
previous_logger: *const Arena,
previous_ast_state: Option<Box<AstAllocState>>,
entered: bool,
}
impl<'a> Default for Scope<'a> {
fn default() -> Self {
Self {
current: None,
previous: None,
previous_logger: ptr::null(),
previous_ast_state: None,
entered: false,
}
}
}
impl<'a> Scope<'a> {
pub fn enter(&mut self) {
debug_assert!(
expr::data::Store::memory_allocator() == stmt::data::Store::memory_allocator()
);
debug_assert!(!self.entered);
self.previous = Some(expr::data::Store::memory_allocator());
self.previous_logger = crate::data_store_override();
self.entered = true;
let (current, arena): (*mut ASTMemoryAllocator, *const Arena) = match &mut self.current {
Some(r) => {
debug_assert!(
!r.ast_pushed,
"ASTMemoryAllocator::enter while its AstAllocState is already installed (push without pop)"
);
let arena: *const Arena = r.arena_raw();
let mut state = r.ast_state.take().unwrap_or_else(ast_alloc::acquire_state);
state.set_spill_heap(r.arena().heap_ptr());
self.previous_ast_state = ast_alloc::swap_state(Some(state));
r.ast_pushed = true;
(std::ptr::from_mut::<ASTMemoryAllocator>(*r), arena)
}
None => {
self.previous_ast_state = ast_alloc::swap_state(None);
(ptr::null_mut(), ptr::null())
}
};
expr::data::Store::set_memory_allocator(current);
stmt::data::Store::set_memory_allocator(current);
crate::set_data_store_override(arena);
if current.is_null() {
stmt::data::Store::begin();
expr::data::Store::begin();
}
}
pub fn exit(&mut self) {
if !self.entered {
return;
}
self.entered = false;
let prev = self.previous.unwrap_or(ptr::null_mut());
expr::data::Store::set_memory_allocator(prev);
stmt::data::Store::set_memory_allocator(prev);
crate::set_data_store_override(self.previous_logger);
let installed = ast_alloc::swap_state(self.previous_ast_state.take());
match self.current.as_deref_mut() {
Some(r) => {
debug_assert!(
installed.is_some(),
"ASTMemoryAllocator::Scope::exit: the installed AstAllocState was taken by someone else"
);
r.ast_state = installed;
r.ast_pushed = false;
}
None => {
debug_assert!(installed.is_none());
}
}
}
}
impl<'a> Drop for Scope<'a> {
fn drop(&mut self) {
self.exit();
}
}