diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock index 9d6fa50b75c..383e5916f8c 100644 --- a/compiler/Cargo.lock +++ b/compiler/Cargo.lock @@ -287,6 +287,7 @@ dependencies = [ "react_compiler_ssa", "react_compiler_typeinference", "react_compiler_validation", + "rustc-hash", "serde", "serde_json", ] @@ -297,6 +298,7 @@ version = "0.1.0" dependencies = [ "indexmap", "react_compiler_diagnostics", + "rustc-hash", "serde", "serde-transcode", "serde_json", @@ -308,6 +310,7 @@ dependencies = [ name = "react_compiler_diagnostics" version = "0.1.0" dependencies = [ + "rustc-hash", "serde", ] @@ -317,6 +320,7 @@ version = "0.1.0" dependencies = [ "indexmap", "react_compiler_diagnostics", + "rustc-hash", "serde", "serde_json", ] @@ -332,6 +336,7 @@ dependencies = [ "react_compiler_optimization", "react_compiler_ssa", "react_compiler_utils", + "rustc-hash", ] [[package]] @@ -342,6 +347,7 @@ dependencies = [ "react_compiler_ast", "react_compiler_diagnostics", "react_compiler_hir", + "rustc-hash", "serde_json", ] @@ -367,6 +373,7 @@ dependencies = [ "react_compiler_hir", "react_compiler_lowering", "react_compiler_ssa", + "rustc-hash", ] [[package]] @@ -378,6 +385,7 @@ dependencies = [ "react_compiler_ast", "react_compiler_diagnostics", "react_compiler_hir", + "rustc-hash", "serde_json", ] @@ -388,6 +396,7 @@ dependencies = [ "indexmap", "react_compiler_diagnostics", "react_compiler_hir", + "rustc-hash", ] [[package]] @@ -397,6 +406,7 @@ dependencies = [ "react_compiler_diagnostics", "react_compiler_hir", "react_compiler_ssa", + "rustc-hash", ] [[package]] @@ -404,6 +414,7 @@ name = "react_compiler_utils" version = "0.1.0" dependencies = [ "indexmap", + "rustc-hash", ] [[package]] @@ -413,6 +424,7 @@ dependencies = [ "indexmap", "react_compiler_diagnostics", "react_compiler_hir", + "rustc-hash", ] [[package]] diff --git a/compiler/crates/react_compiler/Cargo.toml b/compiler/crates/react_compiler/Cargo.toml index 3b265b8d359..b1cec9db841 100644 --- a/compiler/crates/react_compiler/Cargo.toml +++ b/compiler/crates/react_compiler/Cargo.toml @@ -15,5 +15,6 @@ react_compiler_ssa = { path = "../react_compiler_ssa" } react_compiler_typeinference = { path = "../react_compiler_typeinference" } react_compiler_validation = { path = "../react_compiler_validation" } indexmap = "2" +rustc-hash = "2" serde = { version = "1", features = ["derive"] } serde_json = { version = "1", features = ["raw_value"] } diff --git a/compiler/crates/react_compiler/src/entrypoint/imports.rs b/compiler/crates/react_compiler/src/entrypoint/imports.rs index a495686ae56..9201631a09c 100644 --- a/compiler/crates/react_compiler/src/entrypoint/imports.rs +++ b/compiler/crates/react_compiler/src/entrypoint/imports.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_ast::common::BaseNode; use react_compiler_ast::declarations::{ @@ -72,9 +72,9 @@ pub struct ProgramContext { pub debug_enabled: bool, // Internal state - already_compiled: HashSet, - known_referenced_names: HashSet, - imports: HashMap>, + already_compiled: FxHashSet, + known_referenced_names: FxHashSet, + imports: FxHashMap>, } impl ProgramContext { @@ -104,9 +104,9 @@ impl ProgramContext { renames: Vec::new(), timing: TimingData::new(profiling), debug_enabled, - already_compiled: HashSet::new(), - known_referenced_names: HashSet::new(), - imports: HashMap::new(), + already_compiled: FxHashSet::default(), + known_referenced_names: FxHashSet::default(), + imports: FxHashMap::default(), } } @@ -230,13 +230,13 @@ impl ProgramContext { } /// Get the set of known referenced names for seeding per-function Environment UID generation. - pub fn known_referenced_names(&self) -> &HashSet { + pub fn known_referenced_names(&self) -> &FxHashSet { &self.known_referenced_names } /// Merge UID names generated during a function compilation back into the program context, /// so subsequent function compilations avoid collisions. - pub fn merge_uid_known_names(&mut self, names: &HashSet) { + pub fn merge_uid_known_names(&mut self, names: &FxHashSet) { self.known_referenced_names.extend(names.iter().cloned()); } @@ -259,7 +259,7 @@ impl ProgramContext { } /// Get an immutable view of the generated imports. - pub fn imports(&self) -> &HashMap> { + pub fn imports(&self) -> &FxHashMap> { &self.imports } } @@ -274,7 +274,7 @@ pub fn validate_restricted_imports( Some(b) if !b.is_empty() => b, _ => return None, }; - let restricted: HashSet<&str> = blocklisted.iter().map(|s| s.as_str()).collect(); + let restricted: FxHashSet<&str> = blocklisted.iter().map(|s| s.as_str()).collect(); let mut error = CompilerError::new(); for stmt in &program.body { @@ -326,7 +326,7 @@ pub fn add_imports_to_program(program: &mut Program, context: &ProgramContext) { } // Collect existing non-namespaced imports by module name - let existing_import_indices: HashMap = program + let existing_import_indices: FxHashMap = program .body .iter() .enumerate() diff --git a/compiler/crates/react_compiler/src/entrypoint/pipeline.rs b/compiler/crates/react_compiler/src/entrypoint/pipeline.rs index 67bb397466f..97fc232ac2e 100644 --- a/compiler/crates/react_compiler/src/entrypoint/pipeline.rs +++ b/compiler/crates/react_compiler/src/entrypoint/pipeline.rs @@ -8,6 +8,7 @@ //! Analogous to TS `Pipeline.ts` (`compileFn` → `run` → `runWithEnvironment`). //! Currently runs BuildHIR (lowering) and PruneMaybeThrows. +use indexmap::IndexMap; use react_compiler_ast::scope::ScopeInfo; use react_compiler_diagnostics::CompilerError; use react_compiler_hir::ReactFunctionType; @@ -15,6 +16,7 @@ use react_compiler_hir::environment::Environment; use react_compiler_hir::environment::OutputMode; use react_compiler_hir::environment_config::EnvironmentConfig; use react_compiler_lowering::FunctionNode; +use rustc_hash::{FxBuildHasher, FxHashMap}; use super::compile_result::CodegenFunction; use super::compile_result::CompilerErrorDetailInfo; @@ -1229,25 +1231,23 @@ pub fn compile_outlined_fn( fn build_outlined_scope_info( func: &mut react_compiler_ast::statements::FunctionDeclaration, ) -> react_compiler_ast::scope::ScopeInfo { - use std::collections::HashMap; - use react_compiler_ast::scope::*; let mut pos: u32 = 1; // reserve 0 for the function itself func.base.start = Some(0); - let mut fn_bindings: HashMap = HashMap::new(); + let mut fn_bindings: FxHashMap = FxHashMap::default(); let mut bindings_list: Vec = Vec::new(); - let mut ref_to_binding: indexmap::IndexMap = indexmap::IndexMap::new(); + let mut ref_to_binding: IndexMap = IndexMap::default(); // Helper to add a binding let _add_binding = |name: &str, kind: BindingKind, p: u32, - fn_bindings: &mut HashMap, + fn_bindings: &mut FxHashMap, bindings_list: &mut Vec, - ref_to_binding: &mut indexmap::IndexMap| { + ref_to_binding: &mut IndexMap| { if fn_bindings.contains_key(name) { // Already exists, just add reference let bid = fn_bindings[name]; @@ -1296,7 +1296,7 @@ fn build_outlined_scope_info( id: ScopeId(0), parent: None, kind: ScopeKind::Program, - bindings: HashMap::new(), + bindings: FxHashMap::default(), }; let fn_scope = ScopeData { id: ScopeId(1), @@ -1305,21 +1305,21 @@ fn build_outlined_scope_info( bindings: fn_bindings, }; - let mut node_to_scope: HashMap = HashMap::new(); + let mut node_to_scope: FxHashMap = FxHashMap::default(); node_to_scope.insert(0, ScopeId(1)); // Mirror position maps into node-ID maps for outlined functions - let mut node_id_to_scope: HashMap = HashMap::new(); + let mut node_id_to_scope: FxHashMap = FxHashMap::default(); node_id_to_scope.insert(0, ScopeId(1)); - let ref_node_id_to_binding: indexmap::IndexMap = + let ref_node_id_to_binding: IndexMap = ref_to_binding.iter().map(|(&k, &v)| (k, v)).collect(); ScopeInfo { scopes: vec![program_scope, fn_scope], bindings: bindings_list, node_to_scope, - node_to_scope_end: HashMap::new(), - reference_to_binding: indexmap::IndexMap::new(), + node_to_scope_end: FxHashMap::default(), + reference_to_binding: IndexMap::default(), ref_node_id_to_binding, node_id_to_scope, program_scope: ScopeId(0), @@ -1331,9 +1331,9 @@ fn outlined_assign_pattern_positions( pattern: &mut react_compiler_ast::patterns::PatternLike, pos: &mut u32, kind: react_compiler_ast::scope::BindingKind, - fn_bindings: &mut std::collections::HashMap, + fn_bindings: &mut rustc_hash::FxHashMap, bindings_list: &mut Vec, - ref_to_binding: &mut indexmap::IndexMap, + ref_to_binding: &mut IndexMap, ) { use react_compiler_ast::patterns::PatternLike; use react_compiler_ast::scope::*; @@ -1432,9 +1432,9 @@ fn outlined_assign_pattern_positions( fn outlined_assign_stmt_positions( stmt: &mut react_compiler_ast::statements::Statement, pos: &mut u32, - fn_bindings: &mut std::collections::HashMap, + fn_bindings: &mut rustc_hash::FxHashMap, bindings_list: &mut Vec, - ref_to_binding: &mut indexmap::IndexMap, + ref_to_binding: &mut IndexMap, ) { use react_compiler_ast::statements::Statement; @@ -1477,8 +1477,8 @@ fn outlined_assign_stmt_positions( fn outlined_assign_expr_positions( expr: &mut react_compiler_ast::expressions::Expression, pos: &mut u32, - fn_bindings: &std::collections::HashMap, - ref_to_binding: &mut indexmap::IndexMap, + fn_bindings: &rustc_hash::FxHashMap, + ref_to_binding: &mut IndexMap, ) { use react_compiler_ast::expressions::*; @@ -1538,8 +1538,8 @@ fn outlined_assign_expr_positions( fn outlined_assign_jsx_name_positions( name: &mut react_compiler_ast::jsx::JSXElementName, pos: &mut u32, - fn_bindings: &std::collections::HashMap, - ref_to_binding: &mut indexmap::IndexMap, + fn_bindings: &rustc_hash::FxHashMap, + ref_to_binding: &mut IndexMap, ) { match name { react_compiler_ast::jsx::JSXElementName::JSXIdentifier(id) => { @@ -1561,8 +1561,8 @@ fn outlined_assign_jsx_name_positions( fn outlined_assign_jsx_member_positions( member: &mut react_compiler_ast::jsx::JSXMemberExpression, pos: &mut u32, - fn_bindings: &std::collections::HashMap, - ref_to_binding: &mut indexmap::IndexMap, + fn_bindings: &rustc_hash::FxHashMap, + ref_to_binding: &mut IndexMap, ) { match &mut *member.object { react_compiler_ast::jsx::JSXMemberExprObject::JSXIdentifier(id) => { @@ -1583,8 +1583,8 @@ fn outlined_assign_jsx_member_positions( fn outlined_assign_jsx_val_positions( val: &mut react_compiler_ast::jsx::JSXAttributeValue, pos: &mut u32, - fn_bindings: &std::collections::HashMap, - ref_to_binding: &mut indexmap::IndexMap, + fn_bindings: &rustc_hash::FxHashMap, + ref_to_binding: &mut IndexMap, ) { match val { react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(c) => { @@ -1608,8 +1608,8 @@ fn outlined_assign_jsx_val_positions( fn outlined_assign_jsx_child_positions( child: &mut react_compiler_ast::jsx::JSXChild, pos: &mut u32, - fn_bindings: &std::collections::HashMap, - ref_to_binding: &mut indexmap::IndexMap, + fn_bindings: &rustc_hash::FxHashMap, + ref_to_binding: &mut IndexMap, ) { match child { react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(c) => { diff --git a/compiler/crates/react_compiler/src/entrypoint/program.rs b/compiler/crates/react_compiler/src/entrypoint/program.rs index c01b7b90d7d..10b7b49a288 100644 --- a/compiler/crates/react_compiler/src/entrypoint/program.rs +++ b/compiler/crates/react_compiler/src/entrypoint/program.rs @@ -14,8 +14,7 @@ //! 5. Processing each function through the compilation pipeline //! 6. Applying compiled functions back to the AST -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_ast::File; use react_compiler_ast::Program; @@ -2084,9 +2083,9 @@ struct CompiledFnForReplacement { fn get_functions_referenced_before_declaration( program: &Program, compiled_fns: &[CompiledFnForReplacement], -) -> HashSet { +) -> FxHashSet { // Collect function names and their node_ids for compiled FunctionDeclarations - let mut fn_names: HashMap = HashMap::new(); + let mut fn_names: FxHashMap = FxHashMap::default(); for compiled in compiled_fns { if compiled.original_kind == OriginalFnKind::FunctionDeclaration { if let Some(ref name) = compiled.fn_name { @@ -2098,10 +2097,10 @@ fn get_functions_referenced_before_declaration( } if fn_names.is_empty() { - return HashSet::new(); + return FxHashSet::default(); } - let mut referenced_before_decl: HashSet = HashSet::new(); + let mut referenced_before_decl: FxHashSet = FxHashSet::default(); // Walk through program body in order. For each statement, check if it references // any of the function names before the function's declaration. @@ -2597,7 +2596,7 @@ fn apply_compiled_functions( let referenced_before_decl = if has_gating { get_functions_referenced_before_declaration(program, compiled_fns) } else { - HashSet::new() + FxHashSet::default() }; // For gated functions, we need to clone the original function expressions diff --git a/compiler/crates/react_compiler/src/entrypoint/validate_source_locations.rs b/compiler/crates/react_compiler/src/entrypoint/validate_source_locations.rs index 01fed078d31..137cd134a8e 100644 --- a/compiler/crates/react_compiler/src/entrypoint/validate_source_locations.rs +++ b/compiler/crates/react_compiler/src/entrypoint/validate_source_locations.rs @@ -11,7 +11,7 @@ //! //! Analogous to TS `ValidateSourceLocations.ts`. -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_ast::common::SourceLocation as AstSourceLocation; use react_compiler_ast::expressions::{ @@ -38,14 +38,14 @@ pub fn validate_source_locations( let important_original = collect_important_original_locations(func); // Step 2: Collect all locations from the generated AST - let mut generated = HashMap::>::new(); + let mut generated = FxHashMap::>::default(); collect_generated_from_block(&codegen.body.body, &mut generated); for outlined in &codegen.outlined { collect_generated_from_block(&outlined.func.body.body, &mut generated); } // Step 3: Validate that all important locations are preserved - let strict_node_types: HashSet<&str> = + let strict_node_types: FxHashSet<&str> = ["VariableDeclaration", "VariableDeclarator", "Identifier"] .into_iter() .collect(); @@ -101,7 +101,7 @@ pub fn validate_source_locations( struct ImportantLocation { key: String, loc: AstSourceLocation, - node_types: HashSet<&'static str>, + node_types: FxHashSet<&'static str>, } // ---- Location key ---- @@ -157,7 +157,7 @@ fn report_wrong_node_type( env: &mut Environment, loc: &AstSourceLocation, expected_type: &str, - actual_types: &HashSet, + actual_types: &FxHashSet, ) { let diag_loc = ast_to_diag_loc(loc); let mut actual: Vec<&str> = actual_types.iter().map(|s| s.as_str()).collect(); @@ -249,8 +249,8 @@ fn is_manual_memoization(expr: &Expression) -> bool { fn collect_important_original_locations( func: &FunctionNode<'_>, -) -> HashMap { - let mut locations = HashMap::new(); +) -> FxHashMap { + let mut locations = FxHashMap::default(); // Note: TS uses func.traverse() which visits DESCENDANTS only, not the root // function node itself. So we don't record the root function as important. @@ -294,14 +294,14 @@ fn collect_important_original_locations( fn record_important( node_type: &'static str, loc: &Option, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { if let Some(loc) = loc { let key = location_key(loc); if let Some(existing) = locations.get_mut(&key) { existing.node_types.insert(node_type); } else { - let mut node_types = HashSet::new(); + let mut node_types = FxHashSet::default(); node_types.insert(node_type); locations.insert( key.clone(), @@ -318,7 +318,7 @@ fn record_important( fn collect_original_block( stmts: &[Statement], in_single_return_arrow: bool, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { for stmt in stmts { collect_original_statement(stmt, in_single_return_arrow, locations); @@ -328,7 +328,7 @@ fn collect_original_block( fn collect_original_statement( stmt: &Statement, in_single_return_arrow: bool, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { // Record this statement if it's an important type if let Some(type_name) = important_statement_type(stmt) { @@ -476,7 +476,7 @@ fn collect_original_statement( fn collect_original_var_declaration( decl: &VariableDeclaration, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { for declarator in &decl.declarations { // VariableDeclarator is an important type @@ -490,7 +490,7 @@ fn collect_original_var_declaration( fn collect_original_expression( expr: &Expression, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { // Record this expression if it's an important type if let Some(type_name) = important_expression_type(expr) { @@ -667,7 +667,7 @@ fn collect_original_expression( fn collect_original_arrow_children( arrow: &ArrowFunctionExpression, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { for param in &arrow.params { collect_original_pattern(param, locations); @@ -685,7 +685,7 @@ fn collect_original_arrow_children( fn collect_original_fn_expr_children( func: &FunctionExpression, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { if let Some(id) = &func.id { record_important("Identifier", &id.base.loc, locations); @@ -698,7 +698,7 @@ fn collect_original_fn_expr_children( fn collect_original_pattern( pattern: &PatternLike, - locations: &mut HashMap, + locations: &mut FxHashMap, ) { match pattern { PatternLike::Identifier(id) => { @@ -852,7 +852,7 @@ fn expression_loc(expr: &Expression) -> &Option { fn collect_generated_from_block( stmts: &[Statement], - locations: &mut HashMap>, + locations: &mut FxHashMap>, ) { for stmt in stmts { collect_generated_statement(stmt, locations); @@ -862,7 +862,7 @@ fn collect_generated_from_block( fn record_generated( type_name: &str, loc: &Option, - locations: &mut HashMap>, + locations: &mut FxHashMap>, ) { if let Some(loc) = loc { let key = location_key(loc); @@ -873,7 +873,10 @@ fn record_generated( } } -fn collect_generated_statement(stmt: &Statement, locations: &mut HashMap>) { +fn collect_generated_statement( + stmt: &Statement, + locations: &mut FxHashMap>, +) { // Record this statement's location let type_name = statement_type_name(stmt); record_generated(type_name, statement_loc(stmt), locations); @@ -1008,7 +1011,7 @@ fn collect_generated_statement(stmt: &Statement, locations: &mut HashMap>, + locations: &mut FxHashMap>, ) { for declarator in &decl.declarations { record_generated("VariableDeclarator", &declarator.base.loc, locations); @@ -1021,7 +1024,7 @@ fn collect_generated_var_declaration( fn collect_generated_expression( expr: &Expression, - locations: &mut HashMap>, + locations: &mut FxHashMap>, ) { let type_name = expression_type_name(expr); record_generated(type_name, expression_loc(expr), locations); @@ -1188,7 +1191,7 @@ fn collect_generated_expression( fn collect_generated_pattern( pattern: &PatternLike, - locations: &mut HashMap>, + locations: &mut FxHashMap>, ) { match pattern { PatternLike::Identifier(id) => { diff --git a/compiler/crates/react_compiler_ast/Cargo.toml b/compiler/crates/react_compiler_ast/Cargo.toml index 49208522fb5..4a3e387f06e 100644 --- a/compiler/crates/react_compiler_ast/Cargo.toml +++ b/compiler/crates/react_compiler_ast/Cargo.toml @@ -9,6 +9,7 @@ serde = { version = "1", features = ["derive"] } serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } serde-transcode = "1" indexmap = { version = "2", features = ["serde"] } +rustc-hash = "2" [dev-dependencies] walkdir = "2" diff --git a/compiler/crates/react_compiler_ast/src/scope.rs b/compiler/crates/react_compiler_ast/src/scope.rs index a9543d0b6cc..28b3cec8df4 100644 --- a/compiler/crates/react_compiler_ast/src/scope.rs +++ b/compiler/crates/react_compiler_ast/src/scope.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rustc_hash::{FxBuildHasher, FxHashMap}; use indexmap::IndexMap; use serde::Deserialize; @@ -20,7 +20,7 @@ pub struct ScopeData { pub kind: ScopeKind, /// Bindings declared directly in this scope, keyed by name. /// Maps to BindingId for lookup in the binding table. - pub bindings: HashMap, + pub bindings: FxHashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -113,18 +113,18 @@ pub struct ScopeInfo { /// **NOT for identity lookups** — use `node_id_to_scope` (via `resolve_scope_for_node`) /// instead. Retained only for position-range containment queries /// (e.g., "is reference R inside function scope S?"). - pub node_to_scope: HashMap, + pub node_to_scope: FxHashMap, /// Maps an AST node's start offset to the node's end offset. /// Parallel to `node_to_scope` — used for position-range containment checks. - #[serde(default, skip_serializing_if = "HashMap::is_empty")] - pub node_to_scope_end: HashMap, + #[serde(default, skip_serializing_if = "FxHashMap::is_empty")] + pub node_to_scope_end: FxHashMap, /// **DEPRECATED** — retained only for Babel bridge JSON deserialization. /// All backends pass empty maps; only the Babel bridge populates this. /// Use `ref_node_id_to_binding` for all lookups and iteration. #[serde(default)] - pub reference_to_binding: IndexMap, + pub reference_to_binding: IndexMap, /// Maps an identifier reference's node-ID to the binding it resolves to. /// Only present for identifiers that resolve to a binding (not globals). @@ -134,15 +134,15 @@ pub struct ScopeInfo { skip_serializing_if = "IndexMap::is_empty", rename = "refNodeIdToBinding" )] - pub ref_node_id_to_binding: IndexMap, + pub ref_node_id_to_binding: IndexMap, /// Maps a scope-creating AST node's node-ID to the scope it creates. #[serde( default, - skip_serializing_if = "HashMap::is_empty", + skip_serializing_if = "FxHashMap::is_empty", rename = "nodeIdToScope" )] - pub node_id_to_scope: HashMap, + pub node_id_to_scope: FxHashMap, /// The program-level (module) scope. Always scopes[0]. pub program_scope: ScopeId, @@ -207,7 +207,7 @@ impl ScopeInfo { name: &str, ancestor: ScopeId, ) -> Option<&BindingData> { - let mut descendants = std::collections::HashSet::new(); + let mut descendants = rustc_hash::FxHashSet::default(); descendants.insert(ancestor); let mut changed = true; while changed { @@ -238,7 +238,7 @@ impl ScopeInfo { name: &str, ancestor: ScopeId, ) -> Option<(BindingId, &BindingData)> { - let mut descendants = std::collections::HashSet::new(); + let mut descendants = rustc_hash::FxHashSet::default(); descendants.insert(ancestor); let mut changed = true; while changed { @@ -306,7 +306,7 @@ impl ScopeInfo { ancestor: ScopeId, is_claimed: impl Fn(ScopeId) -> bool, ) -> Option { - let mut descendants = std::collections::HashSet::new(); + let mut descendants = rustc_hash::FxHashSet::default(); descendants.insert(ancestor); let mut changed = true; while changed { diff --git a/compiler/crates/react_compiler_diagnostics/Cargo.toml b/compiler/crates/react_compiler_diagnostics/Cargo.toml index 873843c5ac3..bc9a84721fc 100644 --- a/compiler/crates/react_compiler_diagnostics/Cargo.toml +++ b/compiler/crates/react_compiler_diagnostics/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +rustc-hash = "2" serde = { version = "1", features = ["derive"] } diff --git a/compiler/crates/react_compiler_diagnostics/src/code_frame.rs b/compiler/crates/react_compiler_diagnostics/src/code_frame.rs index 00bb2c495d4..83e9de714f4 100644 --- a/compiler/crates/react_compiler_diagnostics/src/code_frame.rs +++ b/compiler/crates/react_compiler_diagnostics/src/code_frame.rs @@ -158,8 +158,8 @@ pub fn code_frame_columns( let number_max_width = format!("{}", end).len(); // Build a lookup map for marker lines - let mut marker_map: std::collections::HashMap = - std::collections::HashMap::new(); + let mut marker_map: rustc_hash::FxHashMap = + rustc_hash::FxHashMap::default(); let line_diff = end_line as usize - start_line as usize; for (line_number, entry) in marker_lines_raw { // Resolve placeholder lengths using actual source lines diff --git a/compiler/crates/react_compiler_hir/Cargo.toml b/compiler/crates/react_compiler_hir/Cargo.toml index b410995f125..8273eaa778b 100644 --- a/compiler/crates/react_compiler_hir/Cargo.toml +++ b/compiler/crates/react_compiler_hir/Cargo.toml @@ -6,5 +6,6 @@ edition = "2024" [dependencies] react_compiler_diagnostics = { path = "../react_compiler_diagnostics" } indexmap = { version = "2", features = ["serde"] } +rustc-hash = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/compiler/crates/react_compiler_hir/src/default_module_type_provider.rs b/compiler/crates/react_compiler_hir/src/default_module_type_provider.rs index 282daa17724..38668a8bc3e 100644 --- a/compiler/crates/react_compiler_hir/src/default_module_type_provider.rs +++ b/compiler/crates/react_compiler_hir/src/default_module_type_provider.rs @@ -20,11 +20,11 @@ use crate::type_config::{ pub fn default_module_type_provider(module_name: &str) -> Option { match module_name { "react-hook-form" => Some(TypeConfig::Object(ObjectTypeConfig { - properties: Some(IndexMap::from([( + properties: Some(IndexMap::from_iter([( "useForm".to_string(), TypeConfig::Hook(HookTypeConfig { return_type: Box::new(TypeConfig::Object(ObjectTypeConfig { - properties: Some(IndexMap::from([( + properties: Some(IndexMap::from_iter([( "watch".to_string(), TypeConfig::Function(FunctionTypeConfig { positional_params: Vec::new(), @@ -58,7 +58,7 @@ pub fn default_module_type_provider(module_name: &str) -> Option { })), "@tanstack/react-table" => Some(TypeConfig::Object(ObjectTypeConfig { - properties: Some(IndexMap::from([( + properties: Some(IndexMap::from_iter([( "useReactTable".to_string(), TypeConfig::Hook(HookTypeConfig { positional_params: Some(Vec::new()), @@ -77,7 +77,7 @@ pub fn default_module_type_provider(module_name: &str) -> Option { })), "@tanstack/react-virtual" => Some(TypeConfig::Object(ObjectTypeConfig { - properties: Some(IndexMap::from([( + properties: Some(IndexMap::from_iter([( "useVirtualizer".to_string(), TypeConfig::Hook(HookTypeConfig { positional_params: Some(Vec::new()), diff --git a/compiler/crates/react_compiler_hir/src/dominator.rs b/compiler/crates/react_compiler_hir/src/dominator.rs index 9aaf6e9d517..8e8595038ab 100644 --- a/compiler/crates/react_compiler_hir/src/dominator.rs +++ b/compiler/crates/react_compiler_hir/src/dominator.rs @@ -9,7 +9,7 @@ //! Uses the Cooper/Harvey/Kennedy algorithm from //! https://www.cs.rice.edu/~keith/Embed/dom.pdf -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory}; @@ -24,7 +24,7 @@ use crate::{BlockId, HirFunction, Terminal}; pub struct PostDominator { /// The exit node (synthetic node representing function exit). pub exit: BlockId, - nodes: HashMap, + nodes: FxHashMap, } impl PostDominator { @@ -50,8 +50,8 @@ impl PostDominator { struct Node { id: BlockId, index: usize, - preds: HashSet, - succs: HashSet, + preds: FxHashSet, + succs: FxHashSet, } struct Graph { @@ -59,7 +59,7 @@ struct Graph { /// Nodes stored in iteration order (RPO for reverse graph). nodes: Vec, /// Map from BlockId to index in the nodes vec. - node_index: HashMap, + node_index: FxHashMap, } impl Graph { @@ -112,7 +112,7 @@ fn build_reverse_graph( let exit_id = BlockId(next_block_id_counter); // Build initial nodes with reversed edges - let mut raw_nodes: HashMap = HashMap::new(); + let mut raw_nodes: FxHashMap = FxHashMap::default(); // Create exit node raw_nodes.insert( @@ -120,15 +120,15 @@ fn build_reverse_graph( Node { id: exit_id, index: 0, - preds: HashSet::new(), - succs: HashSet::new(), + preds: FxHashSet::default(), + succs: FxHashSet::default(), }, ); for (id, block) in &func.body.blocks { let successors = each_terminal_successor(&block.terminal); - let mut preds_set: HashSet = successors.into_iter().collect(); - let succs_set: HashSet = block.preds.iter().copied().collect(); + let mut preds_set: FxHashSet = successors.into_iter().collect(); + let succs_set: FxHashSet = block.preds.iter().copied().collect(); let is_return = matches!(&block.terminal, Terminal::Return { .. }); let is_throw = matches!(&block.terminal, Terminal::Throw { .. }); @@ -150,7 +150,7 @@ fn build_reverse_graph( } // DFS from exit to compute RPO - let mut visited = HashSet::new(); + let mut visited = FxHashSet::default(); let mut postorder = Vec::new(); dfs_postorder(exit_id, &raw_nodes, &mut visited, &mut postorder); @@ -158,7 +158,7 @@ fn build_reverse_graph( postorder.reverse(); let mut nodes = Vec::with_capacity(postorder.len()); - let mut node_index = HashMap::new(); + let mut node_index = FxHashMap::default(); for (idx, id) in postorder.into_iter().enumerate() { let mut node = raw_nodes.remove(&id).unwrap(); node.index = idx; @@ -175,8 +175,8 @@ fn build_reverse_graph( fn dfs_postorder( id: BlockId, - nodes: &HashMap, - visited: &mut HashSet, + nodes: &FxHashMap, + visited: &mut FxHashSet, postorder: &mut Vec, ) { if !visited.insert(id) { @@ -196,8 +196,8 @@ fn dfs_postorder( fn compute_immediate_dominators( graph: &Graph, -) -> Result, CompilerDiagnostic> { - let mut doms: HashMap = HashMap::new(); +) -> Result, CompilerDiagnostic> { + let mut doms: FxHashMap = FxHashMap::default(); doms.insert(graph.entry, graph.entry); let mut changed = true; @@ -249,7 +249,7 @@ fn compute_immediate_dominators( Ok(doms) } -fn intersect(a: BlockId, b: BlockId, graph: &Graph, doms: &HashMap) -> BlockId { +fn intersect(a: BlockId, b: BlockId, graph: &Graph, doms: &FxHashMap) -> BlockId { let mut block1 = graph.get_node(a); let mut block2 = graph.get_node(b); while block1.id != block2.id { @@ -277,10 +277,10 @@ pub fn post_dominator_frontier( func: &HirFunction, post_dominators: &PostDominator, target_id: BlockId, -) -> HashSet { +) -> FxHashSet { let target_post_dominators = post_dominators_of(func, post_dominators, target_id); - let mut visited = HashSet::new(); - let mut frontier = HashSet::new(); + let mut visited = FxHashSet::default(); + let mut frontier = FxHashSet::default(); let mut to_visit: Vec = target_post_dominators.iter().copied().collect(); to_visit.push(target_id); @@ -305,9 +305,9 @@ pub fn post_dominators_of( func: &HirFunction, post_dominators: &PostDominator, target_id: BlockId, -) -> HashSet { - let mut result = HashSet::new(); - let mut visited = HashSet::new(); +) -> FxHashSet { + let mut result = FxHashSet::default(); + let mut visited = FxHashSet::default(); let mut queue = vec![target_id]; while let Some(current_id) = queue.pop() { @@ -339,8 +339,8 @@ pub fn post_dominators_of( pub fn compute_unconditional_blocks( func: &HirFunction, next_block_id_counter: u32, -) -> Result, CompilerDiagnostic> { - let mut unconditional = HashSet::new(); +) -> Result, CompilerDiagnostic> { + let mut unconditional = FxHashSet::default(); let dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?; let exit = dominators.exit; let mut current: Option = Some(func.body.entry); diff --git a/compiler/crates/react_compiler_hir/src/environment.rs b/compiler/crates/react_compiler_hir/src/environment.rs index e1257b65fe5..735da67bb2a 100644 --- a/compiler/crates/react_compiler_hir/src/environment.rs +++ b/compiler/crates/react_compiler_hir/src/environment.rs @@ -1,5 +1,4 @@ -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::CompilerDiagnostic; use react_compiler_diagnostics::CompilerError; @@ -80,12 +79,12 @@ pub struct Environment { // Used by codegen to filter type annotation renames — only rename identifiers // whose node_id is in this set (type labels like ObjectTypeIndexer params // are NOT in this set and should keep their original names). - pub reference_node_ids: HashSet, + pub reference_node_ids: FxHashSet, // Hoisted identifiers: tracks which bindings have already been hoisted // via DeclareContext to avoid duplicate hoisting. // Uses u32 to avoid depending on react_compiler_ast types. - hoisted_identifiers: HashSet, + hoisted_identifiers: FxHashSet, // Config flags for validation passes (kept for backwards compat with existing pipeline code) pub validate_preserve_existing_memoization_guarantees: bool, @@ -95,8 +94,8 @@ pub struct Environment { // Type system registries globals: GlobalRegistry, pub shapes: ShapeRegistry, - module_types: HashMap>, - module_type_errors: HashMap>, + module_types: FxHashMap>, + module_type_errors: FxHashMap>, // Environment configuration (feature flags, custom hooks, etc.) pub config: EnvironmentConfig, @@ -111,7 +110,7 @@ pub struct Environment { // Known names for collision-aware UID generation. Lazily populated from // identifiers on first use, then updated with each generated name. // Matches Babel's generateUid behavior of checking hasBinding/hasReference. - uid_known_names: Option>, + uid_known_names: Option>, } /// An outlined function entry, stored on Environment during compilation. @@ -164,7 +163,7 @@ impl Environment { } // Register reanimated module type when enabled - let mut module_types: HashMap> = HashMap::new(); + let mut module_types: FxHashMap> = FxHashMap::default(); if config.enable_custom_type_definition_for_reanimated { let reanimated_module_type = globals::get_reanimated_module_type(&mut shapes); module_types.insert( @@ -190,8 +189,8 @@ impl Environment { instrument_gating_name: None, hook_guard_name: None, renames: Vec::new(), - reference_node_ids: HashSet::new(), - hoisted_identifiers: HashSet::new(), + reference_node_ids: FxHashSet::default(), + hoisted_identifiers: FxHashSet::default(), validate_preserve_existing_memoization_guarantees: config .validate_preserve_existing_memoization_guarantees, validate_no_set_state_in_render: config.validate_no_set_state_in_render, @@ -200,7 +199,7 @@ impl Environment { globals: global_registry, shapes, module_types, - module_type_errors: HashMap::new(), + module_type_errors: FxHashMap::default(), default_nonmutating_hook: None, default_mutating_hook: None, outlined_functions: Vec::new(), @@ -237,8 +236,8 @@ impl Environment { instrument_gating_name: self.instrument_gating_name.clone(), hook_guard_name: self.hook_guard_name.clone(), renames: Vec::new(), - reference_node_ids: HashSet::new(), - hoisted_identifiers: HashSet::new(), + reference_node_ids: FxHashSet::default(), + hoisted_identifiers: FxHashSet::default(), validate_preserve_existing_memoization_guarantees: self .validate_preserve_existing_memoization_guarantees, validate_no_set_state_in_render: self.validate_no_set_state_in_render, @@ -897,7 +896,7 @@ impl Environment { // Lazily build the set of known names from existing identifiers. // This approximates Babel's hasBinding/hasGlobal/hasReference checks. if self.uid_known_names.is_none() { - let mut known = HashSet::new(); + let mut known = FxHashSet::default(); for id in &self.identifiers { if let Some(name) = &id.name { known.insert(name.value().to_string()); @@ -929,7 +928,7 @@ impl Environment { /// Seed the UID known names set with external names (e.g. from ProgramContext). /// This ensures UID generation avoids names generated by previous function compilations, /// matching Babel's behavior where the program scope accumulates all generated UIDs. - pub fn seed_uid_known_names(&mut self, names: &HashSet) { + pub fn seed_uid_known_names(&mut self, names: &FxHashSet) { match &mut self.uid_known_names { Some(existing) => existing.extend(names.iter().cloned()), None => self.uid_known_names = Some(names.clone()), @@ -937,7 +936,7 @@ impl Environment { } /// Return the UID known names accumulated during this compilation. - pub fn take_uid_known_names(&mut self) -> Option> { + pub fn take_uid_known_names(&mut self) -> Option> { self.uid_known_names.take() } diff --git a/compiler/crates/react_compiler_hir/src/environment_config.rs b/compiler/crates/react_compiler_hir/src/environment_config.rs index 042802ac765..77a87d122ff 100644 --- a/compiler/crates/react_compiler_hir/src/environment_config.rs +++ b/compiler/crates/react_compiler_hir/src/environment_config.rs @@ -7,7 +7,8 @@ //! //! Contains feature flags and custom hook definitions that control compiler behavior. -use std::collections::HashMap; +use indexmap::IndexMap; +use rustc_hash::{FxBuildHasher, FxHashMap}; use serde::{Deserialize, Serialize}; @@ -80,12 +81,12 @@ fn default_true() -> bool { pub struct EnvironmentConfig { /// Custom hook type definitions, keyed by hook name. #[serde(default)] - pub custom_hooks: HashMap, + pub custom_hooks: FxHashMap, /// Pre-resolved module type provider results. /// Map from module name to TypeConfig, computed by the JS shim. #[serde(default)] - pub module_type_provider: Option>, + pub module_type_provider: Option>, /// Custom macro-like function names that should have their operands /// memoized in the same scope (similar to fbt). @@ -185,7 +186,7 @@ pub struct EnvironmentConfig { impl Default for EnvironmentConfig { fn default() -> Self { Self { - custom_hooks: HashMap::new(), + custom_hooks: FxHashMap::default(), enable_reset_cache_on_source_file_changes: None, module_type_provider: None, enable_preserve_existing_memoization_guarantees: true, diff --git a/compiler/crates/react_compiler_hir/src/globals.rs b/compiler/crates/react_compiler_hir/src/globals.rs index ae93255ff43..3abaa76e954 100644 --- a/compiler/crates/react_compiler_hir/src/globals.rs +++ b/compiler/crates/react_compiler_hir/src/globals.rs @@ -8,7 +8,7 @@ //! Provides `DEFAULT_SHAPES` (built-in object shapes) and `DEFAULT_GLOBALS` //! (global variable types including React hooks and JS built-ins). -use std::collections::HashMap; +use rustc_hash::FxHashMap; use std::sync::LazyLock; use crate::Effect; @@ -31,14 +31,14 @@ pub type Global = Type; /// Registry mapping global names to their types. /// /// Supports two modes: -/// - **Builder mode** (`base=None`): wraps a single HashMap, used during +/// - **Builder mode** (`base=None`): wraps a single FxHashMap, used during /// `build_default_globals` to construct the static base. -/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small -/// extras HashMap. Lookups check extras first, then base. Inserts go into extras. +/// - **Overlay mode** (`base=Some`): holds a `&'static FxHashMap` base plus a small +/// extras FxHashMap. Lookups check extras first, then base. Inserts go into extras. /// Cloning only copies the extras map (the base pointer is shared). pub struct GlobalRegistry { - base: Option<&'static HashMap>, - entries: HashMap, + base: Option<&'static FxHashMap>, + entries: FxHashMap, } impl GlobalRegistry { @@ -46,15 +46,15 @@ impl GlobalRegistry { pub fn new() -> Self { Self { base: None, - entries: HashMap::new(), + entries: FxHashMap::default(), } } /// Create an overlay-mode registry backed by a static base. - pub fn with_base(base: &'static HashMap) -> Self { + pub fn with_base(base: &'static FxHashMap) -> Self { Self { base: Some(base), - entries: HashMap::new(), + entries: FxHashMap::default(), } } @@ -83,9 +83,9 @@ impl GlobalRegistry { self.entries.keys().chain(base_keys) } - /// Consume the registry and return the inner HashMap. + /// Consume the registry and return the inner FxHashMap. /// Only valid in builder mode (no base). - pub fn into_inner(self) -> HashMap { + pub fn into_inner(self) -> FxHashMap { debug_assert!( self.base.is_none(), "into_inner() called on overlay-mode GlobalRegistry" @@ -108,8 +108,8 @@ impl Clone for GlobalRegistry { // ============================================================================= struct BaseRegistries { - shapes: HashMap, - globals: HashMap, + shapes: FxHashMap, + globals: FxHashMap, } static BASE: LazyLock = LazyLock::new(|| { @@ -122,12 +122,12 @@ static BASE: LazyLock = LazyLock::new(|| { }); /// Get a reference to the static base shapes registry. -pub fn base_shapes() -> &'static HashMap { +pub fn base_shapes() -> &'static FxHashMap { &BASE.shapes } /// Get a reference to the static base globals registry. -pub fn base_globals() -> &'static HashMap { +pub fn base_globals() -> &'static FxHashMap { &BASE.globals } @@ -1332,7 +1332,7 @@ fn build_object_shape(shapes: &mut ShapeRegistry) { None, false, ); - let mut mixed_props = HashMap::new(); + let mut mixed_props = FxHashMap::default(); mixed_props.insert("toString".to_string(), mixed_to_string); mixed_props.insert("indexOf".to_string(), mixed_index_of); mixed_props.insert("includes".to_string(), mixed_includes); diff --git a/compiler/crates/react_compiler_hir/src/lib.rs b/compiler/crates/react_compiler_hir/src/lib.rs index 07db0772561..f20d55da1e9 100644 --- a/compiler/crates/react_compiler_hir/src/lib.rs +++ b/compiler/crates/react_compiler_hir/src/lib.rs @@ -9,14 +9,14 @@ pub mod reactive; pub mod type_config; pub mod visitors; -use indexmap::IndexMap; -use indexmap::IndexSet; +use indexmap::{IndexMap, IndexSet}; pub use react_compiler_diagnostics::CompilerDiagnostic; pub use react_compiler_diagnostics::ErrorCategory; pub use react_compiler_diagnostics::GENERATED_SOURCE; pub use react_compiler_diagnostics::Position; pub use react_compiler_diagnostics::SourceLocation; pub use reactive::*; +use rustc_hash::FxBuildHasher; // ============================================================================= // ID newtypes @@ -57,7 +57,7 @@ pub struct MutableRangeId(pub u32); // ============================================================================= /// Wrapper around f64 that stores raw bytes for deterministic equality and hashing. -/// This allows use in HashMap keys and ensures NaN == NaN (bitwise comparison). +/// This allows use in FxHashMap keys and ensures NaN == NaN (bitwise comparison). #[derive(Debug, Clone, Copy)] pub struct FloatValue(u64); @@ -190,7 +190,7 @@ pub enum ParamPattern { #[derive(Debug, Clone)] pub struct HIR { pub entry: BlockId, - pub blocks: IndexMap, + pub blocks: IndexMap, } /// Block kinds @@ -222,7 +222,7 @@ pub struct BasicBlock { pub id: BlockId, pub instructions: Vec, pub terminal: Terminal, - pub preds: IndexSet, + pub preds: IndexSet, pub phis: Vec, } @@ -230,7 +230,7 @@ pub struct BasicBlock { #[derive(Debug, Clone)] pub struct Phi { pub place: Place, - pub operands: IndexMap, + pub operands: IndexMap, } // ============================================================================= diff --git a/compiler/crates/react_compiler_hir/src/object_shape.rs b/compiler/crates/react_compiler_hir/src/object_shape.rs index 3ef536190f7..fb8cebcdf64 100644 --- a/compiler/crates/react_compiler_hir/src/object_shape.rs +++ b/compiler/crates/react_compiler_hir/src/object_shape.rs @@ -8,7 +8,7 @@ //! Defines the shape registry used by Environment to resolve property types //! and function call signatures for built-in objects, hooks, and user-defined types. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use crate::Effect; use crate::Type; @@ -119,21 +119,21 @@ pub struct FunctionSignature { /// Ported from TS `ObjectShape`. #[derive(Debug, Clone)] pub struct ObjectShape { - pub properties: HashMap, + pub properties: FxHashMap, pub function_type: Option, } /// Registry mapping shape IDs to their ObjectShape definitions. /// /// Supports two modes: -/// - **Builder mode** (`base=None`): wraps a single HashMap, used during +/// - **Builder mode** (`base=None`): wraps a single FxHashMap, used during /// `build_builtin_shapes` / `build_default_globals` to construct the static base. -/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small -/// extras HashMap. Lookups check extras first, then base. Inserts go into extras. +/// - **Overlay mode** (`base=Some`): holds a `&'static FxHashMap` base plus a small +/// extras FxHashMap. Lookups check extras first, then base. Inserts go into extras. /// Cloning only copies the extras map (the base pointer is shared). pub struct ShapeRegistry { - base: Option<&'static HashMap>, - entries: HashMap, + base: Option<&'static FxHashMap>, + entries: FxHashMap, } impl ShapeRegistry { @@ -141,15 +141,15 @@ impl ShapeRegistry { pub fn new() -> Self { Self { base: None, - entries: HashMap::new(), + entries: FxHashMap::default(), } } /// Create an overlay-mode registry backed by a static base. - pub fn with_base(base: &'static HashMap) -> Self { + pub fn with_base(base: &'static FxHashMap) -> Self { Self { base: Some(base), - entries: HashMap::new(), + entries: FxHashMap::default(), } } @@ -163,9 +163,9 @@ impl ShapeRegistry { self.entries.insert(key, value); } - /// Consume the registry and return the inner HashMap. + /// Consume the registry and return the inner FxHashMap. /// Only valid in builder mode (no base). - pub fn into_inner(self) -> HashMap { + pub fn into_inner(self) -> FxHashMap { debug_assert!( self.base.is_none(), "into_inner() called on overlay-mode ShapeRegistry" diff --git a/compiler/crates/react_compiler_hir/src/print.rs b/compiler/crates/react_compiler_hir/src/print.rs index 989efa91b78..7855c1bd644 100644 --- a/compiler/crates/react_compiler_hir/src/print.rs +++ b/compiler/crates/react_compiler_hir/src/print.rs @@ -8,7 +8,7 @@ //! It also exports standalone formatting functions (format_loc, format_primitive, etc.) //! that require no state. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_diagnostics::CompilerError; use react_compiler_diagnostics::CompilerErrorOrDiagnostic; @@ -223,8 +223,8 @@ pub fn format_value_reason(reason: ValueReason) -> &'static str { /// like Places, Identifiers, Scopes, Types, InstructionValues, etc. pub struct PrintFormatter<'a> { pub env: &'a Environment, - pub seen_identifiers: HashSet, - pub seen_scopes: HashSet, + pub seen_identifiers: FxHashSet, + pub seen_scopes: FxHashSet, pub output: Vec, pub indent_level: usize, } @@ -233,8 +233,8 @@ impl<'a> PrintFormatter<'a> { pub fn new(env: &'a Environment) -> Self { Self { env, - seen_identifiers: HashSet::new(), - seen_scopes: HashSet::new(), + seen_identifiers: FxHashSet::default(), + seen_scopes: FxHashSet::default(), output: Vec::new(), indent_level: 0, } diff --git a/compiler/crates/react_compiler_hir/src/type_config.rs b/compiler/crates/react_compiler_hir/src/type_config.rs index 06554b82ff5..e8c4a9b851c 100644 --- a/compiler/crates/react_compiler_hir/src/type_config.rs +++ b/compiler/crates/react_compiler_hir/src/type_config.rs @@ -9,6 +9,7 @@ //! and `installTypeConfig` to describe module/function/hook types. use indexmap::IndexMap; +use rustc_hash::FxBuildHasher; use crate::Effect; @@ -166,7 +167,7 @@ pub enum TypeConfig { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ObjectTypeConfig { - pub properties: Option>, + pub properties: Option>, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/compiler/crates/react_compiler_hir/src/visitors.rs b/compiler/crates/react_compiler_hir/src/visitors.rs index 993710b198c..4ec1344159e 100644 --- a/compiler/crates/react_compiler_hir/src/visitors.rs +++ b/compiler/crates/react_compiler_hir/src/visitors.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -use std::collections::HashMap; +use rustc_hash::FxHashMap; use crate::environment::Environment; use crate::{ @@ -1289,14 +1289,14 @@ pub struct ScopeBlockTraversal { /// Live stack of active scopes active_scopes: Vec, /// Map from block ID to scope block info - pub block_infos: HashMap, + pub block_infos: FxHashMap, } impl ScopeBlockTraversal { pub fn new() -> Self { ScopeBlockTraversal { active_scopes: Vec::new(), - block_infos: HashMap::new(), + block_infos: FxHashMap::default(), } } diff --git a/compiler/crates/react_compiler_inference/Cargo.toml b/compiler/crates/react_compiler_inference/Cargo.toml index b69182a3ac0..4fb7504e674 100644 --- a/compiler/crates/react_compiler_inference/Cargo.toml +++ b/compiler/crates/react_compiler_inference/Cargo.toml @@ -11,3 +11,4 @@ react_compiler_optimization = { path = "../react_compiler_optimization" } react_compiler_ssa = { path = "../react_compiler_ssa" } react_compiler_utils = { path = "../react_compiler_utils" } indexmap = "2" +rustc-hash = "2" diff --git a/compiler/crates/react_compiler_inference/src/align_method_call_scopes.rs b/compiler/crates/react_compiler_inference/src/align_method_call_scopes.rs index afdad42f877..d782c7b2cd7 100644 --- a/compiler/crates/react_compiler_inference/src/align_method_call_scopes.rs +++ b/compiler/crates/react_compiler_inference/src/align_method_call_scopes.rs @@ -9,7 +9,7 @@ //! //! Ported from TypeScript `src/ReactiveScopes/AlignMethodCallScopes.ts`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_hir::environment::Environment; use react_compiler_hir::{EvaluationOrder, HirFunction, IdentifierId, InstructionValue, ScopeId}; @@ -25,7 +25,7 @@ use react_compiler_utils::DisjointSet; /// Corresponds to TS `alignMethodCallScopes(fn: HIRFunction): void`. pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) { // Maps an identifier to the scope it should be assigned to (or None to remove scope) - let mut scope_mapping: HashMap> = HashMap::new(); + let mut scope_mapping: FxHashMap> = FxHashMap::default(); let mut merged_scopes = DisjointSet::::new(); // Phase 1: Walk instructions and collect scope relationships @@ -74,9 +74,10 @@ pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) { } // Phase 2: Merge scope ranges for unioned scopes. - // Use a HashMap to accumulate min/max across all scopes mapping to the same root, + // Use a FxHashMap to accumulate min/max across all scopes mapping to the same root, // matching TS behavior where root.range is updated in-place during iteration. - let mut range_updates: HashMap = HashMap::new(); + let mut range_updates: FxHashMap = + FxHashMap::default(); merged_scopes.for_each(|scope_id, root_id| { if scope_id == root_id { @@ -93,7 +94,7 @@ pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) { }); // Save original scope range IDs before updating - let original_range_ids: HashMap = range_updates + let original_range_ids: FxHashMap = range_updates .keys() .map(|&root_id| { let range_id = env.scopes[root_id.0 as usize].range.id; diff --git a/compiler/crates/react_compiler_inference/src/align_object_method_scopes.rs b/compiler/crates/react_compiler_inference/src/align_object_method_scopes.rs index 9f32b9d3954..5bcd8f73e2a 100644 --- a/compiler/crates/react_compiler_inference/src/align_object_method_scopes.rs +++ b/compiler/crates/react_compiler_inference/src/align_object_method_scopes.rs @@ -9,8 +9,8 @@ //! //! Ported from TypeScript `src/ReactiveScopes/AlignObjectMethodScopes.ts`. +use rustc_hash::{FxHashMap, FxHashSet}; use std::cmp; -use std::collections::{HashMap, HashSet}; use react_compiler_hir::environment::Environment; use react_compiler_hir::{ @@ -26,7 +26,7 @@ use react_compiler_utils::DisjointSet; /// instructions whose operands reference those methods. Returns a disjoint set /// of scopes that must be merged. fn find_scopes_to_merge(func: &HirFunction, env: &Environment) -> DisjointSet { - let mut object_method_decls: HashSet = HashSet::new(); + let mut object_method_decls: FxHashSet = FxHashSet::default(); let mut merged_scopes = DisjointSet::::new(); for (_block_id, block) in &func.body.blocks { @@ -99,9 +99,10 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment) let mut merged_scopes = find_scopes_to_merge(func, env); // Step 1: Merge affected scopes to their canonical root. - // Use a HashMap to accumulate min/max across all scopes mapping to the same root, + // Use a FxHashMap to accumulate min/max across all scopes mapping to the same root, // matching TS behavior where root.range is updated in-place during iteration. - let mut range_updates: HashMap = HashMap::new(); + let mut range_updates: FxHashMap = + FxHashMap::default(); merged_scopes.for_each(|scope_id, root_id| { if scope_id == root_id { @@ -118,7 +119,7 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment) }); // Save original scope range IDs before updating - let original_range_ids: HashMap = range_updates + let original_range_ids: FxHashMap = range_updates .keys() .map(|&root_id| { let range_id = env.scopes[root_id.0 as usize].range.id; @@ -147,7 +148,7 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment) // Step 2: Repoint identifiers whose scopes were merged // Build a map from old scope -> root scope for quick lookup - let mut scope_remap: HashMap = HashMap::new(); + let mut scope_remap: FxHashMap = FxHashMap::default(); merged_scopes.for_each(|scope_id, root_id| { if scope_id != root_id { scope_remap.insert(scope_id, root_id); diff --git a/compiler/crates/react_compiler_inference/src/align_reactive_scopes_to_block_scopes_hir.rs b/compiler/crates/react_compiler_inference/src/align_reactive_scopes_to_block_scopes_hir.rs index 141f92f9034..9da085a2962 100644 --- a/compiler/crates/react_compiler_inference/src/align_reactive_scopes_to_block_scopes_hir.rs +++ b/compiler/crates/react_compiler_inference/src/align_reactive_scopes_to_block_scopes_hir.rs @@ -22,8 +22,7 @@ //! instructions in each scope, the scopes must be aligned to block-scope //! boundaries — we can't memoize half of a loop! -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::BlockId; use react_compiler_hir::BlockKind; @@ -99,9 +98,9 @@ pub fn align_reactive_scopes_to_block_scopes_hir(func: &mut HirFunction, env: &m env.scopes.iter().map(|s| s.range.clone()).collect(); let mut active_block_fallthrough_ranges: Vec = Vec::new(); - let mut active_scopes: HashSet = HashSet::new(); - let mut seen: HashSet = HashSet::new(); - let mut value_block_nodes: HashMap = HashMap::new(); + let mut active_scopes: FxHashSet = FxHashSet::default(); + let mut seen: FxHashSet = FxHashSet::default(); + let mut value_block_nodes: FxHashMap = FxHashMap::default(); let block_ids: Vec = func.body.blocks.keys().copied().collect(); @@ -301,8 +300,8 @@ fn record_place_id( identifier_id: IdentifierId, node: &Option, env: &mut Environment, - active_scopes: &mut HashSet, - seen: &mut HashSet, + active_scopes: &mut FxHashSet, + seen: &mut FxHashSet, ) { // Get the scope for this identifier, if active at this instruction let scope_id = match env.identifiers[identifier_id.0 as usize].scope { diff --git a/compiler/crates/react_compiler_inference/src/analyse_functions.rs b/compiler/crates/react_compiler_inference/src/analyse_functions.rs index 139a8d98cf8..5bd23bb4f82 100644 --- a/compiler/crates/react_compiler_inference/src/analyse_functions.rs +++ b/compiler/crates/react_compiler_inference/src/analyse_functions.rs @@ -15,7 +15,7 @@ use indexmap::IndexMap; use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory}; use react_compiler_hir::environment::Environment; -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ AliasingEffect, BlockId, Effect, EvaluationOrder, FunctionId, HIR, HirFunction, IdentifierId, @@ -138,7 +138,7 @@ where // Phase 2: Populate the Effect of each context variable to use in inferring // the outer function. Corresponds to TS Phase 2 in lowerWithMutationAliasing. - let mut captured_or_mutated: HashSet = HashSet::new(); + let mut captured_or_mutated: FxHashSet = FxHashSet::default(); for effect in &function_effects { match effect { AliasingEffect::Assign { from, .. } @@ -208,7 +208,7 @@ fn placeholder_function() -> HirFunction { context: Vec::new(), body: HIR { entry: BlockId(0), - blocks: IndexMap::new(), + blocks: IndexMap::default(), }, instructions: Vec::new(), generator: false, diff --git a/compiler/crates/react_compiler_inference/src/build_reactive_scope_terminals_hir.rs b/compiler/crates/react_compiler_inference/src/build_reactive_scope_terminals_hir.rs index 7c992057b5f..b288cdc2658 100644 --- a/compiler/crates/react_compiler_inference/src/build_reactive_scope_terminals_hir.rs +++ b/compiler/crates/react_compiler_inference/src/build_reactive_scope_terminals_hir.rs @@ -11,10 +11,9 @@ //! //! Ported from TypeScript `src/HIR/BuildReactiveScopeTerminalsHIR.ts`. -use std::collections::HashMap; -use std::collections::HashSet; +use indexmap::{IndexMap, IndexSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; -use indexmap::IndexMap; use react_compiler_hir::BasicBlock; use react_compiler_hir::BlockId; use react_compiler_hir::EvaluationOrder; @@ -38,7 +37,7 @@ use react_compiler_lowering::mark_predecessors; /// Collect all unique scopes from places in the function that have non-empty ranges. /// Corresponds to TS `getScopes(fn)`. fn get_scopes(func: &HirFunction, env: &Environment) -> Vec { - let mut scope_ids: HashSet = HashSet::new(); + let mut scope_ids: FxHashSet = FxHashSet::default(); let mut visit_place = |identifier_id: IdentifierId| { if let Some(scope_id) = env.identifiers[identifier_id.0 as usize].scope { @@ -117,7 +116,7 @@ fn collect_scope_rewrites(func: &HirFunction, env: &mut Environment) -> Vec = Vec::new(); - let mut fallthroughs: HashMap = HashMap::new(); + let mut fallthroughs: FxHashMap = FxHashMap::default(); let mut active_items: Vec = Vec::new(); for i in 0..items.len() { @@ -222,7 +221,7 @@ fn handle_rewrite( }; let curr_block_id = context.next_block_id; - let mut preds = indexmap::IndexSet::new(); + let mut preds = IndexSet::default(); for &p in &context.next_preds { preds.insert(p); } @@ -264,8 +263,8 @@ pub fn build_reactive_scope_terminals_hir(func: &mut HirFunction, env: &mut Envi let mut queued_rewrites = collect_scope_rewrites(func, env); // Step 2: Apply rewrites by splitting blocks - let mut rewritten_final_blocks: HashMap = HashMap::new(); - let mut next_blocks: IndexMap = IndexMap::new(); + let mut rewritten_final_blocks: FxHashMap = FxHashMap::default(); + let mut next_blocks: IndexMap = IndexMap::default(); // Reverse so we can pop from the end while traversing in ascending order queued_rewrites.reverse(); @@ -300,7 +299,7 @@ pub fn build_reactive_scope_terminals_hir(func: &mut HirFunction, env: &mut Envi } if !context.rewrites.is_empty() { - let mut final_preds = indexmap::IndexSet::new(); + let mut final_preds = IndexSet::default(); for &p in &context.next_preds { final_preds.insert(p); } diff --git a/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_effects.rs b/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_effects.rs index cffceb4d97c..83b06418738 100644 --- a/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_effects.rs +++ b/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_effects.rs @@ -11,10 +11,9 @@ //! creation, aliasing, mutation, freezing, and error conditions for each //! instruction and terminal in the HIR. -use std::collections::HashMap; -use std::collections::HashSet; +use indexmap::{IndexMap, IndexSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; -use indexmap::IndexSet; use react_compiler_diagnostics::CompilerDiagnostic; use react_compiler_diagnostics::CompilerDiagnosticDetail; use react_compiler_diagnostics::ErrorCategory; @@ -61,7 +60,7 @@ pub fn infer_mutation_aliasing_effects( let mut initial_state = InferenceState::empty(env, is_function_expression); // Map of blocks to the last (merged) incoming state that was processed - let mut states_by_block: HashMap = HashMap::new(); + let mut states_by_block: FxHashMap = FxHashMap::default(); // Initialize context variables for ctx_place in &func.context { @@ -115,12 +114,12 @@ pub fn infer_mutation_aliasing_effects( } } - let mut queued_states: indexmap::IndexMap = indexmap::IndexMap::new(); + let mut queued_states: IndexMap = IndexMap::default(); // Queue helper fn queue( - queued_states: &mut indexmap::IndexMap, - states_by_block: &HashMap, + queued_states: &mut IndexMap, + states_by_block: &FxHashMap, block_id: BlockId, state: InferenceState, ) { @@ -152,16 +151,16 @@ pub fn infer_mutation_aliasing_effects( let non_mutating_spreads = find_non_mutated_destructure_spreads(func, env); let mut context = Context { - interned_effects: HashMap::new(), - instruction_signature_cache: HashMap::new(), - catch_handlers: HashMap::new(), + interned_effects: FxHashMap::default(), + instruction_signature_cache: FxHashMap::default(), + catch_handlers: FxHashMap::default(), is_function_expression, hoisted_context_declarations, non_mutating_spreads, - effect_value_id_cache: HashMap::new(), - function_values: HashMap::new(), - function_signature_cache: HashMap::new(), - aliasing_config_temp_cache: HashMap::new(), + effect_value_id_cache: FxHashMap::default(), + function_values: FxHashMap::default(), + function_signature_cache: FxHashMap::default(), + aliasing_config_temp_cache: FxHashMap::default(), }; let mut iteration_count = 0; @@ -262,11 +261,11 @@ impl ValueId { #[derive(Debug, Clone)] struct AbstractValue { kind: ValueKind, - reason: IndexSet, + reason: IndexSet, } -fn hashset_of(r: ValueReason) -> IndexSet { - let mut s = IndexSet::new(); +fn hashset_of(r: ValueReason) -> IndexSet { + let mut s = IndexSet::default(); s.insert(r); s } @@ -282,9 +281,9 @@ fn hashset_of(r: ValueReason) -> IndexSet { struct InferenceState { is_function_expression: bool, /// The kind of each value, based on its allocation site - values: HashMap, + values: FxHashMap, /// The set of values pointed to by each identifier - variables: HashMap>, + variables: FxHashMap>, /// Tracks uninitialized identifier access errors (matches TS invariant). /// Uses Cell so it can be set from `&self` methods like `kind()`. /// Stores (IdentifierId, usage_loc) where usage_loc is the source location @@ -296,8 +295,8 @@ impl InferenceState { fn empty(_env: &Environment, is_function_expression: bool) -> Self { InferenceState { is_function_expression, - values: HashMap::new(), - variables: HashMap::new(), + values: FxHashMap::default(), + variables: FxHashMap::default(), uninitialized_access: std::cell::Cell::new(None), } } @@ -342,7 +341,7 @@ impl InferenceState { } fn define(&mut self, place_id: IdentifierId, value_id: ValueId) { - let mut set = HashSet::new(); + let mut set = FxHashSet::default(); set.insert(value_id); self.variables.insert(place_id, set); } @@ -354,7 +353,7 @@ impl InferenceState { // Create a stable value for uninitialized identifiers // Use a deterministic ID based on the from identifier let vid = ValueId(from.0 | 0x80000000); - let mut set = HashSet::new(); + let mut set = FxHashSet::default(); set.insert(vid); if !self.values.contains_key(&vid) { self.values.insert( @@ -380,7 +379,7 @@ impl InferenceState { Some(v) => v.clone(), None => return, }; - let merged: HashSet = prev_values.union(&new_values).copied().collect(); + let merged: FxHashSet = prev_values.union(&new_values).copied().collect(); self.variables.insert(place, merged); } @@ -486,8 +485,8 @@ impl InferenceState { } fn merge(&self, other: &InferenceState) -> Option { - let mut next_values: Option> = None; - let mut next_variables: Option>> = None; + let mut next_values: Option> = None; + let mut next_variables: Option>> = None; // Merge values present in both for (id, this_value) in &self.values { @@ -521,7 +520,7 @@ impl InferenceState { } if has_new { let nvars = next_variables.get_or_insert_with(|| self.variables.clone()); - let merged: HashSet = + let merged: FxHashSet = this_values.union(other_values).copied().collect(); nvars.insert(*id, merged); } @@ -550,9 +549,9 @@ impl InferenceState { fn infer_phi( &mut self, phi_place_id: IdentifierId, - phi_operands: &indexmap::IndexMap, + phi_operands: &IndexMap, ) { - let mut values: HashSet = HashSet::new(); + let mut values: FxHashSet = FxHashSet::default(); for (_, operand) in phi_operands { if let Some(operand_values) = self.variables.get(&operand.identifier) { for v in operand_values { @@ -567,7 +566,10 @@ impl InferenceState { } } -fn is_superset(a: &IndexSet, b: &IndexSet) -> bool { +fn is_superset( + a: &IndexSet, + b: &IndexSet, +) -> bool { b.iter().all(|x| a.contains(x)) } @@ -593,24 +595,24 @@ enum MutationResult { // ============================================================================= struct Context { - interned_effects: HashMap, - instruction_signature_cache: HashMap, - catch_handlers: HashMap, + interned_effects: FxHashMap, + instruction_signature_cache: FxHashMap, + catch_handlers: FxHashMap, is_function_expression: bool, - hoisted_context_declarations: HashMap>, - non_mutating_spreads: HashSet, + hoisted_context_declarations: FxHashMap>, + non_mutating_spreads: FxHashSet, /// Cache of ValueIds keyed by effect hash, ensuring stable allocation-site identity /// across fixpoint iterations. Mirrors TS `effectInstructionValueCache`. - effect_value_id_cache: HashMap, + effect_value_id_cache: FxHashMap, /// Maps ValueId to FunctionId for function expressions, so we can look up /// locally-declared functions when processing Apply effects. - function_values: HashMap, + function_values: FxHashMap, /// Cache of function expression signatures, keyed by FunctionId - function_signature_cache: HashMap, + function_signature_cache: FxHashMap, /// Cache of temporary places created for aliasing signature config temporaries. /// Keyed by (lvalue_identifier_id, temp_name) to ensure stable allocation /// across fixpoint iterations. - aliasing_config_temp_cache: HashMap<(IdentifierId, String), Place>, + aliasing_config_temp_cache: FxHashMap<(IdentifierId, String), Place>, } impl Context { @@ -785,11 +787,11 @@ fn merge_value_kinds(a: ValueKind, b: ValueKind) -> ValueKind { fn find_hoisted_context_declarations( func: &HirFunction, env: &Environment, -) -> HashMap> { - let mut hoisted: HashMap> = HashMap::new(); +) -> FxHashMap> { + let mut hoisted: FxHashMap> = FxHashMap::default(); fn visit( - hoisted: &mut HashMap>, + hoisted: &mut FxHashMap>, place: &Place, env: &Environment, ) { @@ -831,8 +833,8 @@ fn find_hoisted_context_declarations( fn find_non_mutated_destructure_spreads( func: &HirFunction, env: &Environment, -) -> HashSet { - let mut known_frozen: HashSet = HashSet::new(); +) -> FxHashSet { + let mut known_frozen: FxHashSet = FxHashSet::default(); if func.fn_type == ReactFunctionType::Component { if let Some(param) = func.params.first() { if let ParamPattern::Place(p) = param { @@ -847,7 +849,8 @@ fn find_non_mutated_destructure_spreads( } } - let mut candidate_non_mutating_spreads: HashMap = HashMap::new(); + let mut candidate_non_mutating_spreads: FxHashMap = + FxHashMap::default(); for (_block_id, block) in &func.body.blocks { if !candidate_non_mutating_spreads.is_empty() { for phi in &block.phis { @@ -954,7 +957,7 @@ fn find_non_mutated_destructure_spreads( } } - let mut non_mutating: HashSet = HashSet::new(); + let mut non_mutating: FxHashSet = FxHashSet::default(); for (key, value) in &candidate_non_mutating_spreads { if key == value { non_mutating.insert(*key); @@ -991,7 +994,7 @@ fn infer_block( let block = &func.body.blocks[&block_id]; // Process phis - let phis: Vec<(IdentifierId, indexmap::IndexMap)> = block + let phis: Vec<(IdentifierId, IndexMap)> = block .phis .iter() .map(|phi| (phi.place.identifier, phi.operands.clone())) @@ -1144,7 +1147,7 @@ fn apply_signature( | InstructionValue::ObjectMethod { lowered_func, .. } => { let inner_func = &env.functions[lowered_func.func.0 as usize]; if let Some(ref aliasing_effects) = inner_func.aliasing_effects { - let context_ids: HashSet = + let context_ids: FxHashSet = inner_func.context.iter().map(|p| p.identifier).collect(); for effect in aliasing_effects { let (mutate_value, is_mutate) = match effect { @@ -1203,7 +1206,7 @@ fn apply_signature( } // Track which values we've already initialized - let mut initialized: HashSet = HashSet::new(); + let mut initialized: FxHashSet = FxHashSet::default(); // Get the cached signature effects let sig = context.instruction_signature_cache.get(&instr_idx).unwrap(); @@ -1296,7 +1299,7 @@ fn apply_effect( context: &mut Context, state: &mut InferenceState, effect: AliasingEffect, - initialized: &mut HashSet, + initialized: &mut FxHashSet, effects: &mut Vec, env: &mut Environment, func: &HirFunction, @@ -1484,7 +1487,7 @@ fn apply_effect( } else { ValueKind::Frozen }, - reason: IndexSet::new(), + reason: IndexSet::default(), }, ); state.define(into.identifier, value_id); @@ -2582,7 +2585,7 @@ fn compute_effects_for_legacy_signature( args: &[PlaceOrSpreadOrHole], _loc: Option<&SourceLocation>, env: &Environment, - function_values: &HashMap, + function_values: &FxHashMap, todo_errors: &mut Vec, ) -> Vec { let return_value_reason = signature.return_value_reason.unwrap_or(ValueReason::Other); @@ -2786,7 +2789,7 @@ fn are_arguments_immutable_and_non_mutating( state: &InferenceState, args: &[PlaceOrSpreadOrHole], env: &Environment, - function_values: &HashMap, + function_values: &FxHashMap, ) -> bool { for arg in args { match arg { @@ -2870,14 +2873,14 @@ fn compute_effects_for_aliasing_signature_config( args: &[PlaceOrSpreadOrHole], context: &[Place], _loc: Option<&SourceLocation>, - temp_cache: &mut HashMap<(IdentifierId, String), Place>, + temp_cache: &mut FxHashMap<(IdentifierId, String), Place>, ) -> Result>, CompilerDiagnostic> { // Build substitutions from config strings to places - let mut substitutions: HashMap> = HashMap::new(); + let mut substitutions: FxHashMap> = FxHashMap::default(); substitutions.insert(config.receiver.clone(), vec![receiver.clone()]); substitutions.insert(config.returns.clone(), vec![lvalue.clone()]); - let mut mutable_spreads: HashSet = HashSet::new(); + let mut mutable_spreads: FxHashSet = FxHashSet::default(); for (i, arg) in args.iter().enumerate() { match arg { @@ -3113,8 +3116,8 @@ fn compute_effects_for_aliasing_signature( return Ok(None); } - let mut mutable_spreads: HashSet = HashSet::new(); - let mut substitutions: HashMap> = HashMap::new(); + let mut mutable_spreads: FxHashSet = FxHashSet::default(); + let mut substitutions: FxHashMap> = FxHashMap::default(); substitutions.insert(signature.receiver, vec![receiver.clone()]); substitutions.insert(signature.returns, vec![lvalue.clone()]); @@ -3407,7 +3410,7 @@ fn compute_effects_for_aliasing_signature( /// since the primary reason is always inserted first, this effectively /// picks the most specific non-Other reason. We replicate this by /// preferring any non-Other reason over Other. -fn primary_reason(reasons: &IndexSet) -> ValueReason { +fn primary_reason(reasons: &IndexSet) -> ValueReason { for &r in reasons { if r != ValueReason::Other { return r; diff --git a/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_ranges.rs b/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_ranges.rs index 9a24249d1c7..31ffb7ad176 100644 --- a/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_ranges.rs +++ b/compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_ranges.rs @@ -14,7 +14,7 @@ //! vars, aliasing between params/context-vars/return-value) //! - The legacy `Effect` to store on each Place -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use indexmap::IndexMap; @@ -76,10 +76,10 @@ enum NodeValue { #[derive(Debug, Clone)] struct Node { id: IdentifierId, - created_from: IndexMap, - captures: IndexMap, - aliases: IndexMap, - maybe_aliases: IndexMap, + created_from: IndexMap, + captures: IndexMap, + aliases: IndexMap, + maybe_aliases: IndexMap, edges: Vec, transitive: Option, local: Option, @@ -92,10 +92,10 @@ impl Node { fn new(id: IdentifierId, value: NodeValue) -> Self { Node { id, - created_from: IndexMap::new(), - captures: IndexMap::new(), - aliases: IndexMap::new(), - maybe_aliases: IndexMap::new(), + created_from: IndexMap::default(), + captures: IndexMap::default(), + aliases: IndexMap::default(), + maybe_aliases: IndexMap::default(), edges: Vec::new(), transitive: None, local: None, @@ -107,13 +107,13 @@ impl Node { } struct AliasingState { - nodes: IndexMap, + nodes: IndexMap, } impl AliasingState { fn new() -> Self { AliasingState { - nodes: IndexMap::new(), + nodes: IndexMap::default(), } } @@ -198,7 +198,7 @@ impl AliasingState { } fn render(&self, index: usize, start: IdentifierId, env: &mut Environment) { - let mut seen = HashSet::new(); + let mut seen = FxHashSet::default(); let mut queue: Vec = vec![start]; while let Some(current) = queue.pop() { if !seen.insert(current) { @@ -260,7 +260,7 @@ impl AliasingState { Forwards, } - let mut seen: HashMap = HashMap::new(); + let mut seen: FxHashMap = FxHashMap::default(); let mut queue: Vec = vec![QueueEntry { place: start, transitive, @@ -475,7 +475,7 @@ pub fn infer_mutation_aliasing_ranges( into: Place, index: usize, } - let mut pending_phis: HashMap> = HashMap::new(); + let mut pending_phis: FxHashMap> = FxHashMap::default(); struct PendingMutation { index: usize, @@ -510,7 +510,7 @@ pub fn infer_mutation_aliasing_ranges( } state.create(&func.returns, NodeValue::Object); - let mut seen_blocks: HashSet = HashSet::new(); + let mut seen_blocks: FxHashSet = FxHashSet::default(); // Collect block iteration data to avoid borrow conflicts let block_order: Vec = func.body.blocks.keys().cloned().collect(); @@ -734,7 +734,7 @@ pub fn infer_mutation_aliasing_ranges( // Set effect on mutated params/context vars // We need to do this in a separate pass because we need to know which params // were mutated before setting effects - let mut captured_params: HashSet = HashSet::new(); + let mut captured_params: FxHashSet = FxHashSet::default(); for param in &func.params { let place = match param { react_compiler_hir::ParamPattern::Place(p) => p, @@ -892,7 +892,7 @@ pub fn infer_mutation_aliasing_ranges( // Compute operand effects from instruction effects let effects = instr.effects.as_ref().unwrap().clone(); - let mut operand_effects: HashMap = HashMap::new(); + let mut operand_effects: FxHashMap = FxHashMap::default(); for effect in &effects { match effect { diff --git a/compiler/crates/react_compiler_inference/src/infer_reactive_places.rs b/compiler/crates/react_compiler_inference/src/infer_reactive_places.rs index e3c8d00aee0..cce5391d555 100644 --- a/compiler/crates/react_compiler_inference/src/infer_reactive_places.rs +++ b/compiler/crates/react_compiler_inference/src/infer_reactive_places.rs @@ -14,7 +14,7 @@ //! 4. Mutation with reactive operands //! 5. Conditional assignment based on reactive control flow -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory}; use react_compiler_hir::dominator::post_dominator_frontier; @@ -69,7 +69,7 @@ pub fn infer_reactive_places( // is already reactive, the TS `continue`s and skips operand processing. // We track which phi operand Places should be marked reactive. // Key: (block_id, phi_idx, operand_idx), Value: should be reactive - let mut phi_operand_reactive: HashMap<(BlockId, usize, usize), bool> = HashMap::new(); + let mut phi_operand_reactive: FxHashMap<(BlockId, usize, usize), bool> = FxHashMap::default(); // Fixpoint iteration — compute reactive set loop { @@ -235,7 +235,7 @@ pub fn infer_reactive_places( struct ReactivityMap<'a> { has_changes: bool, - reactive: HashSet, + reactive: FxHashSet, aliased_identifiers: &'a mut DisjointSet, } @@ -243,7 +243,7 @@ impl<'a> ReactivityMap<'a> { fn new(aliased_identifiers: &'a mut DisjointSet) -> Self { ReactivityMap { has_changes: false, - reactive: HashSet::new(), + reactive: FxHashSet::default(), aliased_identifiers, } } @@ -273,13 +273,13 @@ impl<'a> ReactivityMap<'a> { // ============================================================================= struct StableSidemap { - map: HashMap, + map: FxHashMap, } impl StableSidemap { fn new() -> Self { StableSidemap { - map: HashMap::new(), + map: FxHashMap::default(), } } @@ -538,7 +538,7 @@ fn apply_reactive_flags_replay( env: &mut Environment, reactive_map: &mut ReactivityMap, stable_sidemap: &mut StableSidemap, - phi_operand_reactive: &HashMap<(BlockId, usize, usize), bool>, + phi_operand_reactive: &FxHashMap<(BlockId, usize, usize), bool>, ) { let reactive_ids = build_reactive_id_set(reactive_map); @@ -697,8 +697,8 @@ fn apply_reactive_flags_replay( apply_reactive_flags_to_inner_functions(func, env, &reactive_ids); } -fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> HashSet { - let mut result = HashSet::new(); +fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> FxHashSet { + let mut result = FxHashSet::default(); for &id in &reactive_map.reactive { result.insert(id); } @@ -714,7 +714,7 @@ fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> HashSet, + reactive_ids: &FxHashSet, ) { for (_block_id, block) in &func.body.blocks { for instr_id in &block.instructions { @@ -733,7 +733,7 @@ fn apply_reactive_flags_to_inner_functions( fn apply_reactive_flags_to_inner_func( func_id: FunctionId, env: &mut Environment, - reactive_ids: &HashSet, + reactive_ids: &FxHashSet, ) { // Collect nested function IDs first to avoid borrow issues let nested_func_ids: Vec = { diff --git a/compiler/crates/react_compiler_inference/src/infer_reactive_scope_variables.rs b/compiler/crates/react_compiler_inference/src/infer_reactive_scope_variables.rs index 8847f21214b..a806a873017 100644 --- a/compiler/crates/react_compiler_inference/src/infer_reactive_scope_variables.rs +++ b/compiler/crates/react_compiler_inference/src/infer_reactive_scope_variables.rs @@ -15,7 +15,7 @@ //! 3. MergeOverlappingReactiveScopes ensures scopes do not overlap. //! 4. BuildReactiveBlocks groups the statements for each scope. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory}; use react_compiler_hir::environment::Environment; @@ -45,7 +45,7 @@ pub fn infer_reactive_scope_variables( // Phase 2: assign scopes // Maps each group root identifier to the ScopeId assigned to that group. - let mut scopes: HashMap = HashMap::new(); + let mut scopes: FxHashMap = FxHashMap::default(); scope_identifiers.for_each(|identifier_id, group_id| { let ident_range = env.identifiers[identifier_id.0 as usize] @@ -267,7 +267,7 @@ pub(crate) fn find_disjoint_mutable_values( env: &Environment, ) -> DisjointSet { let mut scope_identifiers = DisjointSet::::new(); - let mut declarations: HashMap = HashMap::new(); + let mut declarations: FxHashMap = FxHashMap::default(); let enable_forest = env.config.enable_forest; @@ -284,8 +284,8 @@ pub(crate) fn find_disjoint_mutable_values( .map(|iid| func.instructions[iid.0 as usize].id) .unwrap_or(block.terminal.evaluation_order()); - let is_phi_mutated_after_creation = phi_range.start.0 + 1 != phi_range.end.0 - && phi_range.end > first_instr_id; + let is_phi_mutated_after_creation = + phi_range.start.0 + 1 != phi_range.end.0 && phi_range.end > first_instr_id; // A phi operand defined at or after the phi's block is a loop // back-edge: the variable is reassigned within the loop (eg a // counter `a++` or `a = a + 1`). The reassignment must count as diff --git a/compiler/crates/react_compiler_inference/src/memoize_fbt_and_macro_operands_in_same_scope.rs b/compiler/crates/react_compiler_inference/src/memoize_fbt_and_macro_operands_in_same_scope.rs index 65969a90288..9c89eaeaadf 100644 --- a/compiler/crates/react_compiler_inference/src/memoize_fbt_and_macro_operands_in_same_scope.rs +++ b/compiler/crates/react_compiler_inference/src/memoize_fbt_and_macro_operands_in_same_scope.rs @@ -13,7 +13,7 @@ //! 1. Forward data-flow: identify all macro tags (including property loads like `fbt.param`) //! 2. Reverse data-flow: merge arguments of macro invocations into the same scope -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::environment::Environment; use react_compiler_hir::visitors; @@ -34,7 +34,7 @@ enum InlineLevel { struct MacroDefinition { level: InlineLevel, /// Maps property names to their own MacroDefinition. `"*"` is a wildcard. - properties: Option>, + properties: Option>, } fn shallow_macro() -> MacroDefinition { @@ -52,7 +52,7 @@ fn transitive_macro() -> MacroDefinition { } fn fbt_macro() -> MacroDefinition { - let mut props = HashMap::new(); + let mut props = FxHashMap::default(); props.insert("*".to_string(), shallow_macro()); // fbt.enum gets FBT_MACRO (recursive/transitive) // We'll fill this in after construction since it's self-referential. @@ -66,7 +66,7 @@ fn fbt_macro() -> MacroDefinition { let enum_macro = MacroDefinition { level: InlineLevel::Transitive, properties: Some({ - let mut p = HashMap::new(); + let mut p = FxHashMap::default(); p.insert("*".to_string(), shallow_macro()); // enum's enum is also recursive, but in practice the depth is bounded p.insert("enum".to_string(), transitive_macro()); @@ -81,8 +81,8 @@ fn fbt_macro() -> MacroDefinition { } /// Built-in FBT tags and their macro definitions. -fn fbt_tags() -> HashMap { - let mut tags = HashMap::new(); +fn fbt_tags() -> FxHashMap { + let mut tags = FxHashMap::default(); tags.insert("fbt".to_string(), fbt_macro()); tags.insert("fbt:param".to_string(), shallow_macro()); tags.insert("fbt:enum".to_string(), fbt_macro()); @@ -98,9 +98,9 @@ fn fbt_tags() -> HashMap { pub fn memoize_fbt_and_macro_operands_in_same_scope( func: &HirFunction, env: &mut Environment, -) -> HashSet { +) -> FxHashSet { // Phase 1: Build macro kinds map from built-in FBT tags + custom macros - let mut macro_kinds: HashMap = fbt_tags(); + let mut macro_kinds: FxHashMap = fbt_tags(); if let Some(ref custom_macros) = env.config.custom_macros { for name in custom_macros { macro_kinds.insert(name.clone(), transitive_macro()); @@ -120,9 +120,9 @@ pub fn memoize_fbt_and_macro_operands_in_same_scope( /// things like `fbt.foo.bar(...)`. fn populate_macro_tags( func: &HirFunction, - macro_kinds: &HashMap, -) -> HashMap { - let mut macro_tags: HashMap = HashMap::new(); + macro_kinds: &FxHashMap, +) -> FxHashMap { + let mut macro_tags: FxHashMap = FxHashMap::default(); for block in func.body.blocks.values() { for &instr_id in &block.instructions { @@ -134,9 +134,7 @@ fn populate_macro_tags( value: PrimitiveValue::String(s), .. } => { - if let Some(macro_def) = - s.as_str().and_then(|utf8| macro_kinds.get(utf8)) - { + if let Some(macro_def) = s.as_str().and_then(|utf8| macro_kinds.get(utf8)) { // We don't distinguish between tag names and strings, so record // all `fbt` string literals in case they are used as a jsx tag. macro_tags.insert(lvalue_id, macro_def.clone()); @@ -180,10 +178,10 @@ fn populate_macro_tags( fn merge_macro_arguments( func: &HirFunction, env: &mut Environment, - macro_tags: &mut HashMap, - macro_kinds: &HashMap, -) -> HashSet { - let mut macro_values: HashSet = macro_tags.keys().copied().collect(); + macro_tags: &mut FxHashMap, + macro_kinds: &FxHashMap, +) -> FxHashSet { + let mut macro_values: FxHashSet = macro_tags.keys().copied().collect(); // Iterate blocks in reverse order let block_ids: Vec<_> = func.body.blocks.keys().copied().collect(); @@ -356,8 +354,8 @@ fn visit_operands( lvalue_id: IdentifierId, value: &InstructionValue, env: &mut Environment, - macro_values: &mut HashSet, - macro_tags: &mut HashMap, + macro_values: &mut FxHashSet, + macro_tags: &mut FxHashMap, ) { macro_values.insert(lvalue_id); diff --git a/compiler/crates/react_compiler_inference/src/merge_overlapping_reactive_scopes_hir.rs b/compiler/crates/react_compiler_inference/src/merge_overlapping_reactive_scopes_hir.rs index 3b8f574bee0..0786d79bda8 100644 --- a/compiler/crates/react_compiler_inference/src/merge_overlapping_reactive_scopes_hir.rs +++ b/compiler/crates/react_compiler_inference/src/merge_overlapping_reactive_scopes_hir.rs @@ -15,8 +15,8 @@ //! //! Ported from TypeScript `src/HIR/MergeOverlappingReactiveScopesHIR.ts`. +use rustc_hash::FxHashMap; use std::cmp; -use std::collections::HashMap; use react_compiler_hir::environment::Environment; use react_compiler_hir::visitors; @@ -46,7 +46,7 @@ struct ScopeInfo { /// Sorted descending by id (so we can pop from the end for smallest) scope_ends: Vec, /// Maps IdentifierId -> ScopeId for all places that have a scope - place_scopes: HashMap, + place_scopes: FxHashMap, } // ============================================================================= @@ -95,9 +95,9 @@ fn is_mutable(env: &Environment, id: EvaluationOrder, identifier_id: IdentifierI // ============================================================================= fn collect_scope_info(func: &HirFunction, env: &Environment) -> ScopeInfo { - let mut scope_starts_map: HashMap> = HashMap::new(); - let mut scope_ends_map: HashMap> = HashMap::new(); - let mut place_scopes: HashMap = HashMap::new(); + let mut scope_starts_map: FxHashMap> = FxHashMap::default(); + let mut scope_ends_map: FxHashMap> = FxHashMap::default(); + let mut place_scopes: FxHashMap = FxHashMap::default(); let mut collect_place_scope = |identifier_id: IdentifierId, env: &Environment| { let scope_id = match env.identifiers[identifier_id.0 as usize].scope { @@ -144,7 +144,7 @@ fn collect_scope_info(func: &HirFunction, env: &Environment) -> ScopeInfo { // We must NOT sort by ScopeId here — the insertion order determines which scope // becomes the root in the disjoint set union. fn dedup_preserve_order(scopes: &mut Vec) { - let mut seen = std::collections::HashSet::new(); + let mut seen = rustc_hash::FxHashSet::default(); scopes.retain(|s| seen.insert(*s)); } for scopes in scope_starts_map.values_mut() { @@ -348,8 +348,8 @@ pub fn merge_overlapping_reactive_scopes_hir(func: &mut HirFunction, env: &mut E // When scope.range is updated, ALL identifiers referencing that range object // automatically see the new values. We use MutableRangeId to identify which // identifiers share the same logical range as a root scope. - let mut original_root_range_ids: HashMap = - HashMap::new(); + let mut original_root_range_ids: FxHashMap = + FxHashMap::default(); for (_, root_id) in &scope_groups { if !original_root_range_ids.contains_key(root_id) { let range_id = env.scopes[root_id.0 as usize].range.id; diff --git a/compiler/crates/react_compiler_inference/src/propagate_scope_dependencies_hir.rs b/compiler/crates/react_compiler_inference/src/propagate_scope_dependencies_hir.rs index 0628a59144a..d0538aad52f 100644 --- a/compiler/crates/react_compiler_inference/src/propagate_scope_dependencies_hir.rs +++ b/compiler/crates/react_compiler_inference/src/propagate_scope_dependencies_hir.rs @@ -13,7 +13,8 @@ //! - `src/HIR/DeriveMinimalDependenciesHIR.ts` use indexmap::IndexMap; -use std::collections::{BTreeSet, HashMap, HashSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; +use std::collections::BTreeSet; use react_compiler_hir::environment::Environment; use react_compiler_hir::visitors::{ScopeBlockInfo, ScopeBlockTraversal}; @@ -44,7 +45,7 @@ pub fn propagate_scope_dependencies_hir(func: &mut HirFunction, env: &mut Enviro let (working, registry) = collect_hoistable_and_propagate(func, env, &temporaries, &hoistable_objects); // Convert to scope-keyed map with full dependency paths - let mut keyed: HashMap> = HashMap::new(); + let mut keyed: FxHashMap> = FxHashMap::default(); for (_block_id, block) in &func.body.blocks { if let Terminal::Scope { scope, @@ -128,17 +129,17 @@ fn are_equal_paths(a: &[DependencyPathEntry], b: &[DependencyPathEntry]) -> bool fn find_temporaries_used_outside_declaring_scope( func: &HirFunction, env: &Environment, -) -> HashSet { - let mut declarations: HashMap = HashMap::new(); - let mut pruned_scopes: HashSet = HashSet::new(); +) -> FxHashSet { + let mut declarations: FxHashMap = FxHashMap::default(); + let mut pruned_scopes: FxHashSet = FxHashSet::default(); let mut traversal = ScopeBlockTraversal::new(); - let mut used_outside_declaring_scope: HashSet = HashSet::new(); + let mut used_outside_declaring_scope: FxHashSet = FxHashSet::default(); let handle_place = |place_id: IdentifierId, - declarations: &HashMap, + declarations: &FxHashMap, traversal: &ScopeBlockTraversal, - pruned_scopes: &HashSet, - used_outside: &mut HashSet, + pruned_scopes: &FxHashSet, + used_outside: &mut FxHashSet, env: &Environment| { let decl_id = env.identifiers[place_id.0 as usize].declaration_id; if let Some(&declaring_scope) = declarations.get(&decl_id) { @@ -227,9 +228,9 @@ fn find_temporaries_used_outside_declaring_scope( fn collect_temporaries_sidemap( func: &HirFunction, env: &Environment, - used_outside_declaring_scope: &HashSet, -) -> HashMap { - let mut temporaries = HashMap::new(); + used_outside_declaring_scope: &FxHashSet, +) -> FxHashMap { + let mut temporaries = FxHashMap::default(); collect_temporaries_sidemap_impl( func, env, @@ -269,8 +270,8 @@ fn convert_hoisted_lvalue_kind(kind: InstructionKind) -> Option fn collect_temporaries_sidemap_impl( func: &HirFunction, env: &Environment, - used_outside_declaring_scope: &HashSet, - temporaries: &mut HashMap, + used_outside_declaring_scope: &FxHashSet, + temporaries: &mut FxHashMap, inner_fn_context: Option, ) { for (_block_id, block) in &func.body.blocks { @@ -369,7 +370,7 @@ fn get_property( property_name: &PropertyLiteral, optional: bool, loc: Option, - temporaries: &HashMap, + temporaries: &FxHashMap, _env: &Environment, ) -> ReactiveScopeDependency { let resolved = temporaries.get(&object.identifier); @@ -405,9 +406,9 @@ fn get_property( // ============================================================================= struct OptionalChainSidemap { - temporaries_read_in_optional: HashMap, - processed_instrs_in_optional: HashSet, - hoistable_objects: HashMap, + temporaries_read_in_optional: FxHashMap, + processed_instrs_in_optional: FxHashSet, + hoistable_objects: FxHashMap, } /// We track processed instructions/terminals by their lvalue IdentifierId + block id. @@ -423,10 +424,10 @@ enum ProcessedInstr { fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> OptionalChainSidemap { let mut ctx = OptionalTraversalContext { - seen_optionals: HashSet::new(), - processed_instrs_in_optional: HashSet::new(), - temporaries_read_in_optional: HashMap::new(), - hoistable_objects: HashMap::new(), + seen_optionals: FxHashSet::default(), + processed_instrs_in_optional: FxHashSet::default(), + temporaries_read_in_optional: FxHashMap::default(), + hoistable_objects: FxHashMap::default(), }; traverse_function_optional(func, env, &mut ctx); @@ -439,10 +440,10 @@ fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> Opti } struct OptionalTraversalContext { - seen_optionals: HashSet, - processed_instrs_in_optional: HashSet, - temporaries_read_in_optional: HashMap, - hoistable_objects: HashMap, + seen_optionals: FxHashSet, + processed_instrs_in_optional: FxHashSet, + temporaries_read_in_optional: FxHashMap, + hoistable_objects: FxHashMap, } fn traverse_function_optional( @@ -785,8 +786,8 @@ fn traverse_optional_block( #[derive(Debug, Clone)] struct PropertyPathNode { - properties: HashMap, // index into registry - optional_properties: HashMap, // index into registry + properties: FxHashMap, // index into registry + optional_properties: FxHashMap, // index into registry #[allow(dead_code)] parent: Option, full_path: ReactiveScopeDependency, @@ -797,14 +798,14 @@ struct PropertyPathNode { struct PropertyPathRegistry { nodes: Vec, - roots: HashMap, + roots: FxHashMap, } impl PropertyPathRegistry { fn new() -> Self { Self { nodes: Vec::new(), - roots: HashMap::new(), + roots: FxHashMap::default(), } } @@ -819,8 +820,8 @@ impl PropertyPathRegistry { } let idx = self.nodes.len(); self.nodes.push(PropertyPathNode { - properties: HashMap::new(), - optional_properties: HashMap::new(), + properties: FxHashMap::default(), + optional_properties: FxHashMap::default(), parent: None, full_path: ReactiveScopeDependency { identifier: identifier_id, @@ -858,8 +859,8 @@ impl PropertyPathRegistry { let mut new_path = parent_full_path.path.clone(); new_path.push(entry.clone()); self.nodes.push(PropertyPathNode { - properties: HashMap::new(), - optional_properties: HashMap::new(), + properties: FxHashMap::default(), + optional_properties: FxHashMap::default(), parent: Some(parent_idx), full_path: ReactiveScopeDependency { identifier: parent_full_path.identifier, @@ -962,11 +963,11 @@ struct BlockInfo { fn collect_hoistable_property_loads( func: &HirFunction, env: &Environment, - temporaries: &HashMap, - hoistable_from_optionals: &HashMap, -) -> HashMap { + temporaries: &FxHashMap, + hoistable_from_optionals: &FxHashMap, +) -> FxHashMap { let mut registry = PropertyPathRegistry::new(); - let known_immutable_identifiers: HashSet = if func.fn_type + let known_immutable_identifiers: FxHashSet = if func.fn_type == ReactFunctionType::Component || func.fn_type == ReactFunctionType::Hook { @@ -978,7 +979,7 @@ fn collect_hoistable_property_loads( }) .collect() } else { - HashSet::new() + FxHashSet::default() }; let assumed_invoked_fns = get_assumed_invoked_functions(func, env); @@ -994,11 +995,11 @@ fn collect_hoistable_property_loads( } struct CollectHoistableContext<'a> { - temporaries: &'a HashMap, - known_immutable_identifiers: &'a HashSet, - hoistable_from_optionals: &'a HashMap, - nested_fn_immutable_context: Option<&'a HashSet>, - assumed_invoked_fns: &'a HashSet, + temporaries: &'a FxHashMap, + known_immutable_identifiers: &'a FxHashSet, + hoistable_from_optionals: &'a FxHashMap, + nested_fn_immutable_context: Option<&'a FxHashSet>, + assumed_invoked_fns: &'a FxHashSet, } fn is_immutable_at_instr( @@ -1027,7 +1028,7 @@ fn in_range(id: EvaluationOrder, range: &MutableRange) -> bool { fn get_maybe_non_null_in_instruction( value: &InstructionValue, - temporaries: &HashMap, + temporaries: &FxHashMap, ) -> Option { match value { InstructionValue::PropertyLoad { object, .. } => Some( @@ -1057,10 +1058,10 @@ fn collect_hoistable_property_loads_impl( env: &Environment, ctx: &CollectHoistableContext, registry: &mut PropertyPathRegistry, -) -> HashMap { +) -> FxHashMap { let nodes = collect_non_nulls_in_blocks(func, env, ctx, registry); let working = propagate_non_null(func, &nodes, registry); - // Return the propagated results, converting HashSet back to BlockInfo + // Return the propagated results, converting FxHashSet back to BlockInfo working .into_iter() .map(|(k, v)| { @@ -1078,17 +1079,18 @@ fn collect_hoistable_property_loads_impl( /// Returns the set of LoweredFunction FunctionIds that are assumed to be invoked. /// The `temporaries` map is shared across recursive calls (matching TS behavior where /// the same Map is passed to recursive invocations for inner functions). -fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> HashSet { - let mut temporaries: HashMap)> = HashMap::new(); +fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> FxHashSet { + let mut temporaries: FxHashMap)> = + FxHashMap::default(); get_assumed_invoked_functions_impl(func, env, &mut temporaries) } fn get_assumed_invoked_functions_impl( func: &HirFunction, env: &Environment, - temporaries: &mut HashMap)>, -) -> HashSet { - let mut hoistable: HashSet = HashSet::new(); + temporaries: &mut FxHashMap)>, +) -> FxHashSet { + let mut hoistable: FxHashSet = FxHashSet::default(); // Step 1: Collect identifier to function expression mappings for (_block_id, block) in &func.body.blocks { @@ -1096,8 +1098,10 @@ fn get_assumed_invoked_functions_impl( let instr = &func.instructions[instr_id.0 as usize]; match &instr.value { InstructionValue::FunctionExpression { lowered_func, .. } => { - temporaries - .insert(instr.lvalue.identifier, (lowered_func.func, HashSet::new())); + temporaries.insert( + instr.lvalue.identifier, + (lowered_func.func, FxHashSet::default()), + ); } InstructionValue::StoreLocal { value: val, lvalue, .. @@ -1221,7 +1225,7 @@ fn collect_non_nulls_in_blocks( env: &Environment, ctx: &CollectHoistableContext, registry: &mut PropertyPathRegistry, -) -> HashMap { +) -> FxHashMap { // Known non-null identifiers (e.g. component props) let mut known_non_null: BTreeSet = BTreeSet::new(); if func.fn_type == ReactFunctionType::Component && !func.params.is_empty() { @@ -1231,7 +1235,7 @@ fn collect_non_nulls_in_blocks( } } - let mut nodes: HashMap = HashMap::new(); + let mut nodes: FxHashMap = FxHashMap::default(); for (block_id, block) in &func.body.blocks { let mut assumed = known_non_null.clone(); @@ -1290,7 +1294,7 @@ fn collect_non_nulls_in_blocks( if ctx.assumed_invoked_fns.contains(&lowered_func.func) { let inner_func = &env.functions[lowered_func.func.0 as usize]; // Build nested fn immutable context - let nested_fn_immutable_context: HashSet = + let nested_fn_immutable_context: FxHashSet = if ctx.nested_fn_immutable_context.is_some() { // Already in a nested fn context, use existing ctx.nested_fn_immutable_context.unwrap().clone() @@ -1307,7 +1311,7 @@ fn collect_non_nulls_in_blocks( let inner_assumed = get_assumed_invoked_functions(inner_func, env); let inner_ctx = CollectHoistableContext { temporaries: ctx.temporaries, - known_immutable_identifiers: &HashSet::new(), + known_immutable_identifiers: &FxHashSet::default(), hoistable_from_optionals: ctx.hoistable_from_optionals, nested_fn_immutable_context: Some(&nested_fn_immutable_context), assumed_invoked_fns: &inner_assumed, @@ -1347,13 +1351,13 @@ fn collect_non_nulls_in_blocks( /// and should be filtered out, allowing non-null info to propagate through non-cyclic paths. fn propagate_non_null( func: &HirFunction, - nodes: &HashMap, + nodes: &FxHashMap, registry: &mut PropertyPathRegistry, -) -> HashMap> { +) -> FxHashMap> { // Build successor map. Use BTreeSet to iterate successors in sorted BlockId // order, matching the TS Set insertion order (blocks are created in // ascending BlockId order). - let mut block_successors: HashMap> = HashMap::new(); + let mut block_successors: FxHashMap> = FxHashMap::default(); for (block_id, block) in &func.body.blocks { for pred in &block.preds { block_successors.entry(*pred).or_default().insert(*block_id); @@ -1361,7 +1365,7 @@ fn propagate_non_null( } // Clone nodes into mutable working set - let mut working: HashMap> = nodes + let mut working: FxHashMap> = nodes .iter() .map(|(k, v)| (*k, v.assumed_non_null_objects.clone())) .collect(); @@ -1374,7 +1378,7 @@ fn propagate_non_null( let mut changed = false; // Forward pass (using predecessors) - let mut traversal_state: HashMap = HashMap::new(); + let mut traversal_state: FxHashMap = FxHashMap::default(); for &block_id in &block_ids { let block_changed = recursively_propagate_non_null( block_id, @@ -1426,10 +1430,10 @@ enum PropagationDirection { fn recursively_propagate_non_null( node_id: BlockId, direction: PropagationDirection, - traversal_state: &mut HashMap, - working: &mut HashMap>, + traversal_state: &mut FxHashMap, + working: &mut FxHashMap>, func: &HirFunction, - block_successors: &HashMap>, + block_successors: &FxHashMap>, registry: &mut PropertyPathRegistry, ) -> bool { // Avoid re-visiting computed or currently active nodes @@ -1500,12 +1504,12 @@ fn recursively_propagate_non_null( fn collect_hoistable_and_propagate( func: &HirFunction, env: &Environment, - temporaries: &HashMap, - hoistable_from_optionals: &HashMap, -) -> (HashMap>, PropertyPathRegistry) { + temporaries: &FxHashMap, + hoistable_from_optionals: &FxHashMap, +) -> (FxHashMap>, PropertyPathRegistry) { let mut registry = PropertyPathRegistry::new(); let assumed_invoked_fns = get_assumed_invoked_functions(func, env); - let known_immutable_identifiers: HashSet = if func.fn_type + let known_immutable_identifiers: FxHashSet = if func.fn_type == ReactFunctionType::Component || func.fn_type == ReactFunctionType::Hook { @@ -1517,7 +1521,7 @@ fn collect_hoistable_and_propagate( }) .collect() } else { - HashSet::new() + FxHashSet::default() }; let ctx = CollectHoistableContext { @@ -1538,9 +1542,9 @@ fn collect_hoistable_and_propagate( #[allow(dead_code)] fn key_by_scope_id( func: &HirFunction, - block_keyed: &HashMap, -) -> HashMap { - let mut keyed: HashMap = HashMap::new(); + block_keyed: &FxHashMap, +) -> FxHashMap { + let mut keyed: FxHashMap = FxHashMap::default(); for (_block_id, block) in &func.body.blocks { if let Terminal::Scope { scope, @@ -1600,7 +1604,7 @@ enum HoistableAccessType { } struct HoistableNode { - properties: HashMap>, + properties: FxHashMap>, access_type: HoistableAccessType, } @@ -1609,7 +1613,7 @@ struct HoistableNodeEntry { } struct DependencyNode { - properties: IndexMap>, + properties: IndexMap, FxBuildHasher>, access_type: PropertyAccessType, loc: Option, } @@ -1619,8 +1623,8 @@ struct DependencyNodeEntry { } struct ReactiveScopeDependencyTreeHIR { - hoistable_roots: HashMap, // node + reactive - dep_roots: IndexMap, // node + reactive (preserves insertion order like JS Map) + hoistable_roots: FxHashMap, // node + reactive + dep_roots: IndexMap, // node + reactive (preserves insertion order like JS Map) } impl ReactiveScopeDependencyTreeHIR { @@ -1628,7 +1632,8 @@ impl ReactiveScopeDependencyTreeHIR { hoistable_objects: impl Iterator, _env: &Environment, ) -> Self { - let mut hoistable_roots: HashMap = HashMap::new(); + let mut hoistable_roots: FxHashMap = + FxHashMap::default(); // Sort hoistable objects so that entries with optional first path come // before non-optional ones. This matches the TS behavior where @@ -1651,7 +1656,7 @@ impl ReactiveScopeDependencyTreeHIR { }; ( HoistableNode { - properties: HashMap::new(), + properties: FxHashMap::default(), access_type, }, dep.reactive, @@ -1671,7 +1676,7 @@ impl ReactiveScopeDependencyTreeHIR { .or_insert_with(|| { Box::new(HoistableNodeEntry { node: HoistableNode { - properties: HashMap::new(), + properties: FxHashMap::default(), access_type, }, }) @@ -1682,7 +1687,7 @@ impl ReactiveScopeDependencyTreeHIR { Self { hoistable_roots, - dep_roots: IndexMap::new(), + dep_roots: IndexMap::default(), } } @@ -1690,7 +1695,7 @@ impl ReactiveScopeDependencyTreeHIR { let root = self.dep_roots.entry(dep.identifier).or_insert_with(|| { ( DependencyNode { - properties: IndexMap::new(), + properties: IndexMap::default(), access_type: PropertyAccessType::UnconditionalAccess, loc: dep.loc, }, @@ -1735,7 +1740,7 @@ impl ReactiveScopeDependencyTreeHIR { .or_insert_with(|| { Box::new(DependencyNodeEntry { node: DependencyNode { - properties: IndexMap::new(), + properties: IndexMap::default(), access_type, loc: entry.loc, }, @@ -1809,30 +1814,30 @@ struct Decl { /// Context for dependency collection. struct DependencyCollectionContext<'a> { - declarations: HashMap, - reassignments: HashMap, + declarations: FxHashMap, + reassignments: FxHashMap, scope_stack: Vec, dep_stack: Vec>, - deps: IndexMap>, - temporaries: &'a HashMap, + deps: IndexMap, FxBuildHasher>, + temporaries: &'a FxHashMap, #[allow(dead_code)] - temporaries_used_outside_scope: &'a HashSet, - processed_instrs_in_optional: &'a HashSet, + temporaries_used_outside_scope: &'a FxHashSet, + processed_instrs_in_optional: &'a FxHashSet, inner_fn_context: Option, } impl<'a> DependencyCollectionContext<'a> { fn new( - temporaries_used_outside_scope: &'a HashSet, - temporaries: &'a HashMap, - processed_instrs_in_optional: &'a HashSet, + temporaries_used_outside_scope: &'a FxHashSet, + temporaries: &'a FxHashMap, + processed_instrs_in_optional: &'a FxHashSet, ) -> Self { Self { - declarations: HashMap::new(), - reassignments: HashMap::new(), + declarations: FxHashMap::default(), + reassignments: FxHashMap::default(), scope_stack: Vec::new(), dep_stack: Vec::new(), - deps: IndexMap::new(), + deps: IndexMap::default(), temporaries, temporaries_used_outside_scope, processed_instrs_in_optional, @@ -2222,10 +2227,10 @@ fn handle_instruction( fn collect_dependencies( func: &HirFunction, env: &mut Environment, - used_outside_declaring_scope: &HashSet, - temporaries: &HashMap, - processed_instrs_in_optional: &HashSet, -) -> IndexMap> { + used_outside_declaring_scope: &FxHashSet, + temporaries: &FxHashMap, + processed_instrs_in_optional: &FxHashSet, +) -> IndexMap, FxBuildHasher> { let mut ctx = DependencyCollectionContext::new( used_outside_declaring_scope, temporaries, diff --git a/compiler/crates/react_compiler_lowering/Cargo.toml b/compiler/crates/react_compiler_lowering/Cargo.toml index 0b586cfb2f1..ac84073e4f9 100644 --- a/compiler/crates/react_compiler_lowering/Cargo.toml +++ b/compiler/crates/react_compiler_lowering/Cargo.toml @@ -8,4 +8,5 @@ react_compiler_ast = { path = "../react_compiler_ast" } react_compiler_hir = { path = "../react_compiler_hir" } react_compiler_diagnostics = { path = "../react_compiler_diagnostics" } indexmap = "2" +rustc-hash = "2" serde_json = "1" diff --git a/compiler/crates/react_compiler_lowering/src/build_hir.rs b/compiler/crates/react_compiler_lowering/src/build_hir.rs index c3d3bd68763..5ec8c7b68a9 100644 --- a/compiler/crates/react_compiler_lowering/src/build_hir.rs +++ b/compiler/crates/react_compiler_lowering/src/build_hir.rs @@ -1,7 +1,6 @@ -use std::collections::HashSet; +use rustc_hash::{FxBuildHasher, FxHashSet}; -use indexmap::IndexMap; -use indexmap::IndexSet; +use indexmap::{IndexMap, IndexSet}; use react_compiler_ast::scope::BindingId; use react_compiler_ast::scope::BindingKind as AstBindingKind; use react_compiler_ast::scope::ScopeId; @@ -2531,7 +2530,7 @@ fn collect_binding_names_from_pattern( pattern: &react_compiler_ast::patterns::PatternLike, scope_id: react_compiler_ast::scope::ScopeId, scope_info: &ScopeInfo, - out: &mut HashSet, + out: &mut FxHashSet, ) { use react_compiler_ast::patterns::PatternLike; match pattern { @@ -2712,7 +2711,7 @@ fn lower_block_statement_inner( } // Track which bindings have been "declared" (their declaration statement has been seen) - let mut declared: HashSet = HashSet::new(); + let mut declared: FxHashSet = FxHashSet::default(); for body_stmt in &block.body { let stmt_start = statement_start(body_stmt).unwrap_or(0); @@ -4320,8 +4319,11 @@ pub fn lower( let context_identifiers = find_context_identifiers(func, scope_info, env, &identifier_locs)?; // For top-level functions, context is empty (no captured refs) - let context_map: IndexMap> = - IndexMap::new(); + let context_map: IndexMap< + react_compiler_ast::scope::BindingId, + Option, + FxBuildHasher, + > = IndexMap::default(); let (hir_func, _used_names, _child_bindings) = lower_inner( params, @@ -5592,7 +5594,7 @@ fn lower_function( } else { let parent = builder.function_scope(); let scope_info = builder.scope_info(); - let mapped: std::collections::HashSet = + let mapped: rustc_hash::FxHashSet = scope_info.node_id_to_scope.values().copied().collect(); let param_names: Vec = params .iter() @@ -5604,7 +5606,7 @@ fn lower_function( } }) .collect(); - let mut descendants = std::collections::HashSet::new(); + let mut descendants = rustc_hash::FxHashSet::default(); descendants.insert(parent); let mut changed = true; while changed { @@ -5671,7 +5673,11 @@ fn lower_function( ident_locs, ref_override.as_ref(), ); - let merged_context: IndexMap> = { + let merged_context: IndexMap< + react_compiler_ast::scope::BindingId, + Option, + FxBuildHasher, + > = { let parent_context = builder.context().clone(); let mut merged = parent_context; for (k, v) in captured_context { @@ -5743,7 +5749,11 @@ fn lower_function_declaration( ident_locs, None, ); - let merged_context: IndexMap> = { + let merged_context: IndexMap< + react_compiler_ast::scope::BindingId, + Option, + FxBuildHasher, + > = { let parent_context = builder.context().clone(); let mut merged = parent_context; for (k, v) in captured_context { @@ -5944,7 +5954,11 @@ fn lower_function_for_object_method( ident_locs, None, ); - let merged_context: IndexMap> = { + let merged_context: IndexMap< + react_compiler_ast::scope::BindingId, + Option, + FxBuildHasher, + > = { let parent_context = builder.context().clone(); let mut merged = parent_context; for (k, v) in captured_context { @@ -5991,19 +6005,27 @@ fn lower_inner( loc: Option, scope_info: &ScopeInfo, env: &mut Environment, - parent_bindings: Option>, - parent_used_names: Option>, - context_map: IndexMap>, + parent_bindings: Option< + IndexMap, + >, + parent_used_names: Option< + IndexMap, + >, + context_map: IndexMap< + react_compiler_ast::scope::BindingId, + Option, + FxBuildHasher, + >, function_scope: react_compiler_ast::scope::ScopeId, component_scope: react_compiler_ast::scope::ScopeId, - context_identifiers: &HashSet, + context_identifiers: &FxHashSet, is_top_level: bool, identifier_locs: &IdentifierLocIndex, ) -> Result< ( HirFunction, - IndexMap, - IndexMap, + IndexMap, + IndexMap, ), CompilerError, > { @@ -6776,22 +6798,22 @@ fn gather_captured_context( func_start: u32, func_end: u32, identifier_locs: &IdentifierLocIndex, - ref_node_ids_override: Option<&IndexSet>, -) -> IndexMap> { + ref_node_ids_override: Option<&IndexSet>, +) -> IndexMap, FxBuildHasher> { let parent_scope = scope_info.scopes[function_scope.0 as usize].parent; let pure_scopes = match parent_scope { Some(parent) => capture_scopes(scope_info, parent, component_scope), - None => IndexSet::new(), + None => IndexSet::default(), }; // Collect the earliest (lowest source position) reference location for each // captured binding. Using the minimum position makes the result independent of // ref_node_id_to_binding iteration order, matching the behavior the TS compiler // gets from Babel's position-ordered traversal. - let mut captured: std::collections::HashMap< + let mut captured: rustc_hash::FxHashMap< react_compiler_ast::scope::BindingId, (u32, Option), // (min_position, loc) - > = std::collections::HashMap::new(); + > = rustc_hash::FxHashMap::default(); for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding { if let Some(allowed) = ref_node_ids_override { @@ -6878,8 +6900,8 @@ fn capture_scopes( scope_info: &ScopeInfo, from: react_compiler_ast::scope::ScopeId, to: react_compiler_ast::scope::ScopeId, -) -> IndexSet { - let mut result = IndexSet::new(); +) -> IndexSet { + let mut result = IndexSet::default(); let mut current = Some(from); while let Some(scope_id) = current { result.insert(scope_id); @@ -7118,8 +7140,8 @@ fn collect_fbt_sub_tags_from_stmts( } } -fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet { - let mut positions = IndexSet::new(); +fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet { + let mut positions = IndexSet::default(); match body { FunctionBody::Block(block) => { for stmt in &block.body { @@ -7135,7 +7157,7 @@ fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet { fn collect_identifier_node_ids_from_stmt( stmt: &react_compiler_ast::statements::Statement, - positions: &mut IndexSet, + positions: &mut IndexSet, ) { use react_compiler_ast::statements::Statement; match stmt { @@ -7175,7 +7197,7 @@ fn collect_identifier_node_ids_from_stmt( fn collect_identifier_node_ids_from_expr( expr: &react_compiler_ast::expressions::Expression, - positions: &mut IndexSet, + positions: &mut IndexSet, ) { use react_compiler_ast::expressions::Expression; match expr { diff --git a/compiler/crates/react_compiler_lowering/src/find_context_identifiers.rs b/compiler/crates/react_compiler_lowering/src/find_context_identifiers.rs index b2d53ecd643..6eb867595d3 100644 --- a/compiler/crates/react_compiler_lowering/src/find_context_identifiers.rs +++ b/compiler/crates/react_compiler_lowering/src/find_context_identifiers.rs @@ -4,8 +4,7 @@ //! walking the AST with scope tracking to find variables that cross //! function boundaries. -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_ast::expressions::*; use react_compiler_ast::patterns::*; @@ -35,7 +34,7 @@ struct ContextIdentifierVisitor<'a> { /// Stack of inner function scopes encountered during traversal. /// Empty when at the top level of the function being compiled. function_stack: Vec, - binding_info: HashMap, + binding_info: FxHashMap, error: Option, } @@ -313,8 +312,8 @@ fn is_captured_by_function( /// ref_node_id_to_binding. These are entries where the reference's node_id /// matches the binding's declaration_node_id — i.e., the "reference" is /// actually the declaration itself. -fn build_declaration_node_ids(scope_info: &ScopeInfo) -> HashSet<(BindingId, u32)> { - let mut result = HashSet::new(); +fn build_declaration_node_ids(scope_info: &ScopeInfo) -> FxHashSet<(BindingId, u32)> { + let mut result = FxHashSet::default(); for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding { let binding = &scope_info.bindings[binding_id.0 as usize]; if binding.declaration_node_id == Some(ref_nid) { @@ -338,7 +337,7 @@ pub fn find_context_identifiers( scope_info: &ScopeInfo, env: &mut Environment, identifier_locs: &crate::identifier_loc_index::IdentifierLocIndex, -) -> Result, CompilerError> { +) -> Result, CompilerError> { let func_scope = scope_info .resolve_scope_for_node(func.node_id()) .unwrap_or(scope_info.program_scope); @@ -347,7 +346,7 @@ pub fn find_context_identifiers( scope_info, env, function_stack: Vec::new(), - binding_info: HashMap::new(), + binding_info: FxHashMap::default(), error: None, }; let mut walker = AstWalker::with_initial_scope(scope_info, func_scope); diff --git a/compiler/crates/react_compiler_lowering/src/hir_builder.rs b/compiler/crates/react_compiler_lowering/src/hir_builder.rs index 84bd1e0d45b..7c2a6d42821 100644 --- a/compiler/crates/react_compiler_lowering/src/hir_builder.rs +++ b/compiler/crates/react_compiler_lowering/src/hir_builder.rs @@ -1,5 +1,4 @@ -use indexmap::IndexMap; -use indexmap::IndexSet; +use indexmap::{IndexMap, IndexSet}; use react_compiler_ast::scope::BindingId; use react_compiler_ast::scope::ImportBindingKind; use react_compiler_ast::scope::ScopeId; @@ -13,6 +12,7 @@ use react_compiler_hir::environment::Environment; use react_compiler_hir::visitors::each_terminal_successor; use react_compiler_hir::visitors::terminal_fallthrough; use react_compiler_hir::*; +use rustc_hash::FxBuildHasher; use crate::identifier_loc_index::IdentifierLocIndex; @@ -139,18 +139,18 @@ fn new_block(id: BlockId, kind: BlockKind) -> WipBlock { // --------------------------------------------------------------------------- pub struct HirBuilder<'a> { - completed: IndexMap, + completed: IndexMap, current: WipBlock, entry: BlockId, scopes: Vec, /// Context identifiers: variables captured from an outer scope. /// Maps the outer scope's BindingId to the source location where it was referenced. - context: IndexMap>, + context: IndexMap, FxBuildHasher>, /// Resolved bindings: maps a BindingId to the HIR IdentifierId created for it. - bindings: IndexMap, + bindings: IndexMap, /// Names already used by bindings, for collision avoidance. /// Maps name string -> how many times it has been used (for appending _0, _1, ...). - used_names: IndexMap, + used_names: IndexMap, env: &'a mut Environment, scope_info: &'a ScopeInfo, exception_handler_stack: Vec, @@ -166,10 +166,10 @@ pub struct HirBuilder<'a> { /// Set of BindingIds for variables declared in scopes between component_scope /// and any inner function scope, that are referenced from an inner function scope. /// These need StoreContext/LoadContext instead of StoreLocal/LoadLocal. - context_identifiers: std::collections::HashSet, + context_identifiers: rustc_hash::FxHashSet, /// Set of ScopeIds that have been matched to synthetic blocks/functions. /// Prevents the same scope from being reused for different synthetic nodes. - claimed_synthetic_scopes: std::collections::HashSet, + claimed_synthetic_scopes: rustc_hash::FxHashSet, /// Index mapping identifier byte offsets to source locations and JSX status. identifier_locs: &'a IdentifierLocIndex, } @@ -192,17 +192,17 @@ impl<'a> HirBuilder<'a> { scope_info: &'a ScopeInfo, function_scope: ScopeId, component_scope: ScopeId, - context_identifiers: std::collections::HashSet, - bindings: Option>, - context: Option>>, + context_identifiers: rustc_hash::FxHashSet, + bindings: Option>, + context: Option, FxBuildHasher>>, entry_block_kind: Option, - used_names: Option>, + used_names: Option>, identifier_locs: &'a IdentifierLocIndex, ) -> Self { let entry = env.next_block_id(); let kind = entry_block_kind.unwrap_or(BlockKind::Block); HirBuilder { - completed: IndexMap::new(), + completed: IndexMap::default(), current: new_block(entry, kind), entry, scopes: Vec::new(), @@ -217,7 +217,7 @@ impl<'a> HirBuilder<'a> { function_scope, component_scope, context_identifiers, - claimed_synthetic_scopes: std::collections::HashSet::new(), + claimed_synthetic_scopes: rustc_hash::FxHashSet::default(), identifier_locs, } } @@ -290,12 +290,12 @@ impl<'a> HirBuilder<'a> { } /// Access the context map. - pub fn context(&self) -> &IndexMap> { + pub fn context(&self) -> &IndexMap, FxBuildHasher> { &self.context } /// Access the pre-computed context identifiers set. - pub fn context_identifiers(&self) -> &std::collections::HashSet { + pub fn context_identifiers(&self) -> &rustc_hash::FxHashSet { &self.context_identifiers } @@ -326,18 +326,21 @@ impl<'a> HirBuilder<'a> { } /// Access the bindings map. - pub fn bindings(&self) -> &IndexMap { + pub fn bindings(&self) -> &IndexMap { &self.bindings } /// Access the used names map. - pub fn used_names(&self) -> &IndexMap { + pub fn used_names(&self) -> &IndexMap { &self.used_names } /// Merge used names from a child builder back into this builder. /// This ensures name deduplication works across function scopes. - pub fn merge_used_names(&mut self, child_used_names: IndexMap) { + pub fn merge_used_names( + &mut self, + child_used_names: IndexMap, + ) { for (name, binding_id) in child_used_names { self.used_names.entry(name).or_insert(binding_id); } @@ -346,7 +349,10 @@ impl<'a> HirBuilder<'a> { /// Merge bindings (binding_id -> IdentifierId) from a child builder back into this builder. /// This matches TS behavior where parent and child share the same #bindings map by reference, /// so bindings resolved by the child are automatically visible to the parent. - pub fn merge_bindings(&mut self, child_bindings: IndexMap) { + pub fn merge_bindings( + &mut self, + child_bindings: IndexMap, + ) { for (binding_id, identifier_id) in child_bindings { self.bindings.entry(binding_id).or_insert(identifier_id); } @@ -403,7 +409,7 @@ impl<'a> HirBuilder<'a> { id: block_id, instructions: wip.instructions, terminal, - preds: IndexSet::new(), + preds: IndexSet::default(), phis: Vec::new(), }, ); @@ -427,7 +433,7 @@ impl<'a> HirBuilder<'a> { id: block_id, instructions: wip.instructions, terminal, - preds: IndexSet::new(), + preds: IndexSet::default(), phis: Vec::new(), }, ); @@ -451,7 +457,7 @@ impl<'a> HirBuilder<'a> { id: block_id, instructions: block.instructions, terminal, - preds: IndexSet::new(), + preds: IndexSet::default(), phis: Vec::new(), }, ); @@ -471,7 +477,7 @@ impl<'a> HirBuilder<'a> { id: completed_wip.id, instructions: completed_wip.instructions, terminal, - preds: IndexSet::new(), + preds: IndexSet::default(), phis: Vec::new(), }, ); @@ -493,7 +499,7 @@ impl<'a> HirBuilder<'a> { id: completed_wip.id, instructions: completed_wip.instructions, terminal, - preds: IndexSet::new(), + preds: IndexSet::default(), phis: Vec::new(), }, ); @@ -769,8 +775,8 @@ impl<'a> HirBuilder<'a> { ( HIR, Vec, - IndexMap, - IndexMap, + IndexMap, + IndexMap, ), CompilerError, > { @@ -1150,19 +1156,19 @@ impl<'a> HirBuilder<'a> { pub fn get_reverse_postordered_blocks( hir: &HIR, _instructions: &[Instruction], -) -> IndexMap { - let mut visited: IndexSet = IndexSet::new(); - let mut used: IndexSet = IndexSet::new(); - let mut used_fallthroughs: IndexSet = IndexSet::new(); +) -> IndexMap { + let mut visited: IndexSet = IndexSet::default(); + let mut used: IndexSet = IndexSet::default(); + let mut used_fallthroughs: IndexSet = IndexSet::default(); let mut postorder: Vec = Vec::new(); fn visit( hir: &HIR, block_id: BlockId, is_used: bool, - visited: &mut IndexSet, - used: &mut IndexSet, - used_fallthroughs: &mut IndexSet, + visited: &mut IndexSet, + used: &mut IndexSet, + used_fallthroughs: &mut IndexSet, postorder: &mut Vec, ) { let was_used = used.contains(&block_id); @@ -1222,7 +1228,7 @@ pub fn get_reverse_postordered_blocks( &mut postorder, ); - let mut blocks = IndexMap::new(); + let mut blocks = IndexMap::default(); for block_id in postorder.into_iter().rev() { let block = hir.blocks.get(&block_id).unwrap(); if used.contains(&block_id) { @@ -1252,7 +1258,7 @@ pub fn get_reverse_postordered_blocks( /// For each block with a `For` terminal whose update block is not in the /// blocks map, set update to None. pub fn remove_unreachable_for_updates(hir: &mut HIR) { - let block_ids: IndexSet = hir.blocks.keys().copied().collect(); + let block_ids: IndexSet = hir.blocks.keys().copied().collect(); for block in hir.blocks.values_mut() { if let Terminal::For { update, .. } = &mut block.terminal { if let Some(update_id) = *update { @@ -1267,7 +1273,7 @@ pub fn remove_unreachable_for_updates(hir: &mut HIR) { /// For each block with a `DoWhile` terminal whose test block is not in /// the blocks map, replace the terminal with a Goto to the loop block. pub fn remove_dead_do_while_statements(hir: &mut HIR) { - let block_ids: IndexSet = hir.blocks.keys().copied().collect(); + let block_ids: IndexSet = hir.blocks.keys().copied().collect(); for block in hir.blocks.values_mut() { let should_replace = if let Terminal::DoWhile { test, .. } = &block.terminal { !block_ids.contains(test) @@ -1304,7 +1310,7 @@ pub fn remove_dead_do_while_statements(hir: &mut HIR) { /// Also cleans up the fallthrough block's predecessors if the handler /// was the only path to it. pub fn remove_unnecessary_try_catch(hir: &mut HIR) { - let block_ids: IndexSet = hir.blocks.keys().copied().collect(); + let block_ids: IndexSet = hir.blocks.keys().copied().collect(); // Collect the blocks that need replacement and their associated data let replacements: Vec<(BlockId, BlockId, BlockId, BlockId, Option)> = hir @@ -1376,13 +1382,13 @@ pub fn mark_predecessors(hir: &mut HIR) { block.preds.clear(); } - let mut visited: IndexSet = IndexSet::new(); + let mut visited: IndexSet = IndexSet::default(); fn visit( hir: &mut HIR, block_id: BlockId, prev_block_id: Option, - visited: &mut IndexSet, + visited: &mut IndexSet, ) { // Add predecessor if let Some(prev_id) = prev_block_id { diff --git a/compiler/crates/react_compiler_lowering/src/identifier_loc_index.rs b/compiler/crates/react_compiler_lowering/src/identifier_loc_index.rs index 4a8b5e02467..eb6964e92c8 100644 --- a/compiler/crates/react_compiler_lowering/src/identifier_loc_index.rs +++ b/compiler/crates/react_compiler_lowering/src/identifier_loc_index.rs @@ -5,7 +5,7 @@ //! lookups; each entry also stores `start` (byte offset) for range-containment //! checks in `gather_captured_context`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_ast::expressions::*; use react_compiler_ast::jsx::JSXIdentifier; @@ -45,7 +45,7 @@ pub struct IdentifierLocEntry { /// Index mapping node_id → IdentifierLocEntry for all Identifier /// and JSXIdentifier nodes in a function's AST. -pub type IdentifierLocIndex = HashMap; +pub type IdentifierLocIndex = FxHashMap; struct IdentifierLocVisitor { index: IdentifierLocIndex, @@ -268,7 +268,7 @@ pub fn build_identifier_loc_index( .unwrap_or(scope_info.program_scope); let mut visitor = IdentifierLocVisitor { - index: HashMap::new(), + index: FxHashMap::default(), current_opening_element_loc: None, }; let mut walker = AstWalker::with_initial_scope(scope_info, func_scope); diff --git a/compiler/crates/react_compiler_optimization/Cargo.toml b/compiler/crates/react_compiler_optimization/Cargo.toml index bdbb4d52769..f7801fc1522 100644 --- a/compiler/crates/react_compiler_optimization/Cargo.toml +++ b/compiler/crates/react_compiler_optimization/Cargo.toml @@ -9,3 +9,4 @@ react_compiler_hir = { path = "../react_compiler_hir" } react_compiler_lowering = { path = "../react_compiler_lowering" } react_compiler_ssa = { path = "../react_compiler_ssa" } indexmap = "2" +rustc-hash = "2" diff --git a/compiler/crates/react_compiler_optimization/src/constant_propagation.rs b/compiler/crates/react_compiler_optimization/src/constant_propagation.rs index 3b227b5b464..6aa526a0c3f 100644 --- a/compiler/crates/react_compiler_optimization/src/constant_propagation.rs +++ b/compiler/crates/react_compiler_optimization/src/constant_propagation.rs @@ -24,7 +24,7 @@ //! //! Analogous to TS `Optimization/ConstantPropagation.ts`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::JsString; use react_compiler_hir::environment::Environment; @@ -68,16 +68,16 @@ impl Constant { } } -/// Map of known constant values. Uses HashMap (not IndexMap) since iteration +/// Map of known constant values. Uses FxHashMap (not IndexMap) since iteration /// order does not affect correctness — this map is only used for lookups. -type Constants = HashMap; +type Constants = FxHashMap; // ============================================================================= // Public entry point // ============================================================================= pub fn constant_propagation(func: &mut HirFunction, env: &mut Environment) { - let mut constants: Constants = HashMap::new(); + let mut constants: Constants = FxHashMap::default(); constant_propagation_impl(func, env, &mut constants); } diff --git a/compiler/crates/react_compiler_optimization/src/dead_code_elimination.rs b/compiler/crates/react_compiler_optimization/src/dead_code_elimination.rs index f3a56367453..20f90e98051 100644 --- a/compiler/crates/react_compiler_optimization/src/dead_code_elimination.rs +++ b/compiler/crates/react_compiler_optimization/src/dead_code_elimination.rs @@ -11,7 +11,7 @@ //! //! Ported from TypeScript `src/Optimization/DeadCodeElimination.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::environment::{Environment, OutputMode}; use react_compiler_hir::object_shape::HookKind; @@ -69,16 +69,16 @@ pub fn dead_code_elimination(func: &mut HirFunction, env: &Environment) { /// State for tracking referenced identifiers during mark phase. struct State { /// SSA-specific usages (by IdentifierId) - identifiers: HashSet, + identifiers: FxHashSet, /// Named variable usages (any version) - named: HashSet, + named: FxHashSet, } impl State { fn new() -> Self { State { - identifiers: HashSet::new(), - named: HashSet::new(), + identifiers: FxHashSet::default(), + named: FxHashSet::default(), } } @@ -409,7 +409,7 @@ fn pruneable_value(value: &InstructionValue, state: &State, env: &Environment) - /// Check if the CFG has any back edges (indicating loops). fn has_back_edge(func: &HirFunction) -> bool { - let mut visited: HashSet = HashSet::new(); + let mut visited: FxHashSet = FxHashSet::default(); for (block_id, block) in &func.body.blocks { for pred_id in &block.preds { if !visited.contains(pred_id) { diff --git a/compiler/crates/react_compiler_optimization/src/drop_manual_memoization.rs b/compiler/crates/react_compiler_optimization/src/drop_manual_memoization.rs index d17793df353..1f98dc10d13 100644 --- a/compiler/crates/react_compiler_optimization/src/drop_manual_memoization.rs +++ b/compiler/crates/react_compiler_optimization/src/drop_manual_memoization.rs @@ -12,8 +12,7 @@ //! //! Analogous to TS `Inference/DropManualMemoization.ts`. -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::CompilerDiagnostic; use react_compiler_diagnostics::CompilerDiagnosticDetail; @@ -58,17 +57,17 @@ struct ManualMemoCallee { struct IdentifierSidemap { /// Maps identifier id -> InstructionId of FunctionExpression instructions - functions: HashSet, + functions: FxHashSet, /// Maps identifier id -> ManualMemoCallee for useMemo/useCallback callees - manual_memos: HashMap, + manual_memos: FxHashMap, /// Set of identifier ids that loaded 'React' global - react: HashSet, + react: FxHashSet, /// Maps identifier id -> deps list info for array expressions - maybe_deps_lists: HashMap, + maybe_deps_lists: FxHashMap, /// Maps identifier id -> ManualMemoDependency for dependency tracking - maybe_deps: HashMap, + maybe_deps: FxHashMap, /// Set of identifier ids that are results of optional chains - optionals: HashSet, + optionals: FxHashSet, } #[derive(Debug, Clone)] @@ -99,11 +98,11 @@ pub fn drop_manual_memoization( let optionals = find_optional_places(func)?; let mut sidemap = IdentifierSidemap { - functions: HashSet::new(), - manual_memos: HashMap::new(), - react: HashSet::new(), - maybe_deps: HashMap::new(), - maybe_deps_lists: HashMap::new(), + functions: FxHashSet::default(), + manual_memos: FxHashMap::default(), + react: FxHashSet::default(), + maybe_deps: FxHashMap::default(), + maybe_deps_lists: FxHashMap::default(), optionals, }; let mut next_manual_memo_id: u32 = 0; @@ -113,7 +112,7 @@ pub fn drop_manual_memoization( // - (if validation is enabled) collect manual memoization markers // // queued_inserts maps InstructionId -> new Instruction to insert after that instruction - let mut queued_inserts: HashMap = HashMap::new(); + let mut queued_inserts: FxHashMap = FxHashMap::default(); // Collect all block instruction lists up front to avoid borrowing func immutably // while needing to mutate it @@ -202,7 +201,7 @@ fn process_manual_memo_call( sidemap: &mut IdentifierSidemap, is_validation_enabled: bool, next_manual_memo_id: &mut u32, - queued_inserts: &mut HashMap, + queued_inserts: &mut FxHashMap, ) { let instr = &func.instructions[instr_id.0 as usize]; @@ -386,7 +385,7 @@ fn collect_temporaries( /// Returns the variable + property reads represented by the instruction value. pub fn collect_maybe_memo_dependencies( value: &InstructionValue, - maybe_deps: &HashMap, + maybe_deps: &FxHashMap, optional: bool, env: &Environment, ) -> Option { @@ -649,10 +648,10 @@ fn extract_manual_memoization_args( // findOptionalPlaces // ============================================================================= -fn find_optional_places(func: &HirFunction) -> Result, CompilerDiagnostic> { +fn find_optional_places(func: &HirFunction) -> Result, CompilerDiagnostic> { use react_compiler_hir::Terminal; - let mut optionals = HashSet::new(); + let mut optionals = FxHashSet::default(); for block in func.body.blocks.values() { if let Terminal::Optional { optional: true, diff --git a/compiler/crates/react_compiler_optimization/src/inline_iifes.rs b/compiler/crates/react_compiler_optimization/src/inline_iifes.rs index 32d15eb8320..c951c554f0b 100644 --- a/compiler/crates/react_compiler_optimization/src/inline_iifes.rs +++ b/compiler/crates/react_compiler_optimization/src/inline_iifes.rs @@ -40,7 +40,8 @@ //! //! Analogous to TS `Inference/InlineImmediatelyInvokedFunctionExpressions.ts`. -use std::collections::{HashMap, HashSet}; +use indexmap::IndexSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::environment::Environment; use react_compiler_hir::visitors; @@ -62,9 +63,9 @@ pub fn inline_immediately_invoked_function_expressions( env: &mut Environment, ) { // Track all function expressions that are assigned to a temporary - let mut functions: HashMap = HashMap::new(); + let mut functions: FxHashMap = FxHashMap::default(); // Functions that are inlined (by identifier id of the callee) - let mut inlined_functions: HashSet = HashSet::new(); + let mut inlined_functions: FxHashSet = FxHashSet::default(); // Iterate the *existing* blocks from the outer component to find IIFEs // and inline them. During iteration we will modify `func` (by inlining the CFG @@ -140,7 +141,7 @@ pub fn inline_immediately_invoked_function_expressions( instructions: continuation_instructions, kind: block_kind, phis: Vec::new(), - preds: indexmap::IndexSet::new(), + preds: IndexSet::default(), terminal: continuation_terminal, }; func.body diff --git a/compiler/crates/react_compiler_optimization/src/merge_consecutive_blocks.rs b/compiler/crates/react_compiler_optimization/src/merge_consecutive_blocks.rs index 3ef96f64dba..8451bec99f8 100644 --- a/compiler/crates/react_compiler_optimization/src/merge_consecutive_blocks.rs +++ b/compiler/crates/react_compiler_optimization/src/merge_consecutive_blocks.rs @@ -13,7 +13,7 @@ //! //! Analogous to TS `HIR/MergeConsecutiveBlocks.ts`. -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::visitors; use react_compiler_hir::{ @@ -53,7 +53,7 @@ pub fn merge_consecutive_blocks(func: &mut HirFunction, functions: &mut [HirFunc } // Build fallthrough set - let mut fallthrough_blocks: HashSet = HashSet::new(); + let mut fallthrough_blocks: FxHashSet = FxHashSet::default(); for block in func.body.blocks.values() { if let Some(ft) = visitors::terminal_fallthrough(&block.terminal) { fallthrough_blocks.insert(ft); @@ -186,13 +186,13 @@ pub fn merge_consecutive_blocks(func: &mut HirFunction, functions: &mut [HirFunc /// Tracks which blocks have been merged and into which target. struct MergedBlocks { - map: HashMap, + map: FxHashMap, } impl MergedBlocks { fn new() -> Self { Self { - map: HashMap::new(), + map: FxHashMap::default(), } } diff --git a/compiler/crates/react_compiler_optimization/src/name_anonymous_functions.rs b/compiler/crates/react_compiler_optimization/src/name_anonymous_functions.rs index 8f9c2f080d3..7f3b17218d6 100644 --- a/compiler/crates/react_compiler_optimization/src/name_anonymous_functions.rs +++ b/compiler/crates/react_compiler_optimization/src/name_anonymous_functions.rs @@ -11,7 +11,7 @@ //! //! Conditional on `env.config.enable_name_anonymous_functions`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_hir::environment::Environment; use react_compiler_hir::object_shape::HookKind; @@ -63,7 +63,7 @@ pub fn name_anonymous_functions(func: &mut HirFunction, env: &mut Environment) { if updates.is_empty() { return; } - let update_map: HashMap = + let update_map: FxHashMap = updates.iter().map(|(fid, name)| (*fid, name)).collect(); // Apply name updates to the inner HirFunction in the arena @@ -86,7 +86,7 @@ pub fn name_anonymous_functions(func: &mut HirFunction, env: &mut Environment) { /// Apply name hints to FunctionExpression instruction values. fn apply_name_hints_to_instructions( instructions: &mut [Instruction], - update_map: &HashMap, + update_map: &FxHashMap, ) { for instr in instructions.iter_mut() { if let InstructionValue::FunctionExpression { @@ -117,9 +117,9 @@ struct Node { fn name_anonymous_functions_impl(func: &HirFunction, env: &Environment) -> Vec { // Functions that we track to generate names for - let mut functions: HashMap = HashMap::new(); + let mut functions: FxHashMap = FxHashMap::default(); // Tracks temporaries that read from variables/globals/properties - let mut names: HashMap = HashMap::new(); + let mut names: FxHashMap = FxHashMap::default(); // Tracks all function nodes let mut nodes: Vec = Vec::new(); @@ -256,8 +256,8 @@ fn handle_call( _func: &HirFunction, callee_id: IdentifierId, args: &[PlaceOrSpread], - functions: &mut HashMap, - names: &HashMap, + functions: &mut FxHashMap, + names: &FxHashMap, nodes: &mut Vec, ) { let callee_ident = &env.identifiers[callee_id.0 as usize]; diff --git a/compiler/crates/react_compiler_optimization/src/optimize_for_ssr.rs b/compiler/crates/react_compiler_optimization/src/optimize_for_ssr.rs index b4a6f251d4d..64074010ef2 100644 --- a/compiler/crates/react_compiler_optimization/src/optimize_for_ssr.rs +++ b/compiler/crates/react_compiler_optimization/src/optimize_for_ssr.rs @@ -17,7 +17,7 @@ //! //! Ported from TypeScript `src/Optimization/OptimizeForSSR.ts`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_hir::environment::Environment; use react_compiler_hir::object_shape::HookKind; @@ -43,7 +43,7 @@ pub fn optimize_for_ssr(func: &mut HirFunction, env: &Environment) { // Any use of the hook return other than the expected destructuring pattern // prevents inlining (we delete from inlined_state if we see the identifier used // as an operand elsewhere). - let mut inlined_state: HashMap = HashMap::new(); + let mut inlined_state: FxHashMap = FxHashMap::default(); for (_block_id, block) in &func.body.blocks { for &instr_id in &block.instructions { diff --git a/compiler/crates/react_compiler_optimization/src/outline_functions.rs b/compiler/crates/react_compiler_optimization/src/outline_functions.rs index 8c51ecc2ed7..2f2092cae5c 100644 --- a/compiler/crates/react_compiler_optimization/src/outline_functions.rs +++ b/compiler/crates/react_compiler_optimization/src/outline_functions.rs @@ -11,7 +11,7 @@ //! //! Conditional on `env.config.enable_function_outlining`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::environment::Environment; use react_compiler_hir::{ @@ -25,7 +25,7 @@ use react_compiler_ssa::enter_ssa::placeholder_function; pub fn outline_functions( func: &mut HirFunction, env: &mut Environment, - fbt_operands: &HashSet, + fbt_operands: &FxHashSet, ) { // Collect per-instruction actions to maintain depth-first name allocation order. // Each entry: (instr index, function_id to recurse into, should_outline) diff --git a/compiler/crates/react_compiler_optimization/src/outline_jsx.rs b/compiler/crates/react_compiler_optimization/src/outline_jsx.rs index 75a7fd97fe7..d9069bc92dd 100644 --- a/compiler/crates/react_compiler_optimization/src/outline_jsx.rs +++ b/compiler/crates/react_compiler_optimization/src/outline_jsx.rs @@ -8,9 +8,9 @@ //! Outlines JSX expressions in callbacks into separate component functions. //! This pass is conditional on `env.config.enable_jsx_outlining` (defaults to false). -use std::collections::{HashMap, HashSet}; +use indexmap::{IndexMap, IndexSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; -use indexmap::IndexMap; use react_compiler_hir::environment::Environment; use react_compiler_hir::{ BasicBlock, BlockId, BlockKind, EvaluationOrder, FunctionId, HIR, HirFunction, IdentifierId, @@ -58,7 +58,7 @@ fn outline_jsx_impl( outlined_fns: &mut Vec, ) { // Collect LoadGlobal instructions (tag -> instr) - let mut globals: HashMap = HashMap::new(); // id -> instr_idx + let mut globals: FxHashMap = FxHashMap::default(); // id -> instr_idx // Process each block let block_ids: Vec = func.body.blocks.keys().copied().collect(); @@ -66,9 +66,9 @@ fn outline_jsx_impl( let block = &func.body.blocks[block_id]; let instr_ids = block.instructions.clone(); - let mut rewrite_instr: HashMap> = HashMap::new(); + let mut rewrite_instr: FxHashMap> = FxHashMap::default(); let mut jsx_group: Vec = Vec::new(); - let mut children_ids: HashSet = HashSet::new(); + let mut children_ids: FxHashSet = FxHashSet::default(); // First pass: collect all instruction info without borrowing func mutably enum InstrAction { @@ -211,8 +211,8 @@ fn process_and_outline_jsx( func: &mut HirFunction, env: &mut Environment, jsx_group: &mut Vec, - globals: &HashMap, - rewrite_instr: &mut HashMap>, + globals: &FxHashMap, + rewrite_instr: &mut FxHashMap>, outlined_fns: &mut Vec, ) { if jsx_group.len() <= 1 { @@ -237,7 +237,7 @@ fn process_jsx_group( func: &HirFunction, env: &mut Environment, jsx_group: &[JsxInstrInfo], - globals: &HashMap, + globals: &FxHashMap, ) -> Option { // Only outline in callbacks, not top-level components if func.fn_type == ReactFunctionType::Component { @@ -266,9 +266,9 @@ fn collect_props( jsx_group: &[JsxInstrInfo], ) -> Option> { let mut id_counter = 1u32; - let mut seen: HashSet = HashSet::new(); + let mut seen: FxHashSet = FxHashSet::default(); let mut attributes = Vec::new(); - let jsx_ids: HashSet = jsx_group.iter().map(|j| j.lvalue_id).collect(); + let jsx_ids: FxHashSet = jsx_group.iter().map(|j| j.lvalue_id).collect(); let mut generate_name = |old_name: &str, _env: &mut Environment| -> String { let mut new_name = old_name.to_string(); @@ -402,7 +402,7 @@ fn emit_outlined_fn( env: &mut Environment, jsx_group: &[JsxInstrInfo], old_props: &[OutlinedJsxAttribute], - globals: &HashMap, + globals: &FxHashMap, ) -> Option { let old_to_new_props = create_old_to_new_props_mapping(env, old_props); @@ -458,7 +458,7 @@ fn emit_outlined_fn( kind: BlockKind::Block, id: BlockId(0), instructions: instr_ids, - preds: indexmap::IndexSet::new(), + preds: IndexSet::default(), terminal: Terminal::Return { value: last_lvalue, return_variant: ReturnVariant::Explicit, @@ -469,7 +469,7 @@ fn emit_outlined_fn( phis: Vec::new(), }; - let mut blocks = IndexMap::new(); + let mut blocks = IndexMap::default(); blocks.insert(BlockId(0), block); let outlined_fn = HirFunction { @@ -498,7 +498,7 @@ fn emit_outlined_fn( fn emit_load_globals( func: &HirFunction, jsx_group: &[JsxInstrInfo], - globals: &HashMap, + globals: &FxHashMap, ) -> Option> { let mut instructions = Vec::new(); for info in jsx_group { @@ -516,9 +516,9 @@ fn emit_load_globals( fn emit_updated_jsx( func: &HirFunction, jsx_group: &[JsxInstrInfo], - old_to_new_props: &IndexMap, + old_to_new_props: &IndexMap, ) -> Vec { - let jsx_ids: HashSet = jsx_group.iter().map(|j| j.lvalue_id).collect(); + let jsx_ids: FxHashSet = jsx_group.iter().map(|j| j.lvalue_id).collect(); let mut new_instrs = Vec::new(); for info in jsx_group { @@ -594,8 +594,8 @@ fn emit_updated_jsx( fn create_old_to_new_props_mapping( env: &mut Environment, old_props: &[OutlinedJsxAttribute], -) -> IndexMap { - let mut old_to_new = IndexMap::new(); +) -> IndexMap { + let mut old_to_new = IndexMap::default(); for old_prop in old_props { if old_prop.original_name == "key" { @@ -629,7 +629,7 @@ fn create_old_to_new_props_mapping( fn emit_destructure_props( env: &mut Environment, props_obj: &Place, - old_to_new_props: &IndexMap, + old_to_new_props: &IndexMap, ) -> Instruction { let mut properties = Vec::new(); for prop in old_to_new_props.values() { diff --git a/compiler/crates/react_compiler_optimization/src/prune_maybe_throws.rs b/compiler/crates/react_compiler_optimization/src/prune_maybe_throws.rs index c6d959f293a..49ee2550242 100644 --- a/compiler/crates/react_compiler_optimization/src/prune_maybe_throws.rs +++ b/compiler/crates/react_compiler_optimization/src/prune_maybe_throws.rs @@ -10,7 +10,7 @@ //! //! Analogous to TS `Optimization/PruneMaybeThrows.ts`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, GENERATED_SOURCE, @@ -86,8 +86,8 @@ pub fn prune_maybe_throws( Ok(()) } -fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option> { - let mut terminal_mapping: HashMap = HashMap::new(); +fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option> { + let mut terminal_mapping: FxHashMap = FxHashMap::default(); let instructions = &func.instructions; for block in func.body.blocks.values_mut() { diff --git a/compiler/crates/react_compiler_optimization/src/prune_unused_labels_hir.rs b/compiler/crates/react_compiler_optimization/src/prune_unused_labels_hir.rs index 7858314038f..8eea52cf774 100644 --- a/compiler/crates/react_compiler_optimization/src/prune_unused_labels_hir.rs +++ b/compiler/crates/react_compiler_optimization/src/prune_unused_labels_hir.rs @@ -12,7 +12,7 @@ //! Analogous to TS `PruneUnusedLabelsHIR.ts`. use react_compiler_hir::{BlockId, BlockKind, GotoVariant, HirFunction, Terminal}; -use std::collections::HashMap; +use rustc_hash::FxHashMap; pub fn prune_unused_labels_hir(func: &mut HirFunction) { // Phase 1: Identify label terminals whose body block immediately breaks @@ -45,7 +45,7 @@ pub fn prune_unused_labels_hir(func: &mut HirFunction) { } // Phase 2: Apply merges - let mut rewrites: HashMap = HashMap::new(); + let mut rewrites: FxHashMap = FxHashMap::default(); for (original_label_id, next_id, fallthrough_id) in &merged { let label_id = rewrites diff --git a/compiler/crates/react_compiler_reactive_scopes/Cargo.toml b/compiler/crates/react_compiler_reactive_scopes/Cargo.toml index 83ce70f37f5..4718cabb076 100644 --- a/compiler/crates/react_compiler_reactive_scopes/Cargo.toml +++ b/compiler/crates/react_compiler_reactive_scopes/Cargo.toml @@ -8,5 +8,6 @@ react_compiler_ast = { path = "../react_compiler_ast" } react_compiler_diagnostics = { path = "../react_compiler_diagnostics" } react_compiler_hir = { path = "../react_compiler_hir" } indexmap = "2" +rustc-hash = "2" serde_json = "1" hmac-sha256 = "1" diff --git a/compiler/crates/react_compiler_reactive_scopes/src/assert_scope_instructions_within_scopes.rs b/compiler/crates/react_compiler_reactive_scopes/src/assert_scope_instructions_within_scopes.rs index 2a14823eafd..d5e69bd6f54 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/assert_scope_instructions_within_scopes.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/assert_scope_instructions_within_scopes.rs @@ -8,7 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/AssertScopeInstructionsWithinScope.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory}; use react_compiler_hir::environment::Environment; @@ -25,7 +25,7 @@ pub fn assert_scope_instructions_within_scopes( env: &Environment, ) -> Result<(), CompilerDiagnostic> { // Pass 1: Collect all scope IDs - let mut existing_scopes: HashSet = HashSet::new(); + let mut existing_scopes: FxHashSet = FxHashSet::default(); let find_visitor = FindAllScopesVisitor { env }; visit_reactive_function(func, &find_visitor, &mut existing_scopes); @@ -33,7 +33,7 @@ pub fn assert_scope_instructions_within_scopes( let check_visitor = CheckInstructionsAgainstScopesVisitor { env }; let mut check_state = CheckState { existing_scopes, - active_scopes: HashSet::new(), + active_scopes: FxHashSet::default(), error: None, }; visit_reactive_function(func, &check_visitor, &mut check_state); @@ -52,13 +52,13 @@ struct FindAllScopesVisitor<'a> { } impl<'a> ReactiveFunctionVisitor for FindAllScopesVisitor<'a> { - type State = HashSet; + type State = FxHashSet; fn env(&self) -> &Environment { self.env } - fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut HashSet) { + fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut FxHashSet) { self.traverse_scope(scope, state); state.insert(scope.scope); } @@ -69,8 +69,8 @@ impl<'a> ReactiveFunctionVisitor for FindAllScopesVisitor<'a> { // ============================================================================= struct CheckState { - existing_scopes: HashSet, - active_scopes: HashSet, + existing_scopes: FxHashSet, + active_scopes: FxHashSet, error: Option, } diff --git a/compiler/crates/react_compiler_reactive_scopes/src/assert_well_formed_break_targets.rs b/compiler/crates/react_compiler_reactive_scopes/src/assert_well_formed_break_targets.rs index 17b80c4587c..4e9fd81e07d 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/assert_well_formed_break_targets.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/assert_well_formed_break_targets.rs @@ -7,7 +7,7 @@ //! //! Corresponds to `src/ReactiveScopes/AssertWellFormedBreakTargets.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ BlockId, ReactiveFunction, ReactiveTerminal, ReactiveTerminalStatement, @@ -19,7 +19,7 @@ use crate::visitors::{ReactiveFunctionVisitor, visit_reactive_function}; /// Assert that all break/continue targets reference existent labels. pub fn assert_well_formed_break_targets(func: &ReactiveFunction, env: &Environment) { let visitor = Visitor { env }; - let mut state: HashSet = HashSet::new(); + let mut state: FxHashSet = FxHashSet::default(); visit_reactive_function(func, &visitor, &mut state); } @@ -28,13 +28,17 @@ struct Visitor<'a> { } impl<'a> ReactiveFunctionVisitor for Visitor<'a> { - type State = HashSet; + type State = FxHashSet; fn env(&self) -> &Environment { self.env } - fn visit_terminal(&self, stmt: &ReactiveTerminalStatement, seen_labels: &mut HashSet) { + fn visit_terminal( + &self, + stmt: &ReactiveTerminalStatement, + seen_labels: &mut FxHashSet, + ) { if let Some(label) = &stmt.label { seen_labels.insert(label.id); } diff --git a/compiler/crates/react_compiler_reactive_scopes/src/build_reactive_function.rs b/compiler/crates/react_compiler_reactive_scopes/src/build_reactive_function.rs index 181e0977634..726920a34ff 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/build_reactive_function.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/build_reactive_function.rs @@ -7,7 +7,7 @@ //! //! Corresponds to `src/ReactiveScopes/BuildReactiveFunction.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation, @@ -108,10 +108,10 @@ impl ControlFlowTarget { struct Context<'a> { ir: &'a HirFunction, next_schedule_id: u32, - emitted: HashSet, - scope_fallthroughs: HashSet, - scheduled: HashSet, - catch_handlers: HashSet, + emitted: FxHashSet, + scope_fallthroughs: FxHashSet, + scheduled: FxHashSet, + catch_handlers: FxHashSet, control_flow_stack: Vec, } @@ -120,10 +120,10 @@ impl<'a> Context<'a> { Self { ir, next_schedule_id: 0, - emitted: HashSet::new(), - scope_fallthroughs: HashSet::new(), - scheduled: HashSet::new(), - catch_handlers: HashSet::new(), + emitted: FxHashSet::default(), + scope_fallthroughs: FxHashSet::default(), + scheduled: FxHashSet::default(), + catch_handlers: FxHashSet::default(), control_flow_stack: Vec::new(), } } diff --git a/compiler/crates/react_compiler_reactive_scopes/src/codegen_reactive_function.rs b/compiler/crates/react_compiler_reactive_scopes/src/codegen_reactive_function.rs index 98aa3935868..9a722b105a0 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/codegen_reactive_function.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/codegen_reactive_function.rs @@ -10,8 +10,7 @@ //! //! Corresponds to `src/ReactiveScopes/CodegenReactiveFunction.ts` in the TS compiler. -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_ast::common::BaseNode; use react_compiler_ast::common::Position as AstPosition; @@ -195,8 +194,8 @@ fn source_file_hash(code: &str) -> String { pub fn codegen_function( func: &ReactiveFunction, env: &mut Environment, - unique_identifiers: HashSet, - fbt_operands: HashSet, + unique_identifiers: FxHashSet, + fbt_operands: FxHashSet, ) -> Result { let fn_name = func.id.as_deref().unwrap_or("[[ anonymous ]]"); let mut cx = Context::new(env, fn_name.to_string(), unique_identifiers, fbt_operands); @@ -556,7 +555,7 @@ pub fn codegen_function( // Context // ============================================================================= -type Temporaries = HashMap>; +type Temporaries = FxHashMap>; #[derive(Clone)] enum ExpressionOrJsxText { @@ -569,37 +568,37 @@ struct Context<'env> { #[allow(dead_code)] fn_name: String, next_cache_index: u32, - declarations: HashSet, + declarations: FxHashSet, temp: Temporaries, - object_methods: HashMap< + object_methods: FxHashMap< IdentifierId, ( InstructionValue, Option, ), >, - unique_identifiers: HashSet, - fbt_operands: HashSet, - synthesized_names: HashMap, + unique_identifiers: FxHashSet, + fbt_operands: FxHashSet, + synthesized_names: FxHashMap, } impl<'env> Context<'env> { fn new( env: &'env mut Environment, fn_name: String, - unique_identifiers: HashSet, - fbt_operands: HashSet, + unique_identifiers: FxHashSet, + fbt_operands: FxHashSet, ) -> Self { Context { env, fn_name, next_cache_index: 0, - declarations: HashSet::new(), - temp: HashMap::new(), - object_methods: HashMap::new(), + declarations: FxHashSet::default(), + temp: FxHashMap::default(), + object_methods: FxHashMap::default(), unique_identifiers, fbt_operands, - synthesized_names: HashMap::new(), + synthesized_names: FxHashMap::default(), } } @@ -4163,7 +4162,7 @@ fn create_function_body_hook_guard( fn apply_renames_to_json( value: &mut serde_json::Value, renames: &[react_compiler_hir::environment::BindingRename], - reference_node_ids: &std::collections::HashSet, + reference_node_ids: &rustc_hash::FxHashSet, ) { apply_renames_to_json_inner(value, renames, reference_node_ids, false); } @@ -4171,7 +4170,7 @@ fn apply_renames_to_json( fn apply_renames_to_json_inner( value: &mut serde_json::Value, renames: &[react_compiler_hir::environment::BindingRename], - reference_node_ids: &std::collections::HashSet, + reference_node_ids: &rustc_hash::FxHashSet, is_property_key: bool, ) { if renames.is_empty() { diff --git a/compiler/crates/react_compiler_reactive_scopes/src/extract_scope_declarations_from_destructuring.rs b/compiler/crates/react_compiler_reactive_scopes/src/extract_scope_declarations_from_destructuring.rs index dea0736828b..70f42a0544f 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/extract_scope_declarations_from_destructuring.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/extract_scope_declarations_from_destructuring.rs @@ -8,7 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/ExtractScopeDeclarationsFromDestructuring.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ DeclarationId, IdentifierId, IdentifierName, InstructionKind, InstructionValue, LValue, @@ -29,7 +29,7 @@ pub fn extract_scope_declarations_from_destructuring( func: &mut ReactiveFunction, env: &mut Environment, ) -> Result<(), react_compiler_diagnostics::CompilerError> { - let mut declared: HashSet = HashSet::new(); + let mut declared: FxHashSet = FxHashSet::default(); for param in &func.params { let place = match param { ParamPattern::Place(p) => p, @@ -44,7 +44,7 @@ pub fn extract_scope_declarations_from_destructuring( } struct ExtractState { - declared: HashSet, + declared: FxHashSet, } struct Transform<'a> { @@ -94,7 +94,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> { }) = &mut instruction.value { // Check if this is a mixed destructuring (some declared, some not) - let mut reassigned: HashSet = HashSet::new(); + let mut reassigned: FxHashSet = FxHashSet::default(); let mut has_declaration = false; for place in visitors::each_pattern_operand(&lvalue.pattern) { diff --git a/compiler/crates/react_compiler_reactive_scopes/src/merge_reactive_scopes_that_invalidate_together.rs b/compiler/crates/react_compiler_reactive_scopes/src/merge_reactive_scopes_that_invalidate_together.rs index 349a5480f51..6971e22bcc5 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/merge_reactive_scopes_that_invalidate_together.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/merge_reactive_scopes_that_invalidate_together.rs @@ -8,7 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts`. -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::CompilerError; use react_compiler_hir::{ @@ -36,14 +36,14 @@ pub fn merge_reactive_scopes_that_invalidate_together( ) -> Result<(), CompilerError> { // Pass 1: find last usage of each declaration let visitor = FindLastUsageVisitor { env: &*env }; - let mut last_usage: HashMap = HashMap::new(); + let mut last_usage: FxHashMap = FxHashMap::default(); visit_reactive_function(func, &visitor, &mut last_usage); // Pass 2+3: merge scopes let mut transform = MergeTransform { env, last_usage, - temporaries: HashMap::new(), + temporaries: FxHashMap::default(), }; let mut state: Option> = None; transform_reactive_function(func, &mut transform, &mut state) @@ -59,7 +59,7 @@ struct FindLastUsageVisitor<'a> { } impl<'a> ReactiveFunctionVisitor for FindLastUsageVisitor<'a> { - type State = HashMap; + type State = FxHashMap; fn env(&self) -> &Environment { self.env @@ -81,8 +81,8 @@ impl<'a> ReactiveFunctionVisitor for FindLastUsageVisitor<'a> { /// TS: `class Transform extends ReactiveFunctionTransform` struct MergeTransform<'a> { env: &'a mut Environment, - last_usage: HashMap, - temporaries: HashMap, + last_usage: FxHashMap, + temporaries: FxHashMap, } impl<'a> ReactiveFunctionTransform for MergeTransform<'a> { @@ -138,7 +138,7 @@ impl<'a> MergeTransform<'a> { scope_id: ScopeId, from: usize, to: usize, - lvalues: HashSet, + lvalues: FxHashSet, } let mut current: Option = None; @@ -308,7 +308,7 @@ impl<'a> MergeTransform<'a> { scope_id: next_scope_id, from: i, to: i + 1, - lvalues: HashSet::new(), + lvalues: FxHashSet::default(), }); } } @@ -319,7 +319,7 @@ impl<'a> MergeTransform<'a> { scope_id: next_scope_id, from: i, to: i + 1, - lvalues: HashSet::new(), + lvalues: FxHashSet::default(), }); } } @@ -398,7 +398,7 @@ impl<'a> MergeTransform<'a> { /// Updates scope declarations to remove any that are not used after the scope. fn update_scope_declarations( scope_id: ScopeId, - last_usage: &HashMap, + last_usage: &FxHashMap, env: &mut Environment, ) { let range_end = env.scopes[scope_id.0 as usize].range.end; @@ -417,8 +417,8 @@ fn update_scope_declarations( /// Returns whether all lvalues are last used at or before the given scope. fn are_lvalues_last_used_by_scope( scope_id: ScopeId, - lvalues: &HashSet, - last_usage: &HashMap, + lvalues: &FxHashSet, + last_usage: &FxHashMap, env: &Environment, ) -> bool { let range_end = env.scopes[scope_id.0 as usize].range.end; @@ -437,7 +437,7 @@ fn can_merge_scopes( current_id: ScopeId, next_id: ScopeId, env: &Environment, - temporaries: &HashMap, + temporaries: &FxHashMap, ) -> bool { let current = &env.scopes[current_id.0 as usize]; let next = &env.scopes[next_id.0 as usize]; diff --git a/compiler/crates/react_compiler_reactive_scopes/src/promote_used_temporaries.rs b/compiler/crates/react_compiler_reactive_scopes/src/promote_used_temporaries.rs index bfd79bf96f5..1334c4a0b7a 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/promote_used_temporaries.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/promote_used_temporaries.rs @@ -8,8 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/PromoteUsedTemporaries.ts`. -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::DeclarationId; use react_compiler_hir::FunctionId; @@ -35,9 +34,9 @@ use react_compiler_hir::environment::Environment; // ============================================================================= struct State { - tags: HashSet, - promoted: HashSet, - pruned: HashMap, + tags: FxHashSet, + promoted: FxHashSet, + pruned: FxHashMap, } struct PrunedInfo { @@ -53,9 +52,9 @@ struct PrunedInfo { /// TS: `promoteUsedTemporaries` pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environment) { let mut state = State { - tags: HashSet::new(), - promoted: HashSet::new(), - pruned: HashMap::new(), + tags: FxHashSet::default(), + promoted: FxHashSet::default(), + pruned: FxHashMap::default(), }; // Phase 1: collect promotable temporaries (jsx tags, pruned scope usage) @@ -78,8 +77,8 @@ pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environme promote_temporaries_block(&func.body, &mut state, env); // Phase 3: promote interposed temporaries - let mut consts: HashSet = HashSet::new(); - let mut globals: HashSet = HashSet::new(); + let mut consts: FxHashSet = FxHashSet::default(); + let mut globals: FxHashSet = FxHashSet::default(); for param in &func.params { match param { ParamPattern::Place(p) => { @@ -90,7 +89,7 @@ pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environme } } } - let mut inter_state: HashMap = HashMap::new(); + let mut inter_state: FxHashMap = FxHashMap::default(); promote_interposed_block( &func.body, &mut state, @@ -555,9 +554,9 @@ fn visit_hir_function_for_promotion(func_id: FunctionId, state: &mut State, env: fn promote_interposed_block( block: &ReactiveBlock, state: &mut State, - inter_state: &mut HashMap, - consts: &mut HashSet, - globals: &mut HashSet, + inter_state: &mut FxHashMap, + consts: &mut FxHashSet, + globals: &mut FxHashSet, env: &mut Environment, ) { for stmt in block { @@ -595,8 +594,8 @@ fn promote_interposed_block( fn promote_interposed_place( place: &Place, state: &mut State, - inter_state: &mut HashMap, - consts: &HashSet, + inter_state: &mut FxHashMap, + consts: &FxHashSet, env: &mut Environment, ) { if let Some(&(id, needs_promotion)) = inter_state.get(&place.identifier) { @@ -610,9 +609,9 @@ fn promote_interposed_place( fn promote_interposed_instruction( instr: &ReactiveInstruction, state: &mut State, - inter_state: &mut HashMap, - consts: &mut HashSet, - globals: &mut HashSet, + inter_state: &mut FxHashMap, + consts: &mut FxHashSet, + globals: &mut FxHashSet, env: &mut Environment, ) { // Check instruction value lvalues (assignment targets) @@ -803,9 +802,9 @@ fn promote_interposed_instruction( fn promote_interposed_value( value: &ReactiveValue, state: &mut State, - inter_state: &mut HashMap, - consts: &mut HashSet, - globals: &mut HashSet, + inter_state: &mut FxHashMap, + consts: &mut FxHashSet, + globals: &mut FxHashSet, env: &mut Environment, ) { match value { @@ -847,9 +846,9 @@ fn promote_interposed_value( fn promote_interposed_terminal( stmt: &ReactiveTerminalStatement, state: &mut State, - inter_state: &mut HashMap, - consts: &mut HashSet, - globals: &mut HashSet, + inter_state: &mut FxHashMap, + consts: &mut FxHashSet, + globals: &mut FxHashSet, env: &mut Environment, ) { match &stmt.terminal { diff --git a/compiler/crates/react_compiler_reactive_scopes/src/prune_always_invalidating_scopes.rs b/compiler/crates/react_compiler_reactive_scopes/src/prune_always_invalidating_scopes.rs index 5039a910aac..ea713e8c5f4 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/prune_always_invalidating_scopes.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/prune_always_invalidating_scopes.rs @@ -11,7 +11,7 @@ //! //! Corresponds to `src/ReactiveScopes/PruneAlwaysInvalidatingScopes.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ IdentifierId, InstructionValue, PrunedReactiveScopeBlock, ReactiveFunction, @@ -30,8 +30,8 @@ pub fn prune_always_invalidating_scopes( ) -> Result<(), react_compiler_diagnostics::CompilerError> { let mut transform = Transform { env, - always_invalidating_values: HashSet::new(), - unmemoized_values: HashSet::new(), + always_invalidating_values: FxHashSet::default(), + unmemoized_values: FxHashSet::default(), }; let mut state = false; // withinScope transform_reactive_function(func, &mut transform, &mut state) @@ -39,8 +39,8 @@ pub fn prune_always_invalidating_scopes( struct Transform<'a> { env: &'a Environment, - always_invalidating_values: HashSet, - unmemoized_values: HashSet, + always_invalidating_values: FxHashSet, + unmemoized_values: FxHashSet, } impl<'a> ReactiveFunctionTransform for Transform<'a> { diff --git a/compiler/crates/react_compiler_reactive_scopes/src/prune_hoisted_contexts.rs b/compiler/crates/react_compiler_reactive_scopes/src/prune_hoisted_contexts.rs index 83687410885..3cbe690a4b8 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/prune_hoisted_contexts.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/prune_hoisted_contexts.rs @@ -8,7 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/PruneHoistedContexts.ts`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{CompilerError, CompilerErrorDetail, ErrorCategory}; use react_compiler_hir::{ @@ -33,7 +33,7 @@ pub fn prune_hoisted_contexts( let mut transform = Transform { env }; let mut state = VisitorState { active_scopes: Vec::new(), - uninitialized: HashMap::new(), + uninitialized: FxHashMap::default(), }; transform_reactive_function(func, &mut transform, &mut state) } @@ -49,8 +49,8 @@ enum UninitializedKind { } struct VisitorState { - active_scopes: Vec>, - uninitialized: HashMap, + active_scopes: Vec>, + uninitialized: FxHashMap, } impl VisitorState { @@ -81,7 +81,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> { state: &mut VisitorState, ) -> Result<(), CompilerError> { let scope_data = &self.env.scopes[scope.scope.0 as usize]; - let decl_ids: std::collections::HashSet = + let decl_ids: rustc_hash::FxHashSet = scope_data.declarations.iter().map(|(id, _)| *id).collect(); // Add declared but not initialized variables diff --git a/compiler/crates/react_compiler_reactive_scopes/src/prune_non_escaping_scopes.rs b/compiler/crates/react_compiler_reactive_scopes/src/prune_non_escaping_scopes.rs index 59808894548..466d747c27e 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/prune_non_escaping_scopes.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/prune_non_escaping_scopes.rs @@ -8,8 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/PruneNonEscapingScopes.ts`. -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use indexmap::IndexSet; use react_compiler_hir::ArrayPatternElement; @@ -74,8 +73,8 @@ pub fn prune_non_escaping_scopes( // Prune scopes that do not declare/reassign any escaping values let mut transform = PruneScopesTransform { env, - pruned_scopes: HashSet::new(), - reassignments: HashMap::new(), + pruned_scopes: FxHashSet::default(), + reassignments: FxHashMap::default(), }; let mut memoized_state = memoized; transform_reactive_function(func, &mut transform, &mut memoized_state) @@ -120,8 +119,8 @@ fn join_aliases(kind1: MemoizationLevel, kind2: MemoizationLevel) -> Memoization struct IdentifierNode { level: MemoizationLevel, memoized: bool, - dependencies: IndexSet, - scopes: IndexSet, + dependencies: IndexSet, + scopes: IndexSet, seen: bool, } @@ -137,19 +136,19 @@ struct ScopeNode { struct CollectState { /// Maps lvalues for LoadLocal to the identifier being loaded, to resolve indirections. - definitions: HashMap, - identifiers: HashMap, - scopes: HashMap, - escaping_values: IndexSet, + definitions: FxHashMap, + identifiers: FxHashMap, + scopes: FxHashMap, + escaping_values: IndexSet, } impl CollectState { fn new() -> Self { CollectState { - definitions: HashMap::new(), - identifiers: HashMap::new(), - scopes: HashMap::new(), - escaping_values: IndexSet::new(), + definitions: FxHashMap::default(), + identifiers: FxHashMap::default(), + scopes: FxHashMap::default(), + escaping_values: IndexSet::default(), } } @@ -160,8 +159,8 @@ impl CollectState { IdentifierNode { level: MemoizationLevel::Never, memoized: false, - dependencies: IndexSet::new(), - scopes: IndexSet::new(), + dependencies: IndexSet::default(), + scopes: IndexSet::default(), seen: false, }, ); @@ -900,8 +899,8 @@ impl<'a> CollectDependenciesVisitor<'a> { .or_insert_with(|| IdentifierNode { level: MemoizationLevel::Never, memoized: false, - dependencies: IndexSet::new(), - scopes: IndexSet::new(), + dependencies: IndexSet::default(), + scopes: IndexSet::default(), seen: false, }); node.level = join_aliases(node.level, lv.level); @@ -1049,17 +1048,17 @@ impl<'a> ReactiveFunctionVisitor for CollectDependenciesVisitor<'a> { // computeMemoizedIdentifiers // ============================================================================= -fn compute_memoized_identifiers(state: &CollectState) -> HashSet { - let mut memoized = HashSet::new(); +fn compute_memoized_identifiers(state: &CollectState) -> FxHashSet { + let mut memoized = FxHashSet::default(); // We need mutable access to the nodes, so we clone the state into mutable structures - let mut identifier_nodes: HashMap< + let mut identifier_nodes: FxHashMap< DeclarationId, ( MemoizationLevel, bool, - IndexSet, - IndexSet, + IndexSet, + IndexSet, bool, ), > = state @@ -1079,7 +1078,7 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet }) .collect(); - let mut scope_nodes: HashMap, bool)> = state + let mut scope_nodes: FxHashMap, bool)> = state .scopes .iter() .map(|(id, node)| (*id, (node.dependencies.clone(), node.seen))) @@ -1088,18 +1087,18 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet fn visit( id: DeclarationId, force_memoize: bool, - identifier_nodes: &mut HashMap< + identifier_nodes: &mut FxHashMap< DeclarationId, ( MemoizationLevel, bool, - IndexSet, - IndexSet, + IndexSet, + IndexSet, bool, ), >, - scope_nodes: &mut HashMap, bool)>, - memoized: &mut HashSet, + scope_nodes: &mut FxHashMap, bool)>, + memoized: &mut FxHashSet, ) -> bool { let Some(&(level, _, _, _, seen)) = identifier_nodes.get(&id) else { return false; @@ -1149,18 +1148,18 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet fn force_memoize_scope_dependencies( id: ScopeId, - identifier_nodes: &mut HashMap< + identifier_nodes: &mut FxHashMap< DeclarationId, ( MemoizationLevel, bool, - IndexSet, - IndexSet, + IndexSet, + IndexSet, bool, ), >, - scope_nodes: &mut HashMap, bool)>, - memoized: &mut HashSet, + scope_nodes: &mut FxHashMap, bool)>, + memoized: &mut FxHashSet, ) { let seen = scope_nodes .get(&id) @@ -1198,12 +1197,12 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet struct PruneScopesTransform<'a> { env: &'a Environment, - pruned_scopes: HashSet, - reassignments: HashMap>, + pruned_scopes: FxHashSet, + reassignments: FxHashMap>, } impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> { - type State = HashSet; + type State = FxHashSet; fn env(&self) -> &Environment { self.env @@ -1212,7 +1211,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> { fn transform_scope( &mut self, scope: &mut ReactiveScopeBlock, - state: &mut HashSet, + state: &mut FxHashSet, ) -> Result, react_compiler_diagnostics::CompilerError> { self.visit_scope(scope, state)?; @@ -1248,7 +1247,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> { fn transform_instruction( &mut self, instruction: &mut ReactiveInstruction, - state: &mut HashSet, + state: &mut FxHashSet, ) -> Result, react_compiler_diagnostics::CompilerError> { self.traverse_instruction(instruction, state)?; @@ -1263,7 +1262,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> { let ids = self .reassignments .entry(decl_id) - .or_insert_with(HashSet::new); + .or_insert_with(FxHashSet::default); ids.insert(store_value.identifier); } ReactiveValue::Instruction(InstructionValue::LoadLocal { place, .. }) => { @@ -1285,7 +1284,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> { let ids = self .reassignments .entry(decl_id) - .or_insert_with(HashSet::new); + .or_insert_with(FxHashSet::default); ids.insert(place.identifier); } } diff --git a/compiler/crates/react_compiler_reactive_scopes/src/prune_non_reactive_dependencies.rs b/compiler/crates/react_compiler_reactive_scopes/src/prune_non_reactive_dependencies.rs index 943203a4b48..fd04d4fa021 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/prune_non_reactive_dependencies.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/prune_non_reactive_dependencies.rs @@ -8,7 +8,7 @@ //! Corresponds to `src/ReactiveScopes/PruneNonReactiveDependencies.ts` //! and `src/ReactiveScopes/CollectReactiveIdentifiers.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ EvaluationOrder, IdentifierId, InstructionValue, Place, PrunedReactiveScopeBlock, @@ -28,9 +28,9 @@ use crate::visitors::{self, ReactiveFunctionTransform, ReactiveFunctionVisitor}; pub fn collect_reactive_identifiers( func: &ReactiveFunction, env: &Environment, -) -> HashSet { +) -> FxHashSet { let visitor = CollectVisitor { env }; - let mut state = HashSet::new(); + let mut state = FxHashSet::default(); crate::visitors::visit_reactive_function(func, &visitor, &mut state); state } @@ -40,7 +40,7 @@ struct CollectVisitor<'a> { } impl<'a> ReactiveFunctionVisitor for CollectVisitor<'a> { - type State = HashSet; + type State = FxHashSet; fn env(&self) -> &Environment { self.env @@ -74,7 +74,7 @@ impl<'a> ReactiveFunctionVisitor for CollectVisitor<'a> { /// TS: `isStableRefType` fn is_stable_ref_type( ty: &react_compiler_hir::Type, - reactive_identifiers: &HashSet, + reactive_identifiers: &FxHashSet, id: IdentifierId, ) -> bool { is_use_ref_type(ty) && !reactive_identifiers.contains(&id) @@ -133,7 +133,7 @@ struct PruneVisitor<'a> { } impl<'a> ReactiveFunctionTransform for PruneVisitor<'a> { - type State = HashSet; + type State = FxHashSet; fn env(&self) -> &Environment { self.env diff --git a/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_labels.rs b/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_labels.rs index 75a0efac98d..ea84e450eab 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_labels.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_labels.rs @@ -8,7 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/PruneUnusedLabels.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ BlockId, ReactiveFunction, ReactiveStatement, ReactiveTerminal, ReactiveTerminalStatement, @@ -23,7 +23,7 @@ pub fn prune_unused_labels( env: &Environment, ) -> Result<(), react_compiler_diagnostics::CompilerError> { let mut transform = Transform { env }; - let mut labels: HashSet = HashSet::new(); + let mut labels: FxHashSet = FxHashSet::default(); transform_reactive_function(func, &mut transform, &mut labels) } @@ -32,7 +32,7 @@ struct Transform<'a> { } impl<'a> ReactiveFunctionTransform for Transform<'a> { - type State = HashSet; + type State = FxHashSet; fn env(&self) -> &Environment { self.env @@ -41,7 +41,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> { fn transform_terminal( &mut self, stmt: &mut ReactiveTerminalStatement, - state: &mut HashSet, + state: &mut FxHashSet, ) -> Result, react_compiler_diagnostics::CompilerError> { // Traverse children first self.traverse_terminal(stmt, state)?; diff --git a/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_lvalues.rs b/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_lvalues.rs index f2997a373e1..16465eeb56e 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_lvalues.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/prune_unused_lvalues.rs @@ -9,7 +9,7 @@ //! //! Corresponds to `src/ReactiveScopes/PruneTemporaryLValues.ts`. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_hir::{ DeclarationId, EvaluationOrder, Place, ReactiveFunction, ReactiveInstruction, @@ -34,7 +34,7 @@ pub fn prune_unused_lvalues(func: &mut ReactiveFunction, env: &Environment) { // When we see an unnamed lvalue on an instruction, we add its DeclarationId. // When we see a place reference (operand), we remove its DeclarationId. let visitor = Visitor { env }; - let mut lvalues: HashSet = HashSet::new(); + let mut lvalues: FxHashSet = FxHashSet::default(); visitors::visit_reactive_function(func, &visitor, &mut lvalues); // Phase 2: Null out lvalues whose DeclarationId remains in the map. @@ -48,7 +48,7 @@ pub fn prune_unused_lvalues(func: &mut ReactiveFunction, env: &Environment) { /// TS: `type LValues = Map` /// In Rust, we only need the set of DeclarationIds (not the instruction refs) /// because we apply changes in a separate pass. -type LValues = HashSet; +type LValues = FxHashSet; /// TS: `class Visitor extends ReactiveFunctionVisitor` struct Visitor<'a> { @@ -87,7 +87,7 @@ impl ReactiveFunctionVisitor for Visitor<'_> { fn null_unused_lvalues( block: &mut Vec, env: &Environment, - unused: &HashSet, + unused: &FxHashSet, ) { for stmt in block.iter_mut() { match stmt { @@ -110,7 +110,7 @@ fn null_unused_lvalues( fn null_unused_in_instruction( instr: &mut ReactiveInstruction, env: &Environment, - unused: &HashSet, + unused: &FxHashSet, ) { if let Some(lv) = &instr.lvalue { let ident = &env.identifiers[lv.identifier.0 as usize]; @@ -124,7 +124,7 @@ fn null_unused_in_instruction( fn null_unused_in_value( value: &mut ReactiveValue, env: &Environment, - unused: &HashSet, + unused: &FxHashSet, ) { match value { ReactiveValue::SequenceExpression { @@ -161,7 +161,7 @@ fn null_unused_in_value( fn null_unused_in_terminal( terminal: &mut react_compiler_hir::ReactiveTerminal, env: &Environment, - unused: &HashSet, + unused: &FxHashSet, ) { use react_compiler_hir::ReactiveTerminal; match terminal { diff --git a/compiler/crates/react_compiler_reactive_scopes/src/rename_variables.rs b/compiler/crates/react_compiler_reactive_scopes/src/rename_variables.rs index 682642059de..f27ed20162e 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/rename_variables.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/rename_variables.rs @@ -8,8 +8,7 @@ //! //! Corresponds to `src/ReactiveScopes/RenameVariables.ts`. -use std::collections::HashMap; -use std::collections::HashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::DeclarationId; use react_compiler_hir::EvaluationOrder; @@ -33,19 +32,19 @@ use crate::visitors::{self}; // ============================================================================= struct Scopes { - seen: HashMap, - stack: Vec>, - globals: HashSet, - names: HashSet, + seen: FxHashMap, + stack: Vec>, + globals: FxHashSet, + names: FxHashSet, } impl Scopes { - fn new(globals: HashSet) -> Self { + fn new(globals: FxHashSet) -> Self { Self { - seen: HashMap::new(), - stack: vec![HashMap::new()], + seen: FxHashMap::default(), + stack: vec![FxHashMap::default()], globals, - names: HashSet::new(), + names: FxHashSet::default(), } } @@ -114,7 +113,7 @@ impl Scopes { } fn enter(&mut self) { - self.stack.push(HashMap::new()); + self.stack.push(FxHashMap::default()); } fn leave(&mut self) { @@ -202,15 +201,15 @@ impl ReactiveFunctionVisitor for Visitor<'_> { /// Renames variables for output — assigns unique names, handles SSA renames. /// Returns a Set of all unique variable names used. /// TS: `renameVariables` -pub fn rename_variables(func: &mut ReactiveFunction, env: &mut Environment) -> HashSet { +pub fn rename_variables(func: &mut ReactiveFunction, env: &mut Environment) -> FxHashSet { rename_variables_with_parent(func, env, None) } fn rename_variables_with_parent( func: &mut ReactiveFunction, env: &mut Environment, - parent_names: Option<&HashSet>, -) -> HashSet { + parent_names: Option<&FxHashSet>, +) -> FxHashSet { let globals = collect_referenced_globals(&func.body, env); // Phase 1: Use ReactiveFunctionVisitor to compute the rename mapping. @@ -242,7 +241,7 @@ fn rename_variables_with_parent( } } - let mut result: HashSet = scopes.names; + let mut result: FxHashSet = scopes.names; result.extend(globals); result } @@ -267,13 +266,17 @@ fn rename_variables_impl(func: &ReactiveFunction, visitor: &Visitor, scopes: &mu /// Collects all globally referenced names from the reactive function. /// TS: `collectReferencedGlobals` -fn collect_referenced_globals(block: &ReactiveBlock, env: &Environment) -> HashSet { - let mut globals = HashSet::new(); +fn collect_referenced_globals(block: &ReactiveBlock, env: &Environment) -> FxHashSet { + let mut globals = FxHashSet::default(); collect_globals_block(block, &mut globals, env); globals } -fn collect_globals_block(block: &ReactiveBlock, globals: &mut HashSet, env: &Environment) { +fn collect_globals_block( + block: &ReactiveBlock, + globals: &mut FxHashSet, + env: &Environment, +) { for stmt in block { match stmt { react_compiler_hir::ReactiveStatement::Instruction(instr) => { @@ -292,7 +295,11 @@ fn collect_globals_block(block: &ReactiveBlock, globals: &mut HashSet, e } } -fn collect_globals_value(value: &ReactiveValue, globals: &mut HashSet, env: &Environment) { +fn collect_globals_value( + value: &ReactiveValue, + globals: &mut FxHashSet, + env: &Environment, +) { match value { ReactiveValue::Instruction(iv) => { if let InstructionValue::LoadGlobal { binding, .. } = iv { @@ -340,7 +347,7 @@ fn collect_globals_value(value: &ReactiveValue, globals: &mut HashSet, e /// Recursively collects LoadGlobal names from an inner HIR function. fn collect_globals_hir_function( func_id: FunctionId, - globals: &mut HashSet, + globals: &mut FxHashSet, env: &Environment, ) { let inner_func = &env.functions[func_id.0 as usize]; @@ -367,7 +374,7 @@ fn collect_globals_hir_function( fn collect_globals_terminal( stmt: &react_compiler_hir::ReactiveTerminalStatement, - globals: &mut HashSet, + globals: &mut FxHashSet, env: &Environment, ) { match &stmt.terminal { diff --git a/compiler/crates/react_compiler_reactive_scopes/src/stabilize_block_ids.rs b/compiler/crates/react_compiler_reactive_scopes/src/stabilize_block_ids.rs index 9d91dfffac7..14a9cc69e15 100644 --- a/compiler/crates/react_compiler_reactive_scopes/src/stabilize_block_ids.rs +++ b/compiler/crates/react_compiler_reactive_scopes/src/stabilize_block_ids.rs @@ -10,7 +10,7 @@ //! //! Corresponds to `src/ReactiveScopes/StabilizeBlockIds.ts`. -use std::collections::HashMap; +use rustc_hash::{FxBuildHasher, FxHashMap}; use indexmap::IndexSet; use react_compiler_hir::{ @@ -27,12 +27,12 @@ use crate::visitors::{ /// TS: `stabilizeBlockIds` pub fn stabilize_block_ids(func: &mut ReactiveFunction, env: &mut Environment) { // Pass 1: Collect referenced labels (preserving insertion order to match TS Set behavior) - let mut referenced: IndexSet = IndexSet::new(); + let mut referenced: IndexSet = IndexSet::default(); let collector = CollectReferencedLabels { env: &*env }; visit_reactive_function(func, &collector, &mut referenced); // Build mappings: referenced block IDs -> sequential IDs (insertion-order deterministic) - let mut mappings: HashMap = HashMap::new(); + let mut mappings: FxHashMap = FxHashMap::default(); for block_id in &referenced { let len = mappings.len() as u32; mappings.entry(*block_id).or_insert(BlockId(len)); @@ -52,7 +52,7 @@ struct CollectReferencedLabels<'a> { } impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> { - type State = IndexSet; + type State = IndexSet; fn env(&self) -> &Environment { self.env @@ -80,7 +80,7 @@ impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> { // Pass 2: RewriteBlockIds // ============================================================================= -fn get_or_insert_mapping(mappings: &mut HashMap, id: BlockId) -> BlockId { +fn get_or_insert_mapping(mappings: &mut FxHashMap, id: BlockId) -> BlockId { let len = mappings.len() as u32; *mappings.entry(id).or_insert(BlockId(len)) } @@ -91,7 +91,7 @@ struct RewriteBlockIds<'a> { } impl<'a> ReactiveFunctionTransform for RewriteBlockIds<'a> { - type State = HashMap; + type State = FxHashMap; fn env(&self) -> &Environment { self.env diff --git a/compiler/crates/react_compiler_ssa/Cargo.toml b/compiler/crates/react_compiler_ssa/Cargo.toml index f0b0f08be0e..9effe781181 100644 --- a/compiler/crates/react_compiler_ssa/Cargo.toml +++ b/compiler/crates/react_compiler_ssa/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" react_compiler_diagnostics = { path = "../react_compiler_diagnostics" } react_compiler_hir = { path = "../react_compiler_hir" } indexmap = "2" +rustc-hash = "2" diff --git a/compiler/crates/react_compiler_ssa/src/eliminate_redundant_phi.rs b/compiler/crates/react_compiler_ssa/src/eliminate_redundant_phi.rs index 231ba4cecd0..37dae183eb4 100644 --- a/compiler/crates/react_compiler_ssa/src/eliminate_redundant_phi.rs +++ b/compiler/crates/react_compiler_ssa/src/eliminate_redundant_phi.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_hir::environment::Environment; use react_compiler_hir::visitors; @@ -10,7 +10,7 @@ use crate::enter_ssa::placeholder_function; // Helper: rewrite_place // ============================================================================= -fn rewrite_place(place: &mut Place, rewrites: &HashMap) { +fn rewrite_place(place: &mut Place, rewrites: &FxHashMap) { if let Some(&rewrite) = rewrites.get(&place.identifier) { place.identifier = rewrite; } @@ -21,7 +21,7 @@ fn rewrite_place(place: &mut Place, rewrites: &HashMap = HashMap::new(); + let mut rewrites: FxHashMap = FxHashMap::default(); eliminate_redundant_phi_impl(func, env, &mut rewrites); } @@ -32,12 +32,12 @@ pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) { fn eliminate_redundant_phi_impl( func: &mut HirFunction, env: &mut Environment, - rewrites: &mut HashMap, + rewrites: &mut FxHashMap, ) { let ir = &mut func.body; let mut has_back_edge = false; - let mut visited: HashSet = HashSet::new(); + let mut visited: FxHashSet = FxHashSet::default(); let mut size; loop { diff --git a/compiler/crates/react_compiler_ssa/src/enter_ssa.rs b/compiler/crates/react_compiler_ssa/src/enter_ssa.rs index 70346fce448..9d508f3007d 100644 --- a/compiler/crates/react_compiler_ssa/src/enter_ssa.rs +++ b/compiler/crates/react_compiler_ssa/src/enter_ssa.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use indexmap::IndexMap; use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory}; @@ -16,35 +16,35 @@ struct IncompletePhi { } struct State { - defs: HashMap, + defs: FxHashMap, incomplete_phis: Vec, } struct SSABuilder { - states: HashMap, + states: FxHashMap, current: Option, - unsealed_preds: HashMap, - block_preds: HashMap>, - unknown: HashSet, - context: HashSet, - pending_phis: HashMap>, + unsealed_preds: FxHashMap, + block_preds: FxHashMap>, + unknown: FxHashSet, + context: FxHashSet, + pending_phis: FxHashMap>, processed_functions: Vec, } impl SSABuilder { - fn new(blocks: &IndexMap) -> Self { - let mut block_preds = HashMap::new(); + fn new(blocks: &IndexMap) -> Self { + let mut block_preds = FxHashMap::default(); for (id, block) in blocks { block_preds.insert(*id, block.preds.iter().copied().collect()); } SSABuilder { - states: HashMap::new(), + states: FxHashMap::default(), current: None, - unsealed_preds: HashMap::new(), + unsealed_preds: FxHashMap::default(), block_preds, - unknown: HashSet::new(), - context: HashSet::new(), - pending_phis: HashMap::new(), + unknown: FxHashSet::default(), + context: FxHashSet::default(), + pending_phis: FxHashMap::default(), processed_functions: Vec::new(), } } @@ -226,7 +226,7 @@ impl SSABuilder { ) { let preds = self.block_preds.get(&block_id).cloned().unwrap_or_default(); - let mut pred_defs: IndexMap = IndexMap::new(); + let mut pred_defs: IndexMap = IndexMap::default(); for pred_block_id in &preds { let pred_id = self.get_id_at(old_place, *pred_block_id, env); pred_defs.insert( @@ -266,7 +266,7 @@ impl SSABuilder { self.states.insert( block_id, State { - defs: HashMap::new(), + defs: FxHashMap::default(), incomplete_phis: Vec::new(), }, ); @@ -310,7 +310,7 @@ fn enter_ssa_impl( env: &mut Environment, root_entry: BlockId, ) -> Result<(), CompilerDiagnostic> { - let mut visited_blocks: HashSet = HashSet::new(); + let mut visited_blocks: FxHashSet = FxHashSet::default(); let block_ids: Vec = func.body.blocks.keys().copied().collect(); for block_id in &block_ids { @@ -520,7 +520,7 @@ pub fn placeholder_function() -> HirFunction { context: Vec::new(), body: HIR { entry: BlockId(0), - blocks: IndexMap::new(), + blocks: IndexMap::default(), }, instructions: Vec::new(), generator: false, diff --git a/compiler/crates/react_compiler_ssa/src/rewrite_instruction_kinds_based_on_reassignment.rs b/compiler/crates/react_compiler_ssa/src/rewrite_instruction_kinds_based_on_reassignment.rs index f4dc5e9694e..cf4aada157d 100644 --- a/compiler/crates/react_compiler_ssa/src/rewrite_instruction_kinds_based_on_reassignment.rs +++ b/compiler/crates/react_compiler_ssa/src/rewrite_instruction_kinds_based_on_reassignment.rs @@ -15,7 +15,7 @@ //! may be converted to a `const` if the reassignment is not used and was removed //! by dead code elimination. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation, @@ -115,7 +115,7 @@ pub fn rewrite_instruction_kinds_based_on_reassignment( // // Track: for each DeclarationId, the location of its first declaration, // and whether it needs to be changed to Let (because of reassignment). - let mut declarations: HashMap = HashMap::new(); + let mut declarations: FxHashMap = FxHashMap::default(); // Track which (block_index, instr_local_index) should have their lvalue.kind set to Reassign let mut reassign_locs: Vec<(usize, usize)> = Vec::new(); // Track which declaration locations need to be set to Let diff --git a/compiler/crates/react_compiler_typeinference/Cargo.toml b/compiler/crates/react_compiler_typeinference/Cargo.toml index 79fdfe37d8e..93e905fb722 100644 --- a/compiler/crates/react_compiler_typeinference/Cargo.toml +++ b/compiler/crates/react_compiler_typeinference/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +rustc-hash = "2" react_compiler_diagnostics = { path = "../react_compiler_diagnostics" } react_compiler_hir = { path = "../react_compiler_hir" } react_compiler_ssa = { path = "../react_compiler_ssa" } diff --git a/compiler/crates/react_compiler_typeinference/src/infer_types.rs b/compiler/crates/react_compiler_typeinference/src/infer_types.rs index 9cde716d5c1..c5739c7cdf7 100644 --- a/compiler/crates/react_compiler_typeinference/src/infer_types.rs +++ b/compiler/crates/react_compiler_typeinference/src/infer_types.rs @@ -8,7 +8,7 @@ //! Generates type equations from the HIR, unifies them, and applies the //! resolved types back to identifiers. Analogous to TS `InferTypes.ts`. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory}; use react_compiler_hir::environment::{Environment, is_hook_name}; @@ -79,7 +79,7 @@ fn pre_resolve_globals( func: &HirFunction, function_key: u32, env: &mut Environment, - global_types: &mut HashMap<(u32, InstructionId), Type>, + global_types: &mut FxHashMap<(u32, InstructionId), Type>, ) { for &instr_id in func.body.blocks.values().flat_map(|b| &b.instructions) { let instr = &func.instructions[instr_id.0 as usize]; @@ -95,7 +95,7 @@ fn pre_resolve_globals( fn pre_resolve_globals_recursive( func_id: FunctionId, env: &mut Environment, - global_types: &mut HashMap<(u32, InstructionId), Type>, + global_types: &mut FxHashMap<(u32, InstructionId), Type>, ) { // Collect LoadGlobal bindings and child function IDs in one pass to avoid // borrow conflicts (we need &env.functions to read, then &mut env for @@ -277,13 +277,13 @@ fn type_equals(a: &Type, b: &Type) -> bool { } } -fn set_name(names: &mut HashMap, id: IdentifierId, source: &Identifier) { +fn set_name(names: &mut FxHashMap, id: IdentifierId, source: &Identifier) { if let Some(IdentifierName::Named(ref name)) = source.name { names.insert(id, name.clone()); } } -fn get_name(names: &HashMap, id: IdentifierId) -> String { +fn get_name(names: &FxHashMap, id: IdentifierId) -> String { names.get(&id).cloned().unwrap_or_default() } @@ -335,7 +335,7 @@ fn generate( // &mut env, but generate_instruction_types takes split borrows on env fields. // The key is (function_key, InstructionId) where function_key is u32::MAX // for the outer function and FunctionId.0 for inner functions. - let mut global_types: HashMap<(u32, InstructionId), Type> = HashMap::new(); + let mut global_types: FxHashMap<(u32, InstructionId), Type> = FxHashMap::default(); pre_resolve_globals(func, u32::MAX, env, &mut global_types); // Also pre-resolve inner functions recursively for &instr_id in func.body.blocks.values().flat_map(|b| &b.instructions) { @@ -355,7 +355,7 @@ fn generate( } } - let mut names: HashMap = HashMap::new(); + let mut names: FxHashMap = FxHashMap::default(); let mut return_types: Vec = Vec::new(); for (_block_id, block) in &func.body.blocks { @@ -420,7 +420,7 @@ fn generate_for_function_id( identifiers: &[Identifier], types: &mut Vec, functions: &mut Vec, - global_types: &HashMap<(u32, InstructionId), Type>, + global_types: &FxHashMap<(u32, InstructionId), Type>, shapes: &ShapeRegistry, unifier: &mut Unifier, ) -> Result<(), CompilerDiagnostic> { @@ -457,7 +457,7 @@ fn generate_for_function_id( // TS creates a fresh `names` Map per recursive `generate` call, so inner // functions don't inherit or pollute the outer function's name mappings. - let mut inner_names: HashMap = HashMap::new(); + let mut inner_names: FxHashMap = FxHashMap::default(); let mut inner_return_types: Vec = Vec::new(); for (_block_id, block) in &inner.body.blocks { @@ -521,8 +521,8 @@ fn generate_instruction_types( identifiers: &[Identifier], types: &mut Vec, functions: &mut Vec, - names: &mut HashMap, - global_types: &HashMap<(u32, InstructionId), Type>, + names: &mut FxHashMap, + global_types: &FxHashMap<(u32, InstructionId), Type>, shapes: &ShapeRegistry, unifier: &mut Unifier, ) -> Result<(), CompilerDiagnostic> { @@ -1304,7 +1304,7 @@ fn apply_instruction_operands( // ============================================================================= struct Unifier { - substitutions: HashMap, + substitutions: FxHashMap, enable_treat_ref_like_identifiers_as_refs: bool, enable_treat_set_identifiers_as_state_setters: bool, custom_hook_type: Option, @@ -1317,7 +1317,7 @@ impl Unifier { enable_treat_set_identifiers_as_state_setters: bool, ) -> Self { Unifier { - substitutions: HashMap::new(), + substitutions: FxHashMap::default(), enable_treat_ref_like_identifiers_as_refs, enable_treat_set_identifiers_as_state_setters, custom_hook_type, diff --git a/compiler/crates/react_compiler_utils/Cargo.toml b/compiler/crates/react_compiler_utils/Cargo.toml index 06b93a5b9d3..ee1eba46110 100644 --- a/compiler/crates/react_compiler_utils/Cargo.toml +++ b/compiler/crates/react_compiler_utils/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] indexmap = "2" +rustc-hash = "2" diff --git a/compiler/crates/react_compiler_utils/src/disjoint_set.rs b/compiler/crates/react_compiler_utils/src/disjoint_set.rs index fc8758a35a0..a3274c4a5ee 100644 --- a/compiler/crates/react_compiler_utils/src/disjoint_set.rs +++ b/compiler/crates/react_compiler_utils/src/disjoint_set.rs @@ -7,7 +7,7 @@ //! //! Ported from TypeScript `src/Utils/DisjointSet.ts`. -use std::collections::HashSet; +use rustc_hash::{FxBuildHasher, FxHashSet}; use std::hash::Hash; use indexmap::IndexMap; @@ -17,13 +17,13 @@ use indexmap::IndexMap; /// Corresponds to TS `DisjointSet` in `src/Utils/DisjointSet.ts`. /// Uses `IndexMap` to preserve insertion order (matching TS `Map` behavior). pub struct DisjointSet { - entries: IndexMap, + entries: IndexMap, } impl DisjointSet { pub fn new() -> Self { DisjointSet { - entries: IndexMap::new(), + entries: IndexMap::default(), } } @@ -87,8 +87,8 @@ impl DisjointSet { /// root) and returns a map of items to their roots. /// /// Corresponds to TS `canonicalize(): Map`. - pub fn canonicalize(&mut self) -> IndexMap { - let mut result = IndexMap::new(); + pub fn canonicalize(&mut self) -> IndexMap { + let mut result = IndexMap::default(); let keys: Vec = self.entries.keys().copied().collect(); for item in keys { let root = self.find(item); @@ -115,9 +115,9 @@ impl DisjointSet { /// Groups all items by their root and returns the groups as a list of sets. /// /// Corresponds to TS `buildSets(): Array>`. - pub fn build_sets(&mut self) -> Vec> { - let mut group_to_index: IndexMap = IndexMap::new(); - let mut sets: Vec> = Vec::new(); + pub fn build_sets(&mut self) -> Vec> { + let mut group_to_index: IndexMap = IndexMap::default(); + let mut sets: Vec> = Vec::new(); let keys: Vec = self.entries.keys().copied().collect(); for item in keys { let group = self.find(item); @@ -126,7 +126,7 @@ impl DisjointSet { None => { let idx = sets.len(); group_to_index.insert(group, idx); - sets.push(HashSet::new()); + sets.push(FxHashSet::default()); idx } }; diff --git a/compiler/crates/react_compiler_validation/Cargo.toml b/compiler/crates/react_compiler_validation/Cargo.toml index f30d13246cf..5274f4cf703 100644 --- a/compiler/crates/react_compiler_validation/Cargo.toml +++ b/compiler/crates/react_compiler_validation/Cargo.toml @@ -5,5 +5,6 @@ edition = "2024" [dependencies] indexmap = "2" +rustc-hash = "2" react_compiler_diagnostics = { path = "../react_compiler_diagnostics" } react_compiler_hir = { path = "../react_compiler_hir" } diff --git a/compiler/crates/react_compiler_validation/src/validate_context_variable_lvalues.rs b/compiler/crates/react_compiler_validation/src/validate_context_variable_lvalues.rs index 82a3e280a29..3f5a8fbfd0b 100644 --- a/compiler/crates/react_compiler_validation/src/validate_context_variable_lvalues.rs +++ b/compiler/crates/react_compiler_validation/src/validate_context_variable_lvalues.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, @@ -27,7 +27,7 @@ impl std::fmt::Display for VarRefKind { } } -type IdentifierKinds = HashMap; +type IdentifierKinds = FxHashMap; /// Validates that context variable lvalues are used consistently. /// @@ -53,7 +53,7 @@ pub fn validate_context_variable_lvalues_with_errors( identifiers: &[Identifier], errors: &mut CompilerError, ) -> Result<(), CompilerDiagnostic> { - let mut identifier_kinds: IdentifierKinds = HashMap::new(); + let mut identifier_kinds: IdentifierKinds = FxHashMap::default(); validate_context_variable_lvalues_impl( func, &mut identifier_kinds, diff --git a/compiler/crates/react_compiler_validation/src/validate_exhaustive_dependencies.rs b/compiler/crates/react_compiler_validation/src/validate_exhaustive_dependencies.rs index a0977e978ba..e7444924e58 100644 --- a/compiler/crates/react_compiler_validation/src/validate_exhaustive_dependencies.rs +++ b/compiler/crates/react_compiler_validation/src/validate_exhaustive_dependencies.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerSuggestion, CompilerSuggestionOperation, @@ -33,7 +33,7 @@ pub fn validate_exhaustive_dependencies( let validate_memo = env.config.validate_exhaustive_memoization_dependencies; let validate_effect = env.config.validate_exhaustive_effect_dependencies.clone(); - let mut temporaries: HashMap = HashMap::new(); + let mut temporaries: FxHashMap = FxHashMap::default(); for param in &func.params { let place = match param { ParamPattern::Place(p) => p, @@ -51,7 +51,7 @@ pub fn validate_exhaustive_dependencies( } let mut start_memo: Option = None; - let mut memo_locals: HashSet = HashSet::new(); + let mut memo_locals: FxHashSet = FxHashSet::default(); // Callbacks struct holding the mutable state let mut callbacks = Callbacks { @@ -61,7 +61,7 @@ pub fn validate_exhaustive_dependencies( validate_effect: validate_effect.clone(), reactive: &reactive, diagnostics: Vec::new(), - invalid_memo_ids: HashSet::new(), + invalid_memo_ids: FxHashSet::default(), }; collect_dependencies( @@ -180,13 +180,13 @@ fn path_to_string(path: &[DependencyPathEntry]) -> String { struct Callbacks<'a> { start_memo: &'a mut Option, #[allow(dead_code)] - memo_locals: &'a mut HashSet, + memo_locals: &'a mut FxHashSet, validate_memo: bool, validate_effect: ExhaustiveEffectDepsMode, - reactive: &'a HashSet, + reactive: &'a FxHashSet, diagnostics: Vec, /// manual_memo_ids that had validation errors (to set has_invalid_deps) - invalid_memo_ids: HashSet, + invalid_memo_ids: FxHashSet, } // ============================================================================= @@ -283,8 +283,8 @@ fn is_sub_path_ignoring_optionals( fn collect_reactive_identifiers( func: &HirFunction, functions: &[HirFunction], -) -> HashSet { - let mut reactive = HashSet::new(); +) -> FxHashSet { + let mut reactive = FxHashSet::default(); for (_block_id, block) in &func.body.blocks { for &instr_id in &block.instructions { let instr = &func.instructions[instr_id.0 as usize]; @@ -319,9 +319,9 @@ fn collect_reactive_identifiers( // findOptionalPlaces // ============================================================================= -fn find_optional_places(func: &HirFunction) -> HashMap { - let mut optionals: HashMap = HashMap::new(); - let mut visited: HashSet = HashSet::new(); +fn find_optional_places(func: &HirFunction) -> FxHashMap { + let mut optionals: FxHashMap = FxHashMap::default(); + let mut visited: FxHashSet = FxHashSet::default(); for (_block_id, block) in &func.body.blocks { if visited.contains(&block.id) { @@ -418,8 +418,8 @@ fn find_optional_places(func: &HirFunction) -> HashMap { fn add_dependency( dep: &Temporary, dependencies: &mut Vec, - dep_keys: &mut HashSet, - locals: &HashSet, + dep_keys: &mut FxHashSet, + locals: &FxHashSet, ) { match dep { Temporary::Aggregate { @@ -464,8 +464,8 @@ fn add_dependency( fn add_dependency_inferred( dep: &InferredDependency, dependencies: &mut Vec, - dep_keys: &mut HashSet, - locals: &HashSet, + dep_keys: &mut FxHashSet, + locals: &FxHashSet, ) { match dep { InferredDependency::Global { .. } => { @@ -487,10 +487,10 @@ fn add_dependency_inferred( fn visit_candidate_dependency( place: &Place, - temporaries: &HashMap, + temporaries: &FxHashMap, dependencies: &mut Vec, - dep_keys: &mut HashSet, - locals: &HashSet, + dep_keys: &mut FxHashSet, + locals: &FxHashSet, ) { if let Some(dep) = temporaries.get(&place.identifier) { add_dependency(dep, dependencies, dep_keys, locals); @@ -502,12 +502,12 @@ fn collect_dependencies( identifiers: &[Identifier], types: &[Type], functions: &[HirFunction], - temporaries: &mut HashMap, + temporaries: &mut FxHashMap, callbacks: &mut Option<&mut Callbacks<'_>>, is_function_expression: bool, ) -> Result { let optionals = find_optional_places(func); - let mut locals: HashSet = HashSet::new(); + let mut locals: FxHashSet = FxHashSet::default(); if is_function_expression { for param in &func.params { @@ -520,15 +520,15 @@ fn collect_dependencies( } let mut dependencies: Vec = Vec::new(); - let mut dep_keys: HashSet = HashSet::new(); + let mut dep_keys: FxHashSet = FxHashSet::default(); // Saved state for when we're inside a memo block (StartMemoize..FinishMemoize). // In TS, `dependencies` and `locals` are shared by reference between the main // collection loop and the callbacks — StartMemoize clears them, FinishMemoize // reads and clears them. We simulate this by saving/restoring. let mut saved_dependencies: Option> = None; - let mut saved_dep_keys: Option> = None; - let mut saved_locals: Option> = None; + let mut saved_dep_keys: Option> = None; + let mut saved_locals: Option> = None; for (_block_id, block) in &func.body.blocks { // Process phis @@ -906,8 +906,8 @@ fn collect_dependencies( } InstructionValue::ArrayExpression { elements, loc, .. } => { let mut array_deps: Vec = Vec::new(); - let mut array_keys: HashSet = HashSet::new(); - let empty_locals = HashSet::new(); + let mut array_keys: FxHashSet = FxHashSet::default(); + let empty_locals = FxHashSet::default(); for elem in elements { let place = match elem { ArrayElement::Place(p) => Some(p), @@ -1224,7 +1224,7 @@ fn collect_dependencies( fn validate_dependencies( mut inferred: Vec, manual_dependencies: &[ManualMemoDependency], - reactive: &HashSet, + reactive: &FxHashSet, manual_memo_loc: Option, category: ErrorCategory, exhaustive_deps_report_mode: &str, @@ -1360,7 +1360,7 @@ fn validate_dependencies( } // Validate manual deps - let mut matched: HashSet = HashSet::new(); // indices into manual_dependencies + let mut matched: FxHashSet = FxHashSet::default(); // indices into manual_dependencies let mut missing: Vec<&InferredDependency> = Vec::new(); let mut extra: Vec<&ManualMemoDependency> = Vec::new(); @@ -1646,7 +1646,7 @@ fn print_manual_memo_dependency(dep: &ManualMemoDependency, identifiers: &[Ident fn is_optional_dependency( identifier: IdentifierId, - reactive: &HashSet, + reactive: &FxHashSet, identifiers: &[Identifier], types: &[Type], ) -> bool { @@ -1659,7 +1659,7 @@ fn is_optional_dependency( fn is_optional_dependency_inferred( dep: &InferredDependency, - reactive: &HashSet, + reactive: &FxHashSet, identifiers: &[Identifier], types: &[Type], ) -> bool { diff --git a/compiler/crates/react_compiler_validation/src/validate_hooks_usage.rs b/compiler/crates/react_compiler_validation/src/validate_hooks_usage.rs index 7d24e59883c..8bb8fe7e55d 100644 --- a/compiler/crates/react_compiler_validation/src/validate_hooks_usage.rs +++ b/compiler/crates/react_compiler_validation/src/validate_hooks_usage.rs @@ -10,7 +10,7 @@ //! and not called dynamically. Also validates that hooks are not //! called inside function expressions. -use std::collections::HashMap; +use rustc_hash::{FxBuildHasher, FxHashMap}; use indexmap::IndexMap; use react_compiler_diagnostics::{ @@ -51,7 +51,7 @@ fn join_kinds(a: Kind, b: Kind) -> Kind { fn get_kind_for_place( place: &Place, - value_kinds: &HashMap, + value_kinds: &FxHashMap, identifiers: &[Identifier], ) -> Kind { let known_kind = value_kinds.get(&place.identifier).copied(); @@ -86,8 +86,8 @@ fn get_hook_kind_for_id<'a>( fn visit_place( place: &Place, - value_kinds: &HashMap, - errors_by_loc: &mut IndexMap, + value_kinds: &FxHashMap, + errors_by_loc: &mut IndexMap, env: &mut Environment, ) -> Result<(), CompilerError> { let kind = value_kinds.get(&place.identifier).copied(); @@ -99,8 +99,8 @@ fn visit_place( fn record_conditional_hook_error( place: &Place, - value_kinds: &mut HashMap, - errors_by_loc: &mut IndexMap, + value_kinds: &mut FxHashMap, + errors_by_loc: &mut IndexMap, env: &mut Environment, ) -> Result<(), CompilerError> { value_kinds.insert(place.identifier, Kind::Error); @@ -133,7 +133,7 @@ fn record_conditional_hook_error( fn record_invalid_hook_usage_error( place: &Place, - errors_by_loc: &mut IndexMap, + errors_by_loc: &mut IndexMap, env: &mut Environment, ) -> Result<(), CompilerError> { let reason = "Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values".to_string(); @@ -164,7 +164,7 @@ fn record_invalid_hook_usage_error( fn record_dynamic_hook_usage_error( place: &Place, - errors_by_loc: &mut IndexMap, + errors_by_loc: &mut IndexMap, env: &mut Environment, ) -> Result<(), CompilerError> { let reason = "Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks".to_string(); @@ -199,8 +199,9 @@ pub fn validate_hooks_usage( env: &mut Environment, ) -> Result<(), react_compiler_diagnostics::CompilerDiagnostic> { let unconditional_blocks = compute_unconditional_blocks(func, env.next_block_id().0)?; - let mut errors_by_loc: IndexMap = IndexMap::new(); - let mut value_kinds: HashMap = HashMap::new(); + let mut errors_by_loc: IndexMap = + IndexMap::default(); + let mut value_kinds: FxHashMap = FxHashMap::default(); // Process params for param in &func.params { @@ -512,8 +513,8 @@ fn hook_kind_display(kind: &HookKind) -> &'static str { /// Uses the canonical `each_instruction_value_operand` from visitors. fn visit_all_operands( value: &InstructionValue, - value_kinds: &HashMap, - errors_by_loc: &mut IndexMap, + value_kinds: &FxHashMap, + errors_by_loc: &mut IndexMap, env: &mut Environment, ) -> Result<(), CompilerError> { let operands = visitors::each_instruction_value_operand(value, &*env); diff --git a/compiler/crates/react_compiler_validation/src/validate_locals_not_reassigned_after_render.rs b/compiler/crates/react_compiler_validation/src/validate_locals_not_reassigned_after_render.rs index 38207f7d0d2..07031cf2572 100644 --- a/compiler/crates/react_compiler_validation/src/validate_locals_not_reassigned_after_render.rs +++ b/compiler/crates/react_compiler_validation/src/validate_locals_not_reassigned_after_render.rs @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory}; use react_compiler_hir::environment::Environment; @@ -20,7 +20,7 @@ use react_compiler_hir::{ /// This prevents a category of bugs in which a closure captures a /// binding from one render but does not update. pub fn validate_locals_not_reassigned_after_render(func: &HirFunction, env: &mut Environment) { - let mut context_variables: HashSet = HashSet::new(); + let mut context_variables: FxHashSet = FxHashSet::default(); let mut diagnostics: Vec = Vec::new(); let reassignment = get_context_reassignment( @@ -85,13 +85,13 @@ fn get_context_reassignment( types: &[Type], functions: &[HirFunction], env: &Environment, - context_variables: &mut HashSet, + context_variables: &mut FxHashSet, is_function_expression: bool, is_async: bool, diagnostics: &mut Vec, ) -> Option { // Maps identifiers to the place that they reassign - let mut reassigning_functions: HashMap = HashMap::new(); + let mut reassigning_functions: FxHashMap = FxHashMap::default(); for (_block_id, block) in &func.body.blocks { for &instruction_id in &block.instructions { diff --git a/compiler/crates/react_compiler_validation/src/validate_no_capitalized_calls.rs b/compiler/crates/react_compiler_validation/src/validate_no_capitalized_calls.rs index 8fd19fe58a0..6d2c510c05c 100644 --- a/compiler/crates/react_compiler_validation/src/validate_no_capitalized_calls.rs +++ b/compiler/crates/react_compiler_validation/src/validate_no_capitalized_calls.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{CompilerError, CompilerErrorDetail, ErrorCategory}; use react_compiler_hir::environment::Environment; @@ -12,15 +12,15 @@ pub fn validate_no_capitalized_calls( env: &mut Environment, ) -> Result<(), CompilerError> { // Build the allow list from global registry keys + config entries - let mut allow_list: HashSet = env.globals().keys().cloned().collect(); + let mut allow_list: FxHashSet = env.globals().keys().cloned().collect(); if let Some(config_entries) = &env.config.validate_no_capitalized_calls { for entry in config_entries { allow_list.insert(entry.clone()); } } - let mut capital_load_globals: HashMap = HashMap::new(); - let mut capitalized_properties: HashMap = HashMap::new(); + let mut capital_load_globals: FxHashMap = FxHashMap::default(); + let mut capitalized_properties: FxHashMap = FxHashMap::default(); let reason = "Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config"; diff --git a/compiler/crates/react_compiler_validation/src/validate_no_derived_computations_in_effects.rs b/compiler/crates/react_compiler_validation/src/validate_no_derived_computations_in_effects.rs index b86df5953a3..247fa1bf262 100644 --- a/compiler/crates/react_compiler_validation/src/validate_no_derived_computations_in_effects.rs +++ b/compiler/crates/react_compiler_validation/src/validate_no_derived_computations_in_effects.rs @@ -10,7 +10,8 @@ //! //! Port of ValidateNoDerivedComputationsInEffects_exp.ts. -use std::collections::{HashMap, HashSet}; +use indexmap::{IndexMap, IndexSet}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, CompilerErrorDetail, ErrorCategory, @@ -111,7 +112,7 @@ struct DerivationMetadata { type_of_value: TypeOfValue, place_identifier: IdentifierId, place_name: Option, - source_ids: indexmap::IndexSet, + source_ids: IndexSet, is_state_source: bool, } @@ -129,16 +130,16 @@ struct DepElement { struct ValidationContext { /// Map from lvalue identifier to the FunctionId of function expressions - functions: HashMap, + functions: FxHashMap, /// Map from lvalue identifier to ArrayExpression elements (candidate deps) - candidate_dependencies: HashMap>, + candidate_dependencies: FxHashMap>, derivation_cache: DerivationCache, - effects_cache: HashMap, - set_state_loads: HashMap>, - set_state_usages: HashMap>, + effects_cache: FxHashMap, + set_state_loads: FxHashMap>, + set_state_usages: FxHashMap>, } -/// A hashable key for SourceLocation to use in HashSet +/// A hashable key for SourceLocation to use in FxHashSet #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct LocKey { start_line: u32, @@ -169,21 +170,21 @@ impl LocKey { #[derive(Debug, Clone)] struct DerivationCache { has_changes: bool, - cache: HashMap, - previous_cache: Option>, + cache: FxHashMap, + previous_cache: Option>, } impl DerivationCache { fn new() -> Self { DerivationCache { has_changes: false, - cache: HashMap::new(), + cache: FxHashMap::default(), previous_cache: None, } } fn take_snapshot(&mut self) { - let mut prev = HashMap::new(); + let mut prev = FxHashMap::default(); for (key, value) in &self.cache { prev.insert( *key, @@ -241,7 +242,7 @@ impl DerivationCache { &mut self, derived_id: IdentifierId, derived_name: Option, - source_ids: indexmap::IndexSet, + source_ids: IndexSet, type_of_value: TypeOfValue, is_state_source: bool, ) { @@ -302,8 +303,8 @@ fn join_value(lvalue_type: TypeOfValue, value_type: TypeOfValue) -> TypeOfValue fn get_root_set_state( key: IdentifierId, - loads: &HashMap>, - visited: &mut HashSet, + loads: &FxHashMap>, + visited: &mut FxHashSet, ) -> Option { if visited.contains(&key) { return None; @@ -320,8 +321,8 @@ fn get_root_set_state( fn maybe_record_set_state_for_instr( instr: &react_compiler_hir::Instruction, env: &Environment, - set_state_loads: &mut HashMap>, - set_state_usages: &mut HashMap>, + set_state_loads: &mut FxHashMap>, + set_state_usages: &mut FxHashMap>, ) { let identifiers = &env.identifiers; let types = &env.types; @@ -349,10 +350,10 @@ fn maybe_record_set_state_for_instr( } } - let root = get_root_set_state(lvalue_id, set_state_loads, &mut HashSet::new()); + let root = get_root_set_state(lvalue_id, set_state_loads, &mut FxHashSet::default()); if let Some(root_id) = root { set_state_usages.entry(root_id).or_insert_with(|| { - let mut set = HashSet::new(); + let mut set = FxHashSet::default(); set.insert(LocKey::from_loc(&instr.lvalue.loc)); set }); @@ -377,12 +378,12 @@ pub fn validate_no_derived_computations_in_effects_exp( let identifiers = &env.identifiers; let mut context = ValidationContext { - functions: HashMap::new(), - candidate_dependencies: HashMap::new(), + functions: FxHashMap::default(), + candidate_dependencies: FxHashMap::default(), derivation_cache: DerivationCache::new(), - effects_cache: HashMap::new(), - set_state_loads: HashMap::new(), - set_state_usages: HashMap::new(), + effects_cache: FxHashMap::default(), + set_state_loads: FxHashMap::default(), + set_state_usages: FxHashMap::default(), }; // Initialize derivation cache based on function type @@ -395,7 +396,7 @@ pub fn validate_no_derived_computations_in_effects_exp( DerivationMetadata { place_identifier: place.identifier, place_name: name, - source_ids: indexmap::IndexSet::new(), + source_ids: IndexSet::default(), type_of_value: TypeOfValue::FromProps, is_state_source: true, }, @@ -411,7 +412,7 @@ pub fn validate_no_derived_computations_in_effects_exp( DerivationMetadata { place_identifier: place.identifier, place_name: name, - source_ids: indexmap::IndexSet::new(), + source_ids: IndexSet::default(), type_of_value: TypeOfValue::FromProps, is_state_source: true, }, @@ -477,7 +478,7 @@ fn record_phi_derivations( let identifiers = &env.identifiers; for phi in &block.phis { let mut type_of_value = TypeOfValue::Ignored; - let mut source_ids: indexmap::IndexSet = indexmap::IndexSet::new(); + let mut source_ids: IndexSet = IndexSet::default(); for (_block_id, operand) in &phi.operands { if let Some(operand_metadata) = context.derivation_cache.cache.get(&operand.identifier) @@ -522,7 +523,7 @@ fn record_instruction_derivations( let mut type_of_value = TypeOfValue::Ignored; let is_source = false; - let mut sources: indexmap::IndexSet = indexmap::IndexSet::new(); + let mut sources: IndexSet = IndexSet::default(); match &instr.value { InstructionValue::FunctionExpression { lowered_func, .. } => { @@ -575,7 +576,7 @@ fn record_instruction_derivations( context.derivation_cache.add_derivation_entry( lvalue_id, name, - indexmap::IndexSet::new(), + IndexSet::default(), TypeOfValue::FromState, true, ); @@ -614,7 +615,7 @@ fn record_instruction_derivations( context.derivation_cache.add_derivation_entry( lvalue_id, name, - indexmap::IndexSet::new(), + IndexSet::default(), TypeOfValue::FromState, true, ); @@ -643,8 +644,11 @@ fn record_instruction_derivations( for (operand_id, operand_loc) in each_instruction_operand(instr, env) { // Track setState usages if context.set_state_loads.contains_key(&operand_id) { - let root = - get_root_set_state(operand_id, &context.set_state_loads, &mut HashSet::new()); + let root = get_root_set_state( + operand_id, + &context.set_state_loads, + &mut FxHashSet::default(), + ); if let Some(root_id) = root { if let Some(usages) = context.set_state_usages.get_mut(&root_id) { usages.insert(LocKey::from_loc(&operand_loc)); @@ -754,7 +758,7 @@ struct TreeNode { fn build_tree_node( source_id: IdentifierId, context: &ValidationContext, - visited: &HashSet, + visited: &FxHashSet, ) -> Vec { let source_metadata = match context.derivation_cache.cache.get(&source_id) { Some(m) => m, @@ -773,7 +777,7 @@ fn build_tree_node( } let mut children: Vec = Vec::new(); - let mut named_siblings: indexmap::IndexSet = indexmap::IndexSet::new(); + let mut named_siblings: IndexSet = IndexSet::default(); for child_id in &source_metadata.source_ids { assert_ne!( @@ -813,8 +817,8 @@ fn render_tree( node: &TreeNode, indent: &str, is_last: bool, - props_set: &mut indexmap::IndexSet, - state_set: &mut indexmap::IndexSet, + props_set: &mut IndexSet, + state_set: &mut IndexSet, ) -> String { let prefix = format!( "{}{}", @@ -865,10 +869,10 @@ fn render_tree( fn get_fn_local_deps( func_id: Option, env: &Environment, -) -> Option> { +) -> Option> { let func_id = func_id?; let inner = &env.functions[func_id.0 as usize]; - let mut deps: HashSet = HashSet::new(); + let mut deps: FxHashSet = FxHashSet::default(); for (_block_id, block) in &inner.body.blocks { for &instr_id in &block.instructions { @@ -894,34 +898,35 @@ fn validate_effect( let types = &env.types; let functions = &env.functions; let effect_function = &functions[effect_func_id.0 as usize]; - let mut seen_blocks: HashSet = HashSet::new(); + let mut seen_blocks: FxHashSet = FxHashSet::default(); struct DerivedSetStateCall { callee_loc: Option, callee_id: IdentifierId, callee_identifier_name: Option, - source_ids: indexmap::IndexSet, + source_ids: IndexSet, } let mut effect_derived_set_state_calls: Vec = Vec::new(); - let mut effect_set_state_usages: HashMap> = HashMap::new(); + let mut effect_set_state_usages: FxHashMap> = + FxHashMap::default(); // Consider setStates in the effect's dependency array as being part of effectSetStateUsages for dep in dependencies { let root = get_root_set_state( dep.identifier, &context.set_state_loads, - &mut HashSet::new(), + &mut FxHashSet::default(), ); if let Some(root_id) = root { - let mut set = HashSet::new(); + let mut set = FxHashSet::default(); set.insert(LocKey::from_loc(&dep.loc)); effect_set_state_usages.insert(root_id, set); } } - let mut cleanup_function_deps: Option> = None; - let mut globals: HashSet = HashSet::new(); + let mut cleanup_function_deps: Option> = None; + let mut globals: FxHashSet = FxHashSet::default(); for (_block_id, block) in &effect_function.body.blocks { // Check for return -> cleanup function @@ -965,7 +970,7 @@ fn validate_effect( let root = get_root_set_state( operand_id, &context.set_state_loads, - &mut HashSet::new(), + &mut FxHashSet::default(), ); if let Some(root_id) = root { if let Some(usages) = effect_set_state_usages.get_mut(&root_id) { @@ -1045,7 +1050,7 @@ fn validate_effect( let root_set_state_call = get_root_set_state( derived.callee_id, &context.set_state_loads, - &mut HashSet::new(), + &mut FxHashSet::default(), ); if let Some(root_id) = root_set_state_call { let effect_usage_count = effect_set_state_usages @@ -1061,13 +1066,13 @@ fn validate_effect( && context.set_state_usages.contains_key(&root_id) && effect_usage_count == total_usage_count - 1 { - let mut props_set: indexmap::IndexSet = indexmap::IndexSet::new(); - let mut state_set: indexmap::IndexSet = indexmap::IndexSet::new(); + let mut props_set: IndexSet = IndexSet::default(); + let mut state_set: IndexSet = IndexSet::default(); - let mut root_nodes_map: indexmap::IndexMap = - indexmap::IndexMap::new(); + let mut root_nodes_map: IndexMap = + IndexMap::default(); for id in &derived.source_ids { - let nodes = build_tree_node(*id, context, &HashSet::new()); + let nodes = build_tree_node(*id, context, &FxHashSet::default()); for node in nodes { if !root_nodes_map.contains_key(&node.name) { root_nodes_map.insert(node.name.clone(), node); @@ -1162,9 +1167,9 @@ pub fn validate_no_derived_computations_in_effects( let effects_to_validate: Vec<(FunctionId, Vec)> = { let ids = &env.identifiers; let tys = &env.types; - let mut candidate_deps: HashMap> = HashMap::new(); - let mut functions_map: HashMap = HashMap::new(); - let mut locals_map: HashMap = HashMap::new(); + let mut candidate_deps: FxHashMap> = FxHashMap::default(); + let mut functions_map: FxHashMap = FxHashMap::default(); + let mut locals_map: FxHashMap = FxHashMap::default(); let mut result = Vec::new(); for (_, block) in &func.body.blocks { @@ -1280,8 +1285,8 @@ fn validate_effect_non_exp( } } - let mut seen_blocks: HashSet = HashSet::new(); - let mut dep_values: HashMap> = HashMap::new(); + let mut seen_blocks: FxHashSet = FxHashSet::default(); + let mut dep_values: FxHashMap> = FxHashMap::default(); for dep in effect_deps { dep_values.insert(*dep, vec![*dep]); } @@ -1296,7 +1301,7 @@ fn validate_effect_non_exp( } for phi in &block.phis { - let mut aggregate: HashSet = HashSet::new(); + let mut aggregate: FxHashSet = FxHashSet::default(); for operand in phi.operands.values() { if let Some(deps) = dep_values.get(&operand.identifier) { for d in deps { @@ -1326,7 +1331,7 @@ fn validate_effect_non_exp( | InstructionValue::TemplateLiteral { .. } | InstructionValue::CallExpression { .. } | InstructionValue::MethodCall { .. } => { - let mut aggregate: HashSet = HashSet::new(); + let mut aggregate: FxHashSet = FxHashSet::default(); for operand in non_exp_value_operands(&instr.value) { if let Some(deps) = dep_values.get(&operand) { for d in deps { @@ -1343,7 +1348,7 @@ fn validate_effect_non_exp( if is_set_state_type(callee_ty) && args.len() == 1 { if let PlaceOrSpread::Place(arg) = &args[0] { if let Some(deps) = dep_values.get(&arg.identifier) { - let dep_set: HashSet<_> = deps.iter().collect(); + let dep_set: FxHashSet<_> = deps.iter().collect(); if dep_set.len() == effect_deps.len() { if let Some(loc) = callee.loc { set_state_locs.push(loc); diff --git a/compiler/crates/react_compiler_validation/src/validate_no_freezing_known_mutable_functions.rs b/compiler/crates/react_compiler_validation/src/validate_no_freezing_known_mutable_functions.rs index 10f173da912..5dc85125338 100644 --- a/compiler/crates/react_compiler_validation/src/validate_no_freezing_known_mutable_functions.rs +++ b/compiler/crates/react_compiler_validation/src/validate_no_freezing_known_mutable_functions.rs @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation, @@ -53,7 +53,7 @@ fn check_no_freezing_known_mutable_functions( env: &Environment, ) -> Vec { // Maps an identifier to the mutation effect that makes it "known mutable" - let mut context_mutation_effects: HashMap = HashMap::new(); + let mut context_mutation_effects: FxHashMap = FxHashMap::default(); let mut diagnostics: Vec = Vec::new(); for (_block_id, block) in &func.body.blocks { @@ -83,7 +83,7 @@ fn check_no_freezing_known_mutable_functions( InstructionValue::FunctionExpression { lowered_func, .. } => { let inner_function = &functions[lowered_func.func.0 as usize]; if let Some(ref aliasing_effects) = inner_function.aliasing_effects { - let context_ids: HashSet = inner_function + let context_ids: FxHashSet = inner_function .context .iter() .map(|place| place.identifier) @@ -170,7 +170,7 @@ fn check_no_freezing_known_mutable_functions( /// If an operand with Effect::Freeze is a known-mutable function, emit a diagnostic. fn check_operand_for_freeze_violation( operand: &Place, - context_mutation_effects: &HashMap, + context_mutation_effects: &FxHashMap, identifiers: &[Identifier], diagnostics: &mut Vec, ) { diff --git a/compiler/crates/react_compiler_validation/src/validate_no_ref_access_in_render.rs b/compiler/crates/react_compiler_validation/src/validate_no_ref_access_in_render.rs index 0a0ac6591ec..22708b1dbb4 100644 --- a/compiler/crates/react_compiler_validation/src/validate_no_ref_access_in_render.rs +++ b/compiler/crates/react_compiler_validation/src/validate_no_ref_access_in_render.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation, @@ -270,16 +270,16 @@ fn join_ref_access_types_many(types: &[RefAccessType]) -> RefAccessType { struct Env { changed: bool, - data: HashMap, - temporaries: HashMap, + data: FxHashMap, + temporaries: FxHashMap, } impl Env { fn new() -> Self { Self { changed: false, - data: HashMap::new(), - temporaries: HashMap::new(), + data: FxHashMap::default(), + temporaries: FxHashMap::default(), } } @@ -626,7 +626,7 @@ fn validate_no_ref_access_in_render_impl( } // Collect identifiers that are interpolated as JSX children - let mut interpolated_as_jsx: HashSet = HashSet::new(); + let mut interpolated_as_jsx: FxHashSet = FxHashSet::default(); for (_, block) in &func.body.blocks { for &instr_id in &block.instructions { let instr = &func.instructions[instr_id.0 as usize]; @@ -890,7 +890,8 @@ fn validate_no_ref_access_in_render_impl( * use the effects to determine what validation to apply. * Track visited id:kind pairs to avoid duplicate errors. */ - let mut visited_effects: HashSet = HashSet::new(); + let mut visited_effects: FxHashSet = + FxHashSet::default(); for effect in effects { let (place, validation) = match effect { AliasingEffect::Freeze { value, .. } => { diff --git a/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs b/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs index 926d8523259..47948feffaa 100644 --- a/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs +++ b/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs @@ -12,7 +12,7 @@ //! //! Port of ValidateNoSetStateInEffects.ts. -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, @@ -37,7 +37,7 @@ pub fn validate_no_set_state_in_effects( let enable_allow_set_state_from_refs = env.config.enable_allow_set_state_from_refs_in_effects; // Map from IdentifierId to the Place where the setState originated - let mut set_state_functions: HashMap = HashMap::new(); + let mut set_state_functions: FxHashMap = FxHashMap::default(); let mut errors = CompilerError::new(); for (_block_id, block) in &func.body.blocks { @@ -246,7 +246,7 @@ fn push_error(errors: &mut CompilerError, info: &SetStateInfo, enable_verbose: b /// Recursively collect all Place identifiers from a destructure pattern. fn collect_destructure_places( pattern: &react_compiler_hir::Pattern, - ref_derived_values: &mut HashSet, + ref_derived_values: &mut FxHashSet, ) { match pattern { react_compiler_hir::Pattern::Array(arr) => { @@ -279,7 +279,7 @@ fn collect_destructure_places( fn is_derived_from_ref( id: IdentifierId, - ref_derived_values: &HashSet, + ref_derived_values: &FxHashSet, identifiers: &[Identifier], types: &[Type], ) -> bool { @@ -306,12 +306,12 @@ fn collect_operands(value: &InstructionValue, functions: &[HirFunction]) -> Vec< fn create_ref_controlled_block_checker( func: &HirFunction, next_block_id_counter: u32, - ref_derived_values: &HashSet, + ref_derived_values: &FxHashSet, identifiers: &[Identifier], types: &[Type], -) -> Result, CompilerDiagnostic> { +) -> Result, CompilerDiagnostic> { let post_dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?; - let mut cache: HashMap = HashMap::new(); + let mut cache: FxHashMap = FxHashMap::default(); for (block_id, _block) in &func.body.blocks { let frontier = post_dominator_frontier(func, &post_dominators, *block_id); @@ -365,7 +365,7 @@ fn create_ref_controlled_block_checker( /// Tracks ref-derived values to allow setState when the value being set comes from a ref. fn get_set_state_call( func: &HirFunction, - set_state_functions: &mut HashMap, + set_state_functions: &mut FxHashMap, identifiers: &[Identifier], types: &[Type], functions: &[HirFunction], @@ -373,7 +373,7 @@ fn get_set_state_call( next_block_id_counter: u32, source_code: Option<&str>, ) -> Result, CompilerDiagnostic> { - let mut ref_derived_values: HashSet = HashSet::new(); + let mut ref_derived_values: FxHashSet = FxHashSet::default(); // First pass: collect ref-derived values (needed before building control dominator checker) // We do a pre-pass to seed ref_derived_values so the control dominator checker has them. @@ -432,7 +432,7 @@ fn get_set_state_call( types, )? } else { - HashMap::new() + FxHashMap::default() }; let is_ref_controlled_block = |block_id: BlockId| -> bool { diff --git a/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_render.rs b/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_render.rs index 19a7c5ea2a7..998ac26ec67 100644 --- a/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_render.rs +++ b/compiler/crates/react_compiler_validation/src/validate_no_set_state_in_render.rs @@ -7,7 +7,7 @@ //! //! Port of ValidateNoSetStateInRender.ts. -use std::collections::HashSet; +use rustc_hash::FxHashSet; use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory}; use react_compiler_hir::dominator::compute_unconditional_blocks; @@ -18,7 +18,7 @@ pub fn validate_no_set_state_in_render( func: &HirFunction, env: &mut Environment, ) -> Result<(), CompilerDiagnostic> { - let mut unconditional_set_state_functions: HashSet = HashSet::new(); + let mut unconditional_set_state_functions: FxHashSet = FxHashSet::default(); let next_block_id = env.next_block_id().0; let diagnostics = validate_impl( func, @@ -52,9 +52,9 @@ fn validate_impl( functions: &[HirFunction], next_block_id_counter: u32, enable_use_keyed_state: bool, - unconditional_set_state_functions: &mut HashSet, + unconditional_set_state_functions: &mut FxHashSet, ) -> Result, CompilerDiagnostic> { - let unconditional_blocks: HashSet = + let unconditional_blocks: FxHashSet = compute_unconditional_blocks(func, next_block_id_counter)?; let mut active_manual_memo_id: Option = None; let mut errors: Vec = Vec::new(); diff --git a/compiler/crates/react_compiler_validation/src/validate_preserved_manual_memoization.rs b/compiler/crates/react_compiler_validation/src/validate_preserved_manual_memoization.rs index f83263c1a10..b757a927dd9 100644 --- a/compiler/crates/react_compiler_validation/src/validate_preserved_manual_memoization.rs +++ b/compiler/crates/react_compiler_validation/src/validate_preserved_manual_memoization.rs @@ -9,7 +9,7 @@ //! accurately preserved, and that no originally memoized values became //! unmemoized in the output. -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation, @@ -25,11 +25,11 @@ use react_compiler_hir::{ /// State tracked during manual memo validation within a StartMemoize..FinishMemoize range. struct ManualMemoBlockState { /// Reassigned temporaries (declaration_id -> set of identifier ids that were reassigned to it). - reassignments: HashMap>, + reassignments: FxHashMap>, /// Source location of the StartMemoize instruction. loc: Option, /// Declarations produced within this manual memo block. - decls: HashSet, + decls: FxHashSet, /// Normalized deps from source (useMemo/useCallback dep array). deps_from_source: Option>, /// Manual memo id from StartMemoize. @@ -41,11 +41,11 @@ struct VisitorState<'a> { env: &'a mut Environment, manual_memo_state: Option, /// Completed (non-pruned) scope IDs. - scopes: HashSet, + scopes: FxHashSet, /// Completed pruned scope IDs. - pruned_scopes: HashSet, + pruned_scopes: FxHashSet, /// Map from identifier ID to its normalized manual memo dependency. - temporaries: HashMap, + temporaries: FxHashMap, } /// Validate that manual memoization (useMemo/useCallback) is preserved. @@ -59,9 +59,9 @@ pub fn validate_preserved_manual_memoization(func: &ReactiveFunction, env: &mut let mut state = VisitorState { env, manual_memo_state: None, - scopes: HashSet::new(), - pruned_scopes: HashSet::new(), - temporaries: HashMap::new(), + scopes: FxHashSet::default(), + pruned_scopes: FxHashSet::default(), + temporaries: FxHashMap::default(), }; visit_block(&func.body, &mut state); } @@ -203,10 +203,10 @@ fn visit_instruction(instr: &ReactiveInstruction, state: &mut VisitorState) { state.manual_memo_state = Some(ManualMemoBlockState { loc: instr.loc, - decls: HashSet::new(), + decls: FxHashSet::default(), deps_from_source, manual_memo_id: *manual_memo_id, - reassignments: HashMap::new(), + reassignments: FxHashMap::default(), }); // Check that each dependency's scope has completed before the memo @@ -518,7 +518,7 @@ fn destructure_lvalue_places(pattern: &react_compiler_hir::Pattern) -> Vec<&Plac /// Check if an identifier is unmemoized (has a scope that hasn't completed). fn is_unmemoized( id: IdentifierId, - completed_scopes: &HashSet, + completed_scopes: &FxHashSet, identifiers: &[Identifier], ) -> bool { let ident = &identifiers[id.0 as usize]; @@ -675,8 +675,8 @@ fn get_compare_dependency_result_description(result: CompareDependencyResult) -> fn validate_inferred_dep( dep_id: IdentifierId, dep_path: &[DependencyPathEntry], - temporaries: &HashMap, - decls_within_memo_block: &HashSet, + temporaries: &FxHashMap, + decls_within_memo_block: &FxHashSet, valid_deps_in_memo_block: &[ManualMemoDependency], env: &mut Environment, memo_location: Option, diff --git a/compiler/crates/react_compiler_validation/src/validate_static_components.rs b/compiler/crates/react_compiler_validation/src/validate_static_components.rs index deca1c9da2b..35e7b5748dd 100644 --- a/compiler/crates/react_compiler_validation/src/validate_static_components.rs +++ b/compiler/crates/react_compiler_validation/src/validate_static_components.rs @@ -9,7 +9,7 @@ //! //! Port of ValidateStaticComponents.ts. -use std::collections::HashMap; +use rustc_hash::FxHashMap; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation, @@ -22,8 +22,8 @@ use react_compiler_hir::{HirFunction, IdentifierId, InstructionValue, JsxTag}; /// Called via `env.logErrors()` pattern in Pipeline.ts. pub fn validate_static_components(func: &HirFunction) -> CompilerError { let mut error = CompilerError::new(); - let mut known_dynamic_components: HashMap> = - HashMap::new(); + let mut known_dynamic_components: FxHashMap> = + FxHashMap::default(); for (_block_id, block) in &func.body.blocks { // Process phis: propagate dynamic component knowledge through phi nodes diff --git a/compiler/crates/react_compiler_validation/src/validate_use_memo.rs b/compiler/crates/react_compiler_validation/src/validate_use_memo.rs index 7691e73b7f8..d19c57f00bc 100644 --- a/compiler/crates/react_compiler_validation/src/validate_use_memo.rs +++ b/compiler/crates/react_compiler_validation/src/validate_use_memo.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use react_compiler_diagnostics::{ CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation, @@ -38,11 +38,11 @@ fn validate_use_memo_impl( validate_no_void_use_memo: bool, ) -> CompilerError { let mut void_memo_errors = CompilerError::new(); - let mut use_memos: HashSet = HashSet::new(); - let mut react: HashSet = HashSet::new(); - let mut func_exprs: HashMap = HashMap::new(); - let mut unused_use_memos: HashMap)> = - HashMap::new(); + let mut use_memos: FxHashSet = FxHashSet::default(); + let mut react: FxHashSet = FxHashSet::default(); + let mut func_exprs: FxHashMap = FxHashMap::default(); + let mut unused_use_memos: FxHashMap)> = + FxHashMap::default(); for (_block_id, block) in &func.body.blocks { for &instr_id in &block.instructions { @@ -157,9 +157,9 @@ fn handle_possible_use_memo_call( functions: &[HirFunction], errors: &mut CompilerError, void_memo_errors: &mut CompilerError, - use_memos: &HashSet, - func_exprs: &HashMap, - unused_use_memos: &mut HashMap)>, + use_memos: &FxHashSet, + func_exprs: &FxHashMap, + unused_use_memos: &mut FxHashMap)>, callee: &Place, args: &[PlaceOrSpread], lvalue: &Place, @@ -254,7 +254,7 @@ fn handle_possible_use_memo_call( } fn validate_no_context_variable_assignment(func: &HirFunction, errors: &mut CompilerError) { - let context: HashSet = + let context: FxHashSet = func.context.iter().map(|place| place.identifier).collect(); for (_block_id, block) in &func.body.blocks { diff --git a/packages/react-devtools-shared/src/__tests__/utils-test.js b/packages/react-devtools-shared/src/__tests__/utils-test.js index 9b9e82102cc..3d42aa28741 100644 --- a/packages/react-devtools-shared/src/__tests__/utils-test.js +++ b/packages/react-devtools-shared/src/__tests__/utils-test.js @@ -501,5 +501,13 @@ function f() { } formatConsoleArguments('This is the %s template', undefined), ).toEqual(['This is the undefined template']); }); + + it('keeps a trailing percent sign', () => { + expect(formatConsoleArguments('Progress 100%', 'extra')).toEqual([ + 'Progress 100%', + 'extra', + ]); + expect(formatConsoleArguments('%s 100%', 'done')).toEqual(['done 100%']); + }); }); }); diff --git a/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js b/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js index eaf4170970e..551f4cf674b 100644 --- a/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js +++ b/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js @@ -64,7 +64,13 @@ export default function formatConsoleArguments( } default: - template += `%${nextChar}`; + if (nextChar === undefined) { + // A trailing '%' with no following character. Keep it as a literal + // '%' rather than emitting the string 'undefined'. + template += '%'; + } else { + template += `%${nextChar}`; + } } }