Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(semantic): memoize Bindings hashmap's key hash (POC) #8622

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl Rule for NoJasmineGlobals {
.scopes()
.root_unresolved_references()
.iter()
.filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(key));
.filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(&key.name));

for (name, reference_ids) in jasmine_references {
for &reference_id in reference_ids {
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_linter/src/rules/jest/no_mocks_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::path::PathBuf;
use oxc_ast::{ast::Argument, AstKind};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::Key;
use oxc_span::Span;

use crate::{context::LintContext, rule::Rule};
Expand Down Expand Up @@ -55,7 +56,8 @@ impl Rule for NoMocksImport {
}
}

let Some(require_reference_ids) = ctx.scopes().root_unresolved_references().get("require")
let Some(require_reference_ids) =
ctx.scopes().root_unresolved_references().get(&Key::new("require"))
else {
return;
};
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/react/exhaustive_deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use oxc_ast::{
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{ReferenceId, ScopeId, Semantic, SymbolId};
use oxc_semantic::{Key, ReferenceId, ScopeId, Semantic, SymbolId};
use oxc_span::{Atom, GetSpan, Span};

use crate::{
Expand Down Expand Up @@ -770,7 +770,7 @@ fn is_identifier_a_dependency<'a>(
component_scope_id: ScopeId,
) -> bool {
// if it is a global e.g. `console` or `window`, then it's not a dependency
if ctx.semantic().scopes().root_unresolved_references().contains_key(ident_name.as_str()) {
if ctx.semantic().scopes().root_unresolved_references().contains_key(&Key::new(ident_name)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
source: crates/oxc_linter/src/tester.rs
snapshot_kind: text
---
⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods
╭─[no_confusing_set_timeout.tsx:10:17]
Expand All @@ -18,14 +17,13 @@ snapshot_kind: text
11 │
╰────

⚠ eslint-plugin-jest(no-confusing-set-timeout): Do not call `jest.setTimeout` multiple times
⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods
╭─[no_confusing_set_timeout.tsx:10:17]
9 │ });
10 │ jest.setTimeout(800);
· ───────────────
11 │
╰────
help: Only the last call to `jest.setTimeout` will have an effect.

⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods
╭─[no_confusing_set_timeout.tsx:10:17]
Expand All @@ -35,13 +33,14 @@ snapshot_kind: text
11 │
╰────

⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods
⚠ eslint-plugin-jest(no-confusing-set-timeout): Do not call `jest.setTimeout` multiple times
╭─[no_confusing_set_timeout.tsx:10:17]
9 │ });
10 │ jest.setTimeout(800);
· ───────────────
11 │
╰────
help: Only the last call to `jest.setTimeout` will have an effect.

⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods
╭─[no_confusing_set_timeout.tsx:3:21]
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/utils/jest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ fn collect_ids_referenced_to_global<'c>(
.scopes()
.root_unresolved_references()
.iter()
.filter(|(name, _)| JEST_METHOD_NAMES.contains(name))
.filter(|(key, _)| JEST_METHOD_NAMES.contains(key.name))
.flat_map(|(_, reference_ids)| reference_ids.iter().copied())
}

Expand Down
22 changes: 15 additions & 7 deletions crates/oxc_mangler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_hash::FxHashSet;
use oxc_allocator::{Allocator, Vec};
use oxc_ast::ast::{Declaration, Program, Statement};
use oxc_index::Idx;
use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable};
use oxc_semantic::{Key, ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable};
use oxc_span::Atom;

#[derive(Default, Debug, Clone, Copy)]
Expand Down Expand Up @@ -195,14 +195,22 @@ impl Mangler {
count += 1;
// Do not mangle keywords and unresolved references
let n = name.as_str();
if !is_keyword(n)
&& !is_special_name(n)
&& !root_unresolved_references.contains_key(n)
&& !(root_bindings.contains_key(n)
&& (!self.options.top_level || exported_names.contains(n)))
if is_keyword(n) {
continue;
}
if is_special_name(n) {
continue;
}
let key = Key::new(n);
if root_unresolved_references.contains_key(&key) {
continue;
}
if root_bindings.contains_key(&key)
&& (!self.options.top_level || exported_names.contains(n))
{
break name;
continue;
}
break name;
};
reserved_names.push(name);
}
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_semantic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ oxc_span = { workspace = true }
oxc_syntax = { workspace = true }

assert-unchecked = { workspace = true }
hashbrown = { workspace = true, features = ["equivalent"] }
itertools = { workspace = true }
phf = { workspace = true, features = ["macros"] }
rustc-hash = { workspace = true }
Expand Down
26 changes: 12 additions & 14 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

use std::{
cell::{Cell, RefCell},
hash::BuildHasherDefault,
mem,
};

use oxc_data_structures::stack::Stack;
use rustc_hash::FxHashMap;

