perf(semantic): use Atom<'a> for References (#3972)

Relates to [this
issue](https://github.com/oxc-project/backlog/issues/31) on the backlog.
This commit is contained in:
Don Isaac 2024-06-29 11:23:36 -04:00 committed by GitHub
parent 0c81fbeac6
commit 1eac3d244d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 88 additions and 88 deletions

View file

@ -66,7 +66,9 @@ impl Rule for NoGlobalAssign {
let reference = symbol_table.get_reference(reference_id);
if reference.is_write() {
let name = reference.name();
if !self.excludes.contains(name) && ctx.env_contains_var(name) {
// Vec::contains isn't working here, but this has the same
// effect and time complexity.
if !self.excludes.iter().any(|e| e == name) && ctx.env_contains_var(name) {
ctx.diagnostic(no_global_assign_diagnostic(name, reference.span()));
}
}

View file

@ -7,11 +7,11 @@ use oxc_span::CompactStr;
type Slot = usize;
#[derive(Debug)]
pub struct Mangler {
symbol_table: SymbolTable,
pub struct Mangler<'a> {
symbol_table: SymbolTable<'a>,
}
impl Mangler {
impl<'a> Mangler<'a> {
pub fn get_symbol_name(&self, symbol_id: SymbolId) -> &str {
self.symbol_table.get_name(symbol_id)
}

View file

@ -9,7 +9,7 @@ use oxc_cfg::{
IterationInstructionKind, ReturnInstructionKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{CompactStr, SourceType, Span};
use oxc_span::{Atom, CompactStr, SourceType, Span};
use oxc_syntax::{module_record::ModuleRecord, operator::AssignmentOperator};
use crate::{
@ -63,8 +63,8 @@ pub struct SemanticBuilder<'a> {
// builders
pub nodes: AstNodes<'a>,
pub scope: ScopeTree,
pub symbols: SymbolTable,
pub scope: ScopeTree<'a>,
pub symbols: SymbolTable<'a>,
pub(crate) module_record: Arc<ModuleRecord>,
@ -315,7 +315,7 @@ impl<'a> SemanticBuilder<'a> {
pub fn declare_reference(
&mut self,
reference: Reference,
reference: Reference<'a>,
add_unresolved_reference: bool,
) -> ReferenceId {
let reference_name = reference.name().clone();
@ -327,7 +327,7 @@ impl<'a> SemanticBuilder<'a> {
reference_id,
);
} else {
self.resolve_reference_ids(reference_name.clone(), vec![reference_id]);
self.resolve_reference_ids(reference_name, vec![reference_id]);
}
reference_id
}
@ -361,7 +361,7 @@ impl<'a> SemanticBuilder<'a> {
}
}
fn resolve_reference_ids(&mut self, name: CompactStr, reference_ids: Vec<ReferenceId>) {
fn resolve_reference_ids(&mut self, name: Atom<'a>, reference_ids: Vec<ReferenceId>) {
let parent_scope_id =
self.scope.get_parent_id(self.current_scope_id).unwrap_or(self.current_scope_id);
@ -1884,9 +1884,9 @@ impl<'a> SemanticBuilder<'a> {
}
}
fn reference_identifier(&mut self, ident: &IdentifierReference) {
fn reference_identifier(&mut self, ident: &IdentifierReference<'a>) {
let flag = self.resolve_reference_usages();
let name = ident.name.to_compact_str();
let name = ident.name.clone();
let reference = Reference::new(ident.span, name, self.current_node_id, flag);
// `function foo({bar: identifier_reference}) {}`
// ^^^^^^^^^^^^^^^^^^^^ Parameter initializer must be resolved immediately
@ -1905,7 +1905,7 @@ impl<'a> SemanticBuilder<'a> {
}
}
fn reference_jsx_identifier(&mut self, ident: &JSXIdentifier) {
fn reference_jsx_identifier(&mut self, ident: &JSXIdentifier<'a>) {
match self.nodes.parent_kind(self.current_node_id) {
Some(AstKind::JSXElementName(_)) => {
if !ident.name.chars().next().is_some_and(char::is_uppercase) {
@ -1917,7 +1917,7 @@ impl<'a> SemanticBuilder<'a> {
}
let reference = Reference::new(
ident.span,
ident.name.to_compact_str(),
ident.name.clone(),
self.current_node_id,
ReferenceFlag::read(),
);

View file

@ -42,9 +42,9 @@ pub struct Semantic<'a> {
nodes: AstNodes<'a>,
scopes: ScopeTree,
scopes: ScopeTree<'a>,
symbols: SymbolTable,
symbols: SymbolTable<'a>,
classes: ClassTable,
@ -60,7 +60,7 @@ pub struct Semantic<'a> {
}
impl<'a> Semantic<'a> {
pub fn into_symbol_table_and_scope_tree(self) -> (SymbolTable, ScopeTree) {
pub fn into_symbol_table_and_scope_tree(self) -> (SymbolTable<'a>, ScopeTree<'a>) {
(self.symbols, self.scopes)
}
@ -84,7 +84,7 @@ impl<'a> Semantic<'a> {
&self.classes
}
pub fn scopes_mut(&mut self) -> &mut ScopeTree {
pub fn scopes_mut(&mut self) -> &mut ScopeTree<'a> {
&mut self.scopes
}

View file

@ -1,7 +1,7 @@
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]
use oxc_span::{CompactStr, Span};
use oxc_span::{Atom, Span};
pub use oxc_syntax::reference::{ReferenceFlag, ReferenceId};
#[cfg(feature = "serialize")]
use serde::Serialize;
@ -13,10 +13,10 @@ use crate::{symbol::SymbolId, AstNodeId};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
pub struct Reference {
pub struct Reference<'a> {
span: Span,
/// The name of the identifier that was referred to
name: CompactStr,
name: Atom<'a>,
node_id: AstNodeId,
symbol_id: Option<SymbolId>,
/// Describes how this referenced is used by other AST nodes. References can
@ -24,14 +24,14 @@ pub struct Reference {
flag: ReferenceFlag,
}
impl Reference {
pub fn new(span: Span, name: CompactStr, node_id: AstNodeId, flag: ReferenceFlag) -> Self {
impl<'a> Reference<'a> {
pub fn new(span: Span, name: Atom<'a>, node_id: AstNodeId, flag: ReferenceFlag) -> Self {
Self { span, name, node_id, symbol_id: None, flag }
}
pub fn new_with_symbol_id(
span: Span,
name: CompactStr,
name: Atom<'a>,
node_id: AstNodeId,
symbol_id: SymbolId,
flag: ReferenceFlag,
@ -43,7 +43,7 @@ impl Reference {
self.span
}
pub fn name(&self) -> &CompactStr {
pub fn name(&self) -> &Atom<'a> {
&self.name
}

View file

@ -2,7 +2,7 @@ use std::hash::BuildHasherDefault;
use indexmap::IndexMap;
use oxc_index::IndexVec;
use oxc_span::CompactStr;
use oxc_span::{Atom, CompactStr};
pub use oxc_syntax::scope::{ScopeFlags, ScopeId};
use rustc_hash::{FxHashMap, FxHasher};
@ -11,13 +11,13 @@ use crate::{reference::ReferenceId, symbol::SymbolId, AstNodeId};
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
type Bindings = FxIndexMap<CompactStr, SymbolId>;
type UnresolvedReferences = FxHashMap<CompactStr, Vec<ReferenceId>>;
type UnresolvedReferences<'a> = FxHashMap<Atom<'a>, Vec<ReferenceId>>;
/// Scope Tree
///
/// `SoA` (Struct of Arrays) for memory efficiency.
#[derive(Debug, Default)]
pub struct ScopeTree {
pub struct ScopeTree<'a> {
/// Maps a scope to the parent scope it belongs in
parent_ids: IndexVec<ScopeId, Option<ScopeId>>,
@ -27,10 +27,10 @@ pub struct ScopeTree {
node_ids: FxHashMap<ScopeId, AstNodeId>,
flags: IndexVec<ScopeId, ScopeFlags>,
bindings: IndexVec<ScopeId, Bindings>,
unresolved_references: IndexVec<ScopeId, UnresolvedReferences>,
unresolved_references: IndexVec<ScopeId, UnresolvedReferences<'a>>,
}
impl ScopeTree {
impl<'a> ScopeTree<'a> {
pub fn len(&self) -> usize {
self.parent_ids.len()
}
@ -141,7 +141,7 @@ impl ScopeTree {
self.get_binding(self.root_scope_id(), name)
}
pub fn add_root_unresolved_reference(&mut self, name: CompactStr, reference_id: ReferenceId) {
pub fn add_root_unresolved_reference(&mut self, name: Atom<'a>, reference_id: ReferenceId) {
self.add_unresolved_reference(self.root_scope_id(), name, reference_id);
}
@ -208,7 +208,7 @@ impl ScopeTree {
pub(crate) fn add_unresolved_reference(
&mut self,
scope_id: ScopeId,
name: CompactStr,
name: Atom<'a>,
reference_id: ReferenceId,
) {
self.unresolved_references[scope_id].entry(name).or_default().push(reference_id);
@ -217,7 +217,7 @@ impl ScopeTree {
pub(crate) fn extend_unresolved_reference(
&mut self,
scope_id: ScopeId,
name: CompactStr,
name: Atom<'a>,
reference_ids: Vec<ReferenceId>,
) {
self.unresolved_references[scope_id].entry(name).or_default().extend(reference_ids);
@ -226,7 +226,7 @@ impl ScopeTree {
pub(crate) fn unresolved_references_mut(
&mut self,
scope_id: ScopeId,
) -> &mut UnresolvedReferences {
) -> &mut UnresolvedReferences<'a> {
&mut self.unresolved_references[scope_id]
}
}

View file

@ -28,7 +28,7 @@ export type IndexVec<I, T> = Array<T>;
/// `SoA` (Struct of Arrays) for memory efficiency.
#[derive(Debug, Default)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify), serde(rename_all = "camelCase"))]
pub struct SymbolTable {
pub struct SymbolTable<'a> {
pub spans: IndexVec<SymbolId, Span>,
pub names: IndexVec<SymbolId, CompactStr>,
pub flags: IndexVec<SymbolId, SymbolFlags>,
@ -36,11 +36,11 @@ pub struct SymbolTable {
/// Pointer to the AST Node where this symbol is declared
pub declarations: IndexVec<SymbolId, AstNodeId>,
pub resolved_references: IndexVec<SymbolId, Vec<ReferenceId>>,
pub references: IndexVec<ReferenceId, Reference>,
pub references: IndexVec<ReferenceId, Reference<'a>>,
pub redeclare_variables: IndexVec<SymbolId, Vec<Span>>,
}
impl SymbolTable {
impl<'a> SymbolTable<'a> {
pub fn len(&self) -> usize {
self.spans.len()
}
@ -136,15 +136,15 @@ impl SymbolTable {
self.redeclare_variables[symbol_id].push(span);
}
pub fn create_reference(&mut self, reference: Reference) -> ReferenceId {
pub fn create_reference(&mut self, reference: Reference<'a>) -> ReferenceId {
self.references.push(reference)
}
pub fn get_reference(&self, reference_id: ReferenceId) -> &Reference {
pub fn get_reference(&self, reference_id: ReferenceId) -> &Reference<'a> {
&self.references[reference_id]
}
pub fn get_reference_mut(&mut self, reference_id: ReferenceId) -> &mut Reference {
pub fn get_reference_mut(&mut self, reference_id: ReferenceId) -> &mut Reference<'a> {
&mut self.references[reference_id]
}

View file

@ -110,18 +110,24 @@ impl<'a> SymbolTester<'a> {
self
}
// Note: can't use `Reference::is_read()` due to error warning about overly
// generic FnMut impl.
#[must_use]
pub fn has_number_of_reads(self, ref_count: usize) -> Self {
self.has_number_of_references_where(ref_count, Reference::is_read)
#[allow(clippy::redundant_closure_for_method_calls)]
self.has_number_of_references_where(ref_count, |r| r.is_read())
}
#[must_use]
pub fn has_number_of_writes(self, ref_count: usize) -> Self {
self.has_number_of_references_where(ref_count, Reference::is_write)
#[allow(clippy::redundant_closure_for_method_calls)]
self.has_number_of_references_where(ref_count, |r| r.is_write())
}
#[must_use]
pub fn has_number_of_references(self, ref_count: usize) -> Self {
#[allow(clippy::redundant_closure_for_method_calls)]
self.has_number_of_references_where(ref_count, |_| true)
}

View file

@ -986,8 +986,7 @@ fn get_read_identifier_reference<'a>(
name: Atom<'a>,
ctx: &mut TraverseCtx<'a>,
) -> IdentifierReference<'a> {
let reference_id =
ctx.create_reference_in_current_scope(name.to_compact_str(), ReferenceFlag::Read);
let reference_id = ctx.create_reference_in_current_scope(name.clone(), ReferenceFlag::Read);
IdentifierReference::new_read(span, name, Some(reference_id))
}

View file

@ -540,11 +540,8 @@ struct Assignment<'a> {
impl<'a> Assignment<'a> {
// Creates `this.name = name`
fn create_this_property_assignment(&self, ctx: &mut TraverseCtx<'a>) -> Statement<'a> {
let reference_id = ctx.create_bound_reference(
self.name.to_compact_str(),
self.symbol_id,
ReferenceFlag::Read,
);
let reference_id =
ctx.create_bound_reference(self.name.clone(), self.symbol_id, ReferenceFlag::Read);
let id = IdentifierReference::new_read(self.span, self.name.clone(), Some(reference_id));
ctx.ast.expression_statement(

View file

@ -4,7 +4,7 @@ use oxc_ast::{
AstBuilder,
};
use oxc_semantic::{ScopeTree, SymbolTable};
use oxc_span::{Atom, CompactStr, Span};
use oxc_span::{Atom, Span};
use oxc_syntax::{
reference::{ReferenceFlag, ReferenceId},
scope::{ScopeFlags, ScopeId},
@ -106,7 +106,7 @@ pub use scoping::TraverseScoping;
/// [`alloc`]: `TraverseCtx::alloc`
pub struct TraverseCtx<'a> {
pub ancestry: TraverseAncestry<'a>,
pub scoping: TraverseScoping,
pub scoping: TraverseScoping<'a>,
pub ast: AstBuilder<'a>,
}
@ -120,7 +120,11 @@ pub enum FinderRet<T> {
// Public methods
impl<'a> TraverseCtx<'a> {
/// Create new traversal context.
pub(crate) fn new(scopes: ScopeTree, symbols: SymbolTable, allocator: &'a Allocator) -> Self {
pub(crate) fn new(
scopes: ScopeTree<'a>,
symbols: SymbolTable<'a>,
allocator: &'a Allocator,
) -> Self {
let ancestry = TraverseAncestry::new();
let scoping = TraverseScoping::new(scopes, symbols);
let ast = AstBuilder::new(allocator);
@ -224,7 +228,7 @@ impl<'a> TraverseCtx<'a> {
///
/// Shortcut for `ctx.scoping.scopes`.
#[inline]
pub fn scopes(&self) -> &ScopeTree {
pub fn scopes(&self) -> &ScopeTree<'a> {
self.scoping.scopes()
}
@ -232,7 +236,7 @@ impl<'a> TraverseCtx<'a> {
///
/// Shortcut for `ctx.scoping.scopes_mut`.
#[inline]
pub fn scopes_mut(&mut self) -> &mut ScopeTree {
pub fn scopes_mut(&mut self) -> &mut ScopeTree<'a> {
self.scoping.scopes_mut()
}
@ -240,7 +244,7 @@ impl<'a> TraverseCtx<'a> {
///
/// Shortcut for `ctx.scoping.symbols`.
#[inline]
pub fn symbols(&self) -> &SymbolTable {
pub fn symbols(&self) -> &SymbolTable<'a> {
self.scoping.symbols()
}
@ -248,7 +252,7 @@ impl<'a> TraverseCtx<'a> {
///
/// Shortcut for `ctx.scoping.symbols_mut`.
#[inline]
pub fn symbols_mut(&mut self) -> &mut SymbolTable {
pub fn symbols_mut(&mut self) -> &mut SymbolTable<'a> {
self.scoping.symbols_mut()
}
@ -351,7 +355,7 @@ impl<'a> TraverseCtx<'a> {
/// This is a shortcut for `ctx.scoping.create_bound_reference`.
pub fn create_bound_reference(
&mut self,
name: CompactStr,
name: Atom<'a>,
symbol_id: SymbolId,
flag: ReferenceFlag,
) -> ReferenceId {
@ -374,11 +378,7 @@ impl<'a> TraverseCtx<'a> {
/// Create an unbound reference.
///
/// This is a shortcut for `ctx.scoping.create_unbound_reference`.
pub fn create_unbound_reference(
&mut self,
name: CompactStr,
flag: ReferenceFlag,
) -> ReferenceId {
pub fn create_unbound_reference(&mut self, name: Atom<'a>, flag: ReferenceFlag) -> ReferenceId {
self.scoping.create_unbound_reference(name, flag)
}
@ -402,7 +402,7 @@ impl<'a> TraverseCtx<'a> {
/// This is a shortcut for `ctx.scoping.create_reference`.
pub fn create_reference(
&mut self,
name: CompactStr,
name: Atom<'a>,
symbol_id: Option<SymbolId>,
flag: ReferenceFlag,
) -> ReferenceId {
@ -430,7 +430,7 @@ impl<'a> TraverseCtx<'a> {
/// This is a shortcut for `ctx.scoping.create_reference_in_current_scope`.
pub fn create_reference_in_current_scope(
&mut self,
name: CompactStr,
name: Atom<'a>,
flag: ReferenceFlag,
) -> ReferenceId {
self.scoping.create_reference_in_current_scope(name, flag)

View file

@ -22,14 +22,14 @@ use super::FinderRet;
///
/// `current_scope_id` is the ID of current scope during traversal.
/// `walk_*` functions update this field when entering/exiting a scope.
pub struct TraverseScoping {
scopes: ScopeTree,
symbols: SymbolTable,
pub struct TraverseScoping<'a> {
scopes: ScopeTree<'a>,
symbols: SymbolTable<'a>,
current_scope_id: ScopeId,
}
// Public methods
impl TraverseScoping {
impl<'a> TraverseScoping<'a> {
/// Get current scope ID
#[inline]
pub fn current_scope_id(&self) -> ScopeId {
@ -44,25 +44,25 @@ impl TraverseScoping {
/// Get scopes tree
#[inline]
pub fn scopes(&self) -> &ScopeTree {
pub fn scopes(&self) -> &ScopeTree<'a> {
&self.scopes
}
/// Get mutable scopes tree
#[inline]
pub fn scopes_mut(&mut self) -> &mut ScopeTree {
pub fn scopes_mut(&mut self) -> &mut ScopeTree<'a> {
&mut self.scopes
}
/// Get symbols table
#[inline]
pub fn symbols(&self) -> &SymbolTable {
pub fn symbols(&self) -> &SymbolTable<'a> {
&self.symbols
}
/// Get mutable symbols table
#[inline]
pub fn symbols_mut(&mut self) -> &mut SymbolTable {
pub fn symbols_mut(&mut self) -> &mut SymbolTable<'a> {
&mut self.symbols
}
@ -259,7 +259,7 @@ impl TraverseScoping {
/// Create a reference bound to a `SymbolId`
pub fn create_bound_reference(
&mut self,
name: CompactStr,
name: Atom<'a>,
symbol_id: SymbolId,
flag: ReferenceFlag,
) -> ReferenceId {
@ -271,14 +271,14 @@ impl TraverseScoping {
}
/// Create an `IdentifierReference` bound to a `SymbolId`
pub fn create_bound_reference_id<'a>(
pub fn create_bound_reference_id(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: SymbolId,
flag: ReferenceFlag,
) -> IdentifierReference<'a> {
let reference_id = self.create_bound_reference(name.to_compact_str(), symbol_id, flag);
let reference_id = self.create_bound_reference(name.clone(), symbol_id, flag);
IdentifierReference {
span,
name,
@ -288,11 +288,7 @@ impl TraverseScoping {
}
/// Create an unbound reference
pub fn create_unbound_reference(
&mut self,
name: CompactStr,
flag: ReferenceFlag,
) -> ReferenceId {
pub fn create_unbound_reference(&mut self, name: Atom<'a>, flag: ReferenceFlag) -> ReferenceId {
let reference = Reference::new(SPAN, name.clone(), AstNodeId::dummy(), flag);
let reference_id = self.symbols.create_reference(reference);
self.scopes.add_root_unresolved_reference(name, reference_id);
@ -300,13 +296,13 @@ impl TraverseScoping {
}
/// Create an unbound `IdentifierReference`
pub fn create_unbound_reference_id<'a>(
pub fn create_unbound_reference_id(
&mut self,
span: Span,
name: Atom<'a>,
flag: ReferenceFlag,
) -> IdentifierReference<'a> {
let reference_id = self.create_unbound_reference(name.to_compact_str(), flag);
let reference_id = self.create_unbound_reference(name.clone(), flag);
IdentifierReference {
span,
name,
@ -321,12 +317,12 @@ impl TraverseScoping {
/// or `TraverseCtx::create_unbound_reference`.
pub fn create_reference(
&mut self,
name: CompactStr,
name: Atom<'a>,
symbol_id: Option<SymbolId>,
flag: ReferenceFlag,
) -> ReferenceId {
if let Some(symbol_id) = symbol_id {
self.create_bound_reference(name, symbol_id, flag)
self.create_bound_reference(name.clone(), symbol_id, flag)
} else {
self.create_unbound_reference(name, flag)
}
@ -336,7 +332,7 @@ impl TraverseScoping {
///
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference_id`
/// or `TraverseCtx::create_unbound_reference_id`.
pub fn create_reference_id<'a>(
pub fn create_reference_id(
&mut self,
span: Span,
name: Atom<'a>,
@ -353,7 +349,7 @@ impl TraverseScoping {
/// Create reference in current scope, looking up binding for `name`
pub fn create_reference_in_current_scope(
&mut self,
name: CompactStr,
name: Atom<'a>,
flag: ReferenceFlag,
) -> ReferenceId {
let symbol_id = self.scopes.find_binding(self.current_scope_id, name.as_str());
@ -362,9 +358,9 @@ impl TraverseScoping {
}
// Methods used internally within crate
impl TraverseScoping {
impl<'a> TraverseScoping<'a> {
/// Create new `TraverseScoping`
pub(super) fn new(scopes: ScopeTree, symbols: SymbolTable) -> Self {
pub(super) fn new(scopes: ScopeTree<'a>, symbols: SymbolTable<'a>) -> Self {
Self {
scopes,
symbols,