use oxc_allocator::{Allocator, Box as ArenaBox, Vec as ArenaVec};
use oxc_ast::{
AstBuilder,
ast::{Expression, IdentifierReference, Statement},
};
use oxc_semantic::Scoping;
use oxc_span::{Atom, Span};
use oxc_syntax::{
reference::{ReferenceFlags, ReferenceId},
scope::{ScopeFlags, ScopeId},
symbol::{SymbolFlags, SymbolId},
};
use crate::{
ancestor::{Ancestor, AncestorType},
ast_operations::{GatherNodeParts, get_var_name_from_node},
};
mod ancestry;
mod bound_identifier;
mod maybe_bound_identifier;
mod reusable;
mod scoping;
mod uid;
use ancestry::PopToken;
pub use ancestry::TraverseAncestry;
pub use bound_identifier::BoundIdentifier;
pub use maybe_bound_identifier::MaybeBoundIdentifier;
pub use reusable::ReusableTraverseCtx;
pub use scoping::TraverseScoping;
pub struct TraverseCtx<'a, State> {
pub state: State,
pub ancestry: TraverseAncestry<'a>,
pub scoping: TraverseScoping<'a>,
pub ast: AstBuilder<'a>,
}
impl<'a, State> TraverseCtx<'a, State> {
#[inline]
pub fn alloc<T>(&self, node: T) -> ArenaBox<'a, T> {
self.ast.alloc(node)
}
#[inline]
pub fn parent<'t>(&'t self) -> Ancestor<'a, 't> {
self.ancestry.parent()
}
#[inline]
pub fn ancestor<'t>(&'t self, level: usize) -> Ancestor<'a, 't> {
self.ancestry.ancestor(level)
}
#[inline]
pub fn ancestors<'t>(&'t self) -> impl Iterator<Item = Ancestor<'a, 't>> {
self.ancestry.ancestors()
}
#[inline]
pub fn ancestors_depth(&self) -> usize {
self.ancestry.ancestors_depth()
}
#[inline]
pub fn current_scope_id(&self) -> ScopeId {
self.scoping.current_scope_id()
}
#[inline]
pub fn current_hoist_scope_id(&self) -> ScopeId {
self.scoping.current_hoist_scope_id()
}
#[inline]
pub fn current_block_scope_id(&self) -> ScopeId {
self.scoping.current_block_scope_id()
}
#[inline]
pub fn current_scope_flags(&self) -> ScopeFlags {
self.scoping.current_scope_flags()
}
#[inline]
pub fn scoping(&self) -> &Scoping {
self.scoping.scoping()
}
#[inline]
pub fn scoping_mut(&mut self) -> &mut Scoping {
self.scoping.scoping_mut()
}
#[inline]
pub fn ancestor_scopes(&self) -> impl Iterator<Item = ScopeId> + '_ {
self.scoping.ancestor_scopes()
}
#[inline]
pub fn create_child_scope(&mut self, parent_id: ScopeId, flags: ScopeFlags) -> ScopeId {
self.scoping.create_child_scope(parent_id, flags)
}
#[inline]
pub fn create_child_scope_of_current(&mut self, flags: ScopeFlags) -> ScopeId {
self.scoping.create_child_scope_of_current(flags)
}
#[inline]
pub fn insert_scope_below_statement(&mut self, stmt: &Statement, flags: ScopeFlags) -> ScopeId {
self.scoping.insert_scope_below_statement(stmt, flags)
}
#[inline]
pub fn insert_scope_below_statement_from_scope_id(
&mut self,
stmt: &Statement,
scope_id: ScopeId,
flags: ScopeFlags,
) -> ScopeId {
self.scoping.insert_scope_below_statement_from_scope_id(stmt, scope_id, flags)
}
#[inline]
pub fn insert_scope_below_expression(
&mut self,
expr: &Expression,
flags: ScopeFlags,
) -> ScopeId {
self.scoping.insert_scope_below_expression(expr, flags)
}
pub fn insert_scope_below_statements(
&mut self,
stmts: &ArenaVec<Statement>,
flags: ScopeFlags,
) -> ScopeId {
self.scoping.insert_scope_below_statements(stmts, flags)
}
pub fn insert_scope_between(
&mut self,
parent_id: ScopeId,
child_id: ScopeId,
flags: ScopeFlags,
) -> ScopeId {
self.scoping.insert_scope_between(parent_id, child_id, flags)
}
pub fn remove_scope_for_expression(&mut self, scope_id: ScopeId, expr: &Expression) {
self.scoping.remove_scope_for_expression(scope_id, expr);
}
pub fn generate_binding(
&mut self,
name: Atom<'a>,
scope_id: ScopeId,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
self.scoping.generate_binding(name, scope_id, flags)
}
pub fn generate_binding_in_current_scope(
&mut self,
name: Atom<'a>,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
self.scoping.generate_binding_in_current_scope(name, flags)
}
pub fn generate_uid_name(&mut self, name: &str) -> Atom<'a> {
self.scoping.generate_uid_name(name, self.ast.allocator)
}
#[inline]
pub fn generate_uid(
&mut self,
name: &str,
scope_id: ScopeId,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
let name = self.generate_uid_name(name);
let symbol_id = self.scoping.add_binding(&name, scope_id, flags);
BoundIdentifier::new(name, symbol_id)
}
#[inline]
pub fn generate_uid_in_current_scope(
&mut self,
name: &str,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
self.generate_uid(name, self.current_scope_id(), flags)
}
#[inline]
pub fn generate_uid_in_root_scope(
&mut self,
name: &str,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
self.generate_uid(name, self.scoping().root_scope_id(), flags)
}
#[inline]
pub fn generate_uid_based_on_node<N: GatherNodeParts<'a>>(
&mut self,
node: &N,
scope_id: ScopeId,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
let name = get_var_name_from_node(node);
self.generate_uid(&name, scope_id, flags)
}
#[inline]
pub fn generate_uid_in_current_scope_based_on_node<N: GatherNodeParts<'a>>(
&mut self,
node: &N,
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
self.generate_uid_based_on_node(node, self.current_scope_id(), flags)
}
#[inline]
pub fn generate_uid_in_current_hoist_scope(&mut self, name: &str) -> BoundIdentifier<'a> {
self.generate_uid(name, self.current_hoist_scope_id(), SymbolFlags::FunctionScopedVariable)
}
#[inline]
pub fn generate_uid_in_current_hoist_scope_based_on_node<N: GatherNodeParts<'a>>(
&mut self,
node: &N,
) -> BoundIdentifier<'a> {
let name = get_var_name_from_node(node);
self.generate_uid_in_current_hoist_scope(&name)
}
#[inline]
pub fn create_bound_reference(
&mut self,
symbol_id: SymbolId,
flags: ReferenceFlags,
) -> ReferenceId {
self.scoping.create_bound_reference(symbol_id, flags)
}
pub fn create_bound_ident_reference(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: SymbolId,
flags: ReferenceFlags,
) -> IdentifierReference<'a> {
let reference_id = self.create_bound_reference(symbol_id, flags);
self.ast.identifier_reference_with_reference_id(span, name, reference_id)
}
pub fn create_bound_ident_expr(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: SymbolId,
flags: ReferenceFlags,
) -> Expression<'a> {
let ident = self.create_bound_ident_reference(span, name, symbol_id, flags);
Expression::Identifier(self.ast.alloc(ident))
}
#[inline]
pub fn create_unbound_reference(&mut self, name: &str, flags: ReferenceFlags) -> ReferenceId {
self.scoping.create_unbound_reference(name, flags)
}
pub fn create_unbound_ident_reference(
&mut self,
span: Span,
name: Atom<'a>,
flags: ReferenceFlags,
) -> IdentifierReference<'a> {
let reference_id = self.create_unbound_reference(name.as_str(), flags);
self.ast.identifier_reference_with_reference_id(span, name, reference_id)
}
pub fn create_unbound_ident_expr(
&mut self,
span: Span,
name: Atom<'a>,
flags: ReferenceFlags,
) -> Expression<'a> {
let ident = self.create_unbound_ident_reference(span, name, flags);
Expression::Identifier(self.ast.alloc(ident))
}
#[inline]
pub fn create_reference(
&mut self,
name: &str,
symbol_id: Option<SymbolId>,
flags: ReferenceFlags,
) -> ReferenceId {
self.scoping.create_reference(name, symbol_id, flags)
}
pub fn create_ident_reference(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: Option<SymbolId>,
flags: ReferenceFlags,
) -> IdentifierReference<'a> {
if let Some(symbol_id) = symbol_id {
self.create_bound_ident_reference(span, name, symbol_id, flags)
} else {
self.create_unbound_ident_reference(span, name, flags)
}
}
pub fn create_ident_expr(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: Option<SymbolId>,
flags: ReferenceFlags,
) -> Expression<'a> {
if let Some(symbol_id) = symbol_id {
self.create_bound_ident_expr(span, name, symbol_id, flags)
} else {
self.create_unbound_ident_expr(span, name, flags)
}
}
#[inline]
pub fn create_reference_in_current_scope(
&mut self,
name: &str,
flags: ReferenceFlags,
) -> ReferenceId {
self.scoping.create_reference_in_current_scope(name, flags)
}
pub fn delete_reference(&mut self, reference_id: ReferenceId, name: &str) {
self.scoping.delete_reference(reference_id, name);
}
pub fn delete_reference_for_identifier(&mut self, ident: &IdentifierReference) {
self.scoping.delete_reference_for_identifier(ident);
}
}
impl<'a, State> TraverseCtx<'a, State> {
pub(crate) fn new(state: State, scoping: Scoping, allocator: &'a Allocator) -> Self {
let ancestry = TraverseAncestry::new();
let scoping = TraverseScoping::new(scoping);
let ast = AstBuilder::new(allocator);
Self { state, ancestry, scoping, ast }
}
#[inline]
pub(crate) fn push_stack(&mut self, ancestor: Ancestor<'a, 'static>) -> PopToken {
self.ancestry.push_stack(ancestor)
}
#[inline]
pub(crate) unsafe fn pop_stack(&mut self, token: PopToken) {
self.ancestry.pop_stack(token);
}
#[inline]
pub(crate) unsafe fn retag_stack(&mut self, ty: AncestorType) {
unsafe { self.ancestry.retag_stack(ty) };
}
#[inline]
pub(crate) fn set_current_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_scope_id(scope_id);
}
#[inline]
pub(crate) fn set_current_hoist_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_hoist_scope_id(scope_id);
}
#[inline]
pub(crate) fn set_current_block_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_block_scope_id(scope_id);
}
}