use std::{fmt, mem};
use oxc_allocator::{Allocator, FromIn, Vec as ArenaVec};
use oxc_ast::ast::{Expression, IdentifierReference};
use oxc_index::{Idx, IndexVec};
use oxc_span::{Atom, Span};
use oxc_syntax::{
node::NodeId,
reference::{Reference, ReferenceId},
scope::ScopeId,
symbol::{RedeclarationId, SymbolFlags, SymbolId},
};
pub struct SymbolTable {
pub(crate) spans: IndexVec<SymbolId, Span>,
pub(crate) flags: IndexVec<SymbolId, SymbolFlags>,
pub(crate) scope_ids: IndexVec<SymbolId, ScopeId>,
pub(crate) declarations: IndexVec<SymbolId, NodeId>,
redeclarations: IndexVec<SymbolId, Option<RedeclarationId>>,
pub references: IndexVec<ReferenceId, Reference>,
inner: SymbolTableCell,
}
impl fmt::Debug for SymbolTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("SymbolTable").finish()
}
}
impl Default for SymbolTable {
fn default() -> Self {
let allocator = Allocator::default();
Self {
spans: IndexVec::new(),
flags: IndexVec::new(),
scope_ids: IndexVec::new(),
declarations: IndexVec::new(),
redeclarations: IndexVec::new(),
references: IndexVec::new(),
inner: SymbolTableCell::new(allocator, |allocator| SymbolTableInner {
names: ArenaVec::new_in(allocator),
resolved_references: ArenaVec::new_in(allocator),
redeclaration_spans: ArenaVec::new_in(allocator),
}),
}
}
}
self_cell::self_cell!(
struct SymbolTableCell {
owner: Allocator,
#[covariant]
dependent: SymbolTableInner,
}
);
struct SymbolTableInner<'cell> {
names: ArenaVec<'cell, Atom<'cell>>,
resolved_references: ArenaVec<'cell, ArenaVec<'cell, ReferenceId>>,
redeclaration_spans: ArenaVec<'cell, ArenaVec<'cell, Span>>,
}
impl SymbolTable {
#[inline]
pub fn len(&self) -> usize {
self.spans.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.spans.is_empty()
}
pub fn names(&self) -> impl Iterator<Item = &str> + '_ {
self.inner.borrow_dependent().names.iter().map(Atom::as_str)
}
pub fn resolved_references(&self) -> impl Iterator<Item = &ArenaVec<'_, ReferenceId>> + '_ {
self.inner.borrow_dependent().resolved_references.iter()
}
pub fn symbol_ids(&self) -> impl Iterator<Item = SymbolId> + '_ {
self.spans.iter_enumerated().map(|(symbol_id, _)| symbol_id)
}
#[inline]
pub fn get_span(&self, symbol_id: SymbolId) -> Span {
self.spans[symbol_id]
}
#[inline]
pub fn get_name(&self, symbol_id: SymbolId) -> &str {
&self.inner.borrow_dependent().names[symbol_id.index()]
}
#[inline]
pub fn set_name(&mut self, symbol_id: SymbolId, name: &str) -> &str {
self.inner
.with_dependent_mut(|allocator, inner| {
mem::replace(&mut inner.names[symbol_id.index()], Atom::from_in(name, allocator))
})
.as_str()
}
#[inline]
pub fn get_flags(&self, symbol_id: SymbolId) -> SymbolFlags {
self.flags[symbol_id]
}
#[inline]
pub fn get_flags_mut(&mut self, symbol_id: SymbolId) -> &mut SymbolFlags {
&mut self.flags[symbol_id]
}
#[inline]
pub fn get_redeclarations(&self, symbol_id: SymbolId) -> &[Span] {
if let Some(redeclaration_id) = self.redeclarations[symbol_id] {
&self.inner.borrow_dependent().redeclaration_spans[redeclaration_id.index()]
} else {
static EMPTY: &[Span] = &[];
EMPTY
}
}
#[inline]
pub fn union_flag(&mut self, symbol_id: SymbolId, includes: SymbolFlags) {
self.flags[symbol_id] |= includes;
}
#[inline]
pub fn set_scope_id(&mut self, symbol_id: SymbolId, scope_id: ScopeId) {
self.scope_ids[symbol_id] = scope_id;
}
#[inline]
pub fn get_scope_id(&self, symbol_id: SymbolId) -> ScopeId {
self.scope_ids[symbol_id]
}
#[inline]
pub fn get_declaration(&self, symbol_id: SymbolId) -> NodeId {
self.declarations[symbol_id]
}
pub fn create_symbol(
&mut self,
span: Span,
name: &str,
flags: SymbolFlags,
scope_id: ScopeId,
node_id: NodeId,
) -> SymbolId {
self.spans.push(span);
self.flags.push(flags);
self.scope_ids.push(scope_id);
self.declarations.push(node_id);
self.inner.with_dependent_mut(|allocator, inner| {
inner.names.push(Atom::from_in(name, allocator));
inner.resolved_references.push(ArenaVec::new_in(allocator));
});
self.redeclarations.push(None)
}
pub fn add_redeclaration(&mut self, symbol_id: SymbolId, span: Span) {
if let Some(redeclaration_id) = self.redeclarations[symbol_id] {
self.inner.with_dependent_mut(|_, inner| {
inner.redeclaration_spans[redeclaration_id.index()].push(span);
});
} else {
self.inner.with_dependent_mut(|allocator, inner| {
let mut v = ArenaVec::new_in(allocator);
v.push(span);
let redeclaration_id = inner.redeclaration_spans.len();
inner.redeclaration_spans.push(v);
self.redeclarations[symbol_id] =
Some(RedeclarationId::from_usize(redeclaration_id));
});
};
}
pub fn create_reference(&mut self, reference: Reference) -> ReferenceId {
self.references.push(reference)
}
#[inline]
pub fn get_reference(&self, reference_id: ReferenceId) -> &Reference {
&self.references[reference_id]
}
#[inline]
pub fn get_reference_mut(&mut self, reference_id: ReferenceId) -> &mut Reference {
&mut self.references[reference_id]
}
#[inline]
pub fn has_binding(&self, reference_id: ReferenceId) -> bool {
self.references[reference_id].symbol_id().is_some()
}
#[inline]
pub fn get_resolved_reference_ids(&self, symbol_id: SymbolId) -> &ArenaVec<'_, ReferenceId> {
&self.inner.borrow_dependent().resolved_references[symbol_id.index()]
}
pub fn get_resolved_references(
&self,
symbol_id: SymbolId,
) -> impl DoubleEndedIterator<Item = &Reference> + '_ {
self.get_resolved_reference_ids(symbol_id)
.iter()
.map(|&reference_id| &self.references[reference_id])
}
pub fn symbol_is_mutated(&self, symbol_id: SymbolId) -> bool {
if self.flags[symbol_id].contains(SymbolFlags::ConstVariable) {
false
} else {
self.get_resolved_references(symbol_id).any(Reference::is_write)
}
}
pub fn add_resolved_reference(&mut self, symbol_id: SymbolId, reference_id: ReferenceId) {
self.inner.with_dependent_mut(|_allocator, inner| {
inner.resolved_references[symbol_id.index()].push(reference_id);
});
}
pub fn delete_resolved_reference(&mut self, symbol_id: SymbolId, reference_id: ReferenceId) {
self.inner.with_dependent_mut(|_allocator, inner| {
let reference_ids = &mut inner.resolved_references[symbol_id.index()];
let index = reference_ids.iter().position(|&id| id == reference_id).unwrap();
reference_ids.swap_remove(index);
});
}
pub fn reserve(&mut self, additional_symbols: usize, additional_references: usize) {
self.spans.reserve(additional_symbols);
self.flags.reserve(additional_symbols);
self.scope_ids.reserve(additional_symbols);
self.declarations.reserve(additional_symbols);
self.inner.with_dependent_mut(|_allocator, inner| {
inner.names.reserve(additional_symbols);
inner.resolved_references.reserve(additional_symbols);
});
self.references.reserve(additional_references);
}
}
pub trait IsGlobalReference {
fn is_global_reference(&self, _symbols: &SymbolTable) -> bool;
fn is_global_reference_name(&self, name: &str, _symbols: &SymbolTable) -> bool;
}
impl IsGlobalReference for ReferenceId {
fn is_global_reference(&self, symbols: &SymbolTable) -> bool {
symbols.references[*self].symbol_id().is_none()
}
fn is_global_reference_name(&self, _name: &str, _symbols: &SymbolTable) -> bool {
panic!("This function is pointless to be called.");
}
}
impl IsGlobalReference for IdentifierReference<'_> {
fn is_global_reference(&self, symbols: &SymbolTable) -> bool {
self.reference_id
.get()
.is_some_and(|reference_id| reference_id.is_global_reference(symbols))
}
fn is_global_reference_name(&self, name: &str, symbols: &SymbolTable) -> bool {
self.name == name && self.is_global_reference(symbols)
}
}
impl IsGlobalReference for Expression<'_> {
fn is_global_reference(&self, symbols: &SymbolTable) -> bool {
if let Expression::Identifier(ident) = self {
return ident.is_global_reference(symbols);
}
false
}
fn is_global_reference_name(&self, name: &str, symbols: &SymbolTable) -> bool {
if let Expression::Identifier(ident) = self {
return ident.is_global_reference_name(name, symbols);
}
false
}
}