diff --git a/crates/oxc_transformer/src/es2022/class_properties/class.rs b/crates/oxc_transformer/src/es2022/class_properties/class.rs index 4054d8c6813de..7265cf37e7271 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/class.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/class.rs @@ -385,6 +385,8 @@ impl<'a> ClassProperties<'a, '_> { if class_details.is_transform_required { self.transform_class_declaration_on_exit_impl(class, ctx); } else { + // Note: `is_transform_required` is `true` if class has any private properties/methods, + // so no need to touch `private_field_count` here debug_assert!(class_details.bindings.temp.is_none()); } // Pop off stack. We're done! @@ -446,6 +448,7 @@ impl<'a> ClassProperties<'a, '_> { if let Some(private_props) = &class_details.private_props { self.private_field_count -= private_props.len(); + if self.private_fields_as_properties { let mut private_props = private_props .iter() @@ -536,6 +539,8 @@ impl<'a> ClassProperties<'a, '_> { if class_details.is_transform_required { self.transform_class_expression_on_exit_impl(expr, ctx); } else { + // Note: `is_transform_required` is `true` if class has any private properties/methods, + // so no need to touch `private_field_count` here debug_assert!(class_details.bindings.temp.is_none()); } @@ -591,6 +596,7 @@ impl<'a> ClassProperties<'a, '_> { // Also insert `var _prop;` temp var declarations for private static props. if let Some(private_props) = &class_details.private_props { self.private_field_count -= private_props.len(); + // Insert `var _prop;` declarations here rather than when binding was created to maintain // same order of `var` declarations as Babel. // `c = class C { #x = 1; static y = 2; }` -> `var _C, _x;` diff --git a/crates/oxc_transformer/src/es2022/class_properties/mod.rs b/crates/oxc_transformer/src/es2022/class_properties/mod.rs index fbf026a81224e..61c23bf64cf8a 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/mod.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/mod.rs @@ -261,6 +261,8 @@ pub struct ClassProperties<'a, 'ctx> { /// This problem only affects class expressions. Class declarations aren't affected, /// as their exit-phase transform happens in `exit_class`. classes_stack: ClassesStack<'a>, + /// Count of private fields in current class and parent classes. + private_field_count: usize, // ----- State used only during enter phase ----- // @@ -277,8 +279,6 @@ pub struct ClassProperties<'a, 'ctx> { /// Keys are symbols' IDs. /// Values are initially the original name of binding, later on the name of new UID name. clashing_constructor_symbols: FxHashMap>, - /// Count of private fields in class and also included parent classes. - private_field_count: usize, // ----- State used only during exit phase ----- // @@ -309,12 +309,12 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { transform_static_blocks, ctx, classes_stack: ClassesStack::new(), + private_field_count: 0, // Temporary values - overwritten when entering class instance_inits_scope_id: ScopeId::new(0), instance_inits_constructor_scope_id: None, // `Vec`s and `FxHashMap`s which are reused for every class being transformed clashing_constructor_symbols: FxHashMap::default(), - private_field_count: 0, insert_before: vec![], insert_after_exprs: vec![], insert_after_stmts: vec![], @@ -342,7 +342,9 @@ impl<'a> Traverse<'a> for ClassProperties<'a, '_> { // `#[inline]` for fast exit for expressions which are not any of the transformed types #[inline] fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - // Return early if no private fields are found. + // All of transforms below only act on `PrivateFieldExpression`s or `PrivateInExpression`s. + // If we're not inside a class which has private fields, `#prop` can't be present here, + // so exit early - fast path for common case. if self.private_field_count == 0 { return; }