use oxc_ast::{ast::*, AstKind, Visit};
use oxc_cfg::{
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind, InstructionKind,
IterationInstructionKind, ReturnInstructionKind,
};
use oxc_data_structures::stack::Stack;
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{Atom, SourceType, Span};
use oxc_syntax::{
Expand All @@ -30,7 +31,7 @@ use crate::{
jsdoc::JSDocBuilder,
label::UnusedLabels,
node::AstNodes,
scope::{Bindings, ScopeTree},
scope::{Bindings, IdentityHasher, Key, ScopeTree},
stats::Stats,
symbol::SymbolTable,
unresolved_stack::UnresolvedReferencesStack,
Expand Down Expand Up @@ -279,9 +280,8 @@ impl<'a> SemanticBuilder<'a> {
if self.check_syntax_error && !self.source_type.is_typescript() {
checker::check_unresolved_exports(&self);
}
self.scope.set_root_unresolved_references(
self.unresolved_references.into_root().into_iter().map(|(k, v)| (k.as_str(), v)),
);
self.scope
.set_root_unresolved_references(self.unresolved_references.into_root().into_iter());

let jsdoc = if self.build_jsdoc { self.jsdoc.build() } else { JSDocFinder::default() };

Expand Down Expand Up @@ -446,13 +446,8 @@ impl<'a> SemanticBuilder<'a> {
/// Declare an unresolved reference in the current scope.
///
/// # Panics
pub(crate) fn declare_reference(
&mut self,
name: Atom<'a>,
reference: Reference,
) -> ReferenceId {
pub(crate) fn declare_reference(&mut self, name: Key<'a>, reference: Reference) -> ReferenceId {
let reference_id = self.symbols.create_reference(reference);

self.unresolved_references.current_mut().entry(name).or_default().push(reference_id);
reference_id
}
Expand Down Expand Up @@ -487,7 +482,7 @@ impl<'a> SemanticBuilder<'a> {
// Try to resolve a reference.
// If unresolved, transfer it to parent scope's unresolved references.
let bindings = self.scope.get_bindings(self.current_scope_id);
if let Some(symbol_id) = bindings.get(name.as_str()).copied() {
if let Some(symbol_id) = bindings.get(&name).copied() {
let symbol_flags = self.symbols.get_flags(symbol_id);
references.retain(|&reference_id| {
let reference = &mut self.symbols.references[reference_id];
Expand Down Expand Up @@ -712,7 +707,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
if self.scope.get_flags(parent_scope_id).is_catch_clause() {
self.scope.cell.with_dependent_mut(|allocator, inner| {
if !inner.bindings[parent_scope_id].is_empty() {
let mut parent_bindings = Bindings::new_in(allocator);
let mut parent_bindings = Bindings::with_hasher_in(
BuildHasherDefault::<IdentityHasher>::default(),
allocator,
);
mem::swap(&mut inner.bindings[parent_scope_id], &mut parent_bindings);
for &symbol_id in parent_bindings.values() {
self.symbols.set_scope_id(symbol_id, self.current_scope_id);
Expand Down Expand Up @@ -2120,7 +2118,7 @@ impl<'a> SemanticBuilder<'a> {
fn reference_identifier(&mut self, ident: &IdentifierReference<'a>) {
let flags = self.resolve_reference_usages();
let reference = Reference::new(self.current_node_id, flags);
let reference_id = self.declare_reference(ident.name, reference);
let reference_id = self.declare_reference(Key::new(ident.name.as_str()), reference);
ident.reference_id.set(Some(reference_id));
}

Expand Down
6 changes: 3 additions & 3 deletions crates/oxc_semantic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ mod unresolved_stack;
pub use builder::{SemanticBuilder, SemanticBuilderReturn};
pub use jsdoc::{JSDoc, JSDocFinder, JSDocTag};
pub use node::{AstNode, AstNodes};
pub use scope::ScopeTree;
pub use scope::{Key, ScopeTree};
pub use stats::Stats;
pub use symbol::{IsGlobalReference, SymbolTable};

Expand Down Expand Up @@ -196,7 +196,7 @@ impl<'a> Semantic<'a> {
let AstKind::IdentifierReference(id) = reference_node.kind() else {
return false;
};
self.scopes().root_unresolved_references().contains_key(id.name.as_str())
self.scopes().root_unresolved_references().contains_key(&Key::new(id.name.as_str()))
}

/// Find which scope a symbol is declared in
Expand All @@ -214,7 +214,7 @@ impl<'a> Semantic<'a> {
}

pub fn is_reference_to_global_variable(&self, ident: &IdentifierReference) -> bool {
self.scopes().root_unresolved_references().contains_key(ident.name.as_str())
self.scopes().root_unresolved_references().contains_key(&Key::new(ident.name.as_str()))
}

pub fn reference_name(&self, reference: &Reference) -> &str {
Expand Down
Loading
Loading