diff --git a/Cargo.lock b/Cargo.lock index 3f523d98e9a2a..436ae0c865148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2249,6 +2249,7 @@ dependencies = [ "oxc_allocator 0.93.0", "oxc_ast 0.93.0", "oxc_data_structures 0.93.0", + "oxc_index 4.1.0", "oxc_parser 0.93.0", "oxc_semantic 0.93.0", "oxc_span 0.93.0", diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 33bcc50aa0957..4884f6a08d5c2 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -2751,9 +2751,9 @@ impl Gen for AccessorProperty<'_> { impl Gen for PrivateIdentifier<'_> { fn r#gen(&self, p: &mut Codegen, _ctx: Context) { - let name = if let Some(class_index) = p.current_class_index() - && let Some(mangled) = &p.private_member_mappings.as_ref().and_then(|m| { - m[0..=class_index].iter().rev().find_map(|m| m.get(self.name.as_str())) + let name = if let Some(private_member_mappings) = &p.private_member_mappings + && let Some(mangled) = p.current_class_ids().find_map(|class_id| { + private_member_mappings.get(class_id).and_then(|m| m.get(self.name.as_str())) }) { (*mangled).clone() } else { diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index 20bdc12c0fdc3..9c561620ff7b7 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -11,9 +11,11 @@ use cow_utils::CowUtils; use oxc_ast::ast::*; use oxc_data_structures::{code_buffer::CodeBuffer, stack::Stack}; +use oxc_index::IndexVec; use oxc_semantic::Scoping; use oxc_span::{CompactStr, GetSpan, Span}; use oxc_syntax::{ + class::ClassId, identifier::{is_identifier_part, is_identifier_part_ascii}, operator::{BinaryOperator, UnaryOperator, UpdateOperator}, precedence::Precedence, @@ -84,7 +86,7 @@ pub struct Codegen<'a> { scoping: Option, /// Private member name mappings for mangling - private_member_mappings: Option>>, + private_member_mappings: Option>>, /// Output Code code: CodeBuffer, @@ -95,7 +97,8 @@ pub struct Codegen<'a> { need_space_before_dot: usize, print_next_indent_as_space: bool, binary_expr_stack: Stack>, - class_stack_pos: usize, + class_stack: Stack, + next_class_id: ClassId, /// Indicates the output is JSX type, it is set in [`Program::gen`] and the result /// is obtained by [`oxc_span::SourceType::is_jsx`] is_jsx: bool, @@ -157,7 +160,8 @@ impl<'a> Codegen<'a> { need_space_before_dot: 0, print_next_indent_as_space: false, binary_expr_stack: Stack::with_capacity(12), - class_stack_pos: 0, + class_stack: Stack::with_capacity(4), + next_class_id: ClassId::from_usize(0), prev_op_end: 0, prev_reg_exp_end: 0, prev_op: None, @@ -204,7 +208,7 @@ impl<'a> Codegen<'a> { #[must_use] pub fn with_private_member_mappings( mut self, - mappings: Option>>, + mappings: Option>>, ) -> Self { self.private_member_mappings = mappings; self @@ -467,17 +471,19 @@ impl<'a> Codegen<'a> { #[inline] fn enter_class(&mut self) { - self.class_stack_pos += 1; + let class_id = self.next_class_id; + self.next_class_id = ClassId::from_usize(self.next_class_id.index() + 1); + self.class_stack.push(class_id); } #[inline] fn exit_class(&mut self) { - self.class_stack_pos -= 1; + self.class_stack.pop(); } #[inline] - fn current_class_index(&self) -> Option { - if self.class_stack_pos > 0 { Some(self.class_stack_pos - 1) } else { None } + fn current_class_ids(&self) -> impl Iterator { + self.class_stack.iter().rev().copied() } #[inline] diff --git a/crates/oxc_mangler/Cargo.toml b/crates/oxc_mangler/Cargo.toml index 650ed533581fc..de0846206100d 100644 --- a/crates/oxc_mangler/Cargo.toml +++ b/crates/oxc_mangler/Cargo.toml @@ -24,6 +24,7 @@ doctest = false oxc_allocator = { workspace = true, features = ["bitset"] } oxc_ast = { workspace = true } oxc_data_structures = { workspace = true, features = ["inline_string"] } +oxc_index = { workspace = true } oxc_semantic = { workspace = true } oxc_span = { workspace = true } oxc_syntax = { workspace = true } diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index 12f8e89724432..66e059c8dcab3 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -2,6 +2,8 @@ use std::iter::{self, repeat_with}; use itertools::Itertools; use keep_names::collect_name_symbols; +use oxc_index::IndexVec; +use oxc_syntax::class::ClassId; use rustc_hash::{FxHashMap, FxHashSet}; use base54::base54; @@ -59,7 +61,7 @@ pub struct ManglerReturn { pub scoping: Scoping, /// A vector where each element corresponds to a class in declaration order. /// Each element is a mapping from original private member names to their mangled names. - pub class_private_mappings: std::vec::Vec>, + pub class_private_mappings: IndexVec>, } /// # Name Mangler / Symbol Minification @@ -560,7 +562,7 @@ impl<'t> Mangler<'t> { /// Returns a Vec where each element corresponds to a class in declaration order fn collect_private_members_from_semantic( semantic: &Semantic<'_>, - ) -> std::vec::Vec> { + ) -> IndexVec> { let classes = semantic.classes(); classes .elements diff --git a/crates/oxc_minifier/tests/mangler/mod.rs b/crates/oxc_minifier/tests/mangler/mod.rs index 67675749123c7..91ff0bf848fa6 100644 --- a/crates/oxc_minifier/tests/mangler/mod.rs +++ b/crates/oxc_minifier/tests/mangler/mod.rs @@ -124,6 +124,7 @@ fn private_member_mangling() { "class Foo { publicField = 1; #privateField = 2; getSum() { return this.publicField + this.#privateField; } }", // Test same names across different classes should reuse mangled names "class A { #field = 1; #method() { return this.#field; } } class B { #field = 2; #method() { return this.#field; } }", + "class A { #field = 1; #method() { return this.#field; } } class B { #field2 = 2; #method2() { return this.#field2; } }", ]; let mut snapshot = String::new(); diff --git a/crates/oxc_minifier/tests/mangler/snapshots/private_member_mangling.snap b/crates/oxc_minifier/tests/mangler/snapshots/private_member_mangling.snap index 66c197afecb2d..02e6a36075da5 100644 --- a/crates/oxc_minifier/tests/mangler/snapshots/private_member_mangling.snap +++ b/crates/oxc_minifier/tests/mangler/snapshots/private_member_mangling.snap @@ -108,3 +108,17 @@ class B { return this.#e; } } + +class A { #field = 1; #method() { return this.#field; } } class B { #field2 = 2; #method2() { return this.#field2; } } +class A { + #e = 1; + #t() { + return this.#e; + } +} +class B { + #e = 2; + #t() { + return this.#e; + } +}