From 0f5cc4d8e8f0c2a05f6e12aa00783b0405415c6d Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 26 Dec 2024 23:54:50 +0800 Subject: [PATCH] feat(transformer/class-properties): transform static/instance accessor methods --- .../src/es2022/class_properties/class.rs | 35 ++- .../es2022/class_properties/class_details.rs | 41 ++- .../es2022/class_properties/private_field.rs | 270 ++++++++++++++---- .../es2022/class_properties/private_method.rs | 19 +- .../src/es2022/class_properties/utils.rs | 28 +- .../accessors/class-binding/output.js | 11 + .../accessors/preserve-comments/output.js | 10 + .../accessors/tagged-template/output.js | 11 + .../destructure-set/output.js | 10 + .../preserve-comments/output.js | 5 + .../tagged-template/output.js | 11 + .../snapshots/babel.snap.md | 24 +- .../snapshots/babel_exec.snap.md | 110 ++----- 13 files changed, 399 insertions(+), 186 deletions(-) create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/class-binding/output.js create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/preserve-comments/output.js create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/tagged-template/output.js create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/destructure-set/output.js create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/preserve-comments/output.js create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/tagged-template/output.js diff --git a/crates/oxc_transformer/src/es2022/class_properties/class.rs b/crates/oxc_transformer/src/es2022/class_properties/class.rs index d6b823aeba739..0d3f223ce025b 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/class.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/class.rs @@ -1,6 +1,7 @@ //! ES2022: Class Properties //! Transform of class itself. +use indexmap::map::Entry; use oxc_allocator::{Address, GetAddress}; use oxc_ast::{ast::*, NONE}; use oxc_span::SPAN; @@ -93,7 +94,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let binding = ctx.generate_uid_in_current_hoist_scope(&ident.name); private_props.insert( ident.name.clone(), - PrivateProp::new(binding, prop.r#static, false, false), + PrivateProp::new(binding, prop.r#static, None, false), ); } @@ -135,10 +136,22 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ctx.current_block_scope_id(), SymbolFlags::FunctionScopedVariable, ); - private_props.insert( - ident.name.clone(), - PrivateProp::new(binding, method.r#static, true, false), - ); + + match private_props.entry(ident.name.clone()) { + Entry::Occupied(mut entry) => { + // If there's already a binding for this private property, + // it's a setter or getter, so store the binding in `binding2`. + entry.get_mut().set_binding2(binding); + } + Entry::Vacant(entry) => { + entry.insert(PrivateProp::new( + binding, + method.r#static, + Some(method.kind), + false, + )); + } + } } } ClassElement::AccessorProperty(prop) => { @@ -148,7 +161,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let dummy_binding = BoundIdentifier::new(Atom::empty(), SymbolId::new(0)); private_props.insert( ident.name.clone(), - PrivateProp::new(dummy_binding, prop.r#static, true, true), + PrivateProp::new(dummy_binding, prop.r#static, None, true), ); } } @@ -443,7 +456,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { self.ctx.statement_injector.insert_many_before( &stmt_address, private_props.iter().filter_map(|(name, prop)| { - if prop.is_method || prop.is_accessor { + if prop.is_method() || prop.is_accessor { return None; } @@ -459,10 +472,10 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { self.ctx.statement_injector.insert_many_before( &stmt_address, private_props.values().filter_map(|prop| { - if prop.is_static || (prop.is_method && has_method) || prop.is_accessor { + if prop.is_static || (prop.is_method() && has_method) || prop.is_accessor { return None; } - if prop.is_method { + if prop.is_method() { // `var _C_brand = new WeakSet();` has_method = true; let binding = class_details.bindings.brand(); @@ -583,7 +596,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { // TODO(improve-on-babel): Simplify this. if self.private_fields_as_properties { exprs.extend(private_props.iter().filter_map(|(name, prop)| { - if prop.is_method || prop.is_accessor { + if prop.is_method() || prop.is_accessor { return None; } @@ -598,7 +611,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let mut weakmap_symbol_id = None; let mut has_method = false; exprs.extend(private_props.values().filter_map(|prop| { - if prop.is_method || prop.is_accessor { + if prop.is_method() || prop.is_accessor { if prop.is_static || has_method { return None; } diff --git a/crates/oxc_transformer/src/es2022/class_properties/class_details.rs b/crates/oxc_transformer/src/es2022/class_properties/class_details.rs index 0289923c95e8a..dbfb5b7ecf286 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/class_details.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/class_details.rs @@ -40,18 +40,29 @@ impl<'a> ClassDetails<'a> { pub(super) struct PrivateProp<'a> { pub binding: BoundIdentifier<'a>, pub is_static: bool, - pub is_method: bool, + pub method_kind: Option, pub is_accessor: bool, + // For accessor methods, they have two bindings, + // one for getter and one for setter. + pub binding2: Option>, } impl<'a> PrivateProp<'a> { pub fn new( binding: BoundIdentifier<'a>, is_static: bool, - is_method: bool, + method_kind: Option, is_accessor: bool, ) -> Self { - Self { binding, is_static, is_method, is_accessor } + Self { binding, is_static, method_kind, is_accessor, binding2: None } + } + + pub fn is_method(&self) -> bool { + self.method_kind.is_some() + } + + pub fn set_binding2(&mut self, binding: BoundIdentifier<'a>) { + self.binding2 = Some(binding); } } @@ -105,6 +116,7 @@ impl<'a> ClassesStack<'a> { pub fn find_private_prop<'b>( &'b mut self, ident: &PrivateIdentifier<'a>, + is_assignment: bool, ) -> ResolvedPrivateProp<'a, 'b> { // Check for binding in closest class first, then enclosing classes. // We skip the first, because this is a `NonEmptyStack` with dummy first entry. @@ -112,12 +124,26 @@ impl<'a> ClassesStack<'a> { for class in self.stack[1..].iter_mut().rev() { if let Some(private_props) = &mut class.private_props { if let Some(prop) = private_props.get(&ident.name) { + let prop_binding = match (is_assignment, prop.method_kind) { + (false, Some(MethodDefinitionKind::Set)) + | (true, Some(MethodDefinitionKind::Get)) => { + if let Some(binding2) = &prop.binding2 { + binding2 + } else { + // TODO: Warn there is no setter method + &prop.binding + } + } + _ => &prop.binding, + }; + return ResolvedPrivateProp { - prop_binding: &prop.binding, + prop_binding, class_bindings: &mut class.bindings, is_static: prop.is_static, - is_method: prop.is_method, - is_accessor: prop.is_accessor, + is_method: prop.method_kind.is_some(), + is_accessor: prop.is_accessor + || prop.method_kind.is_some_and(|kind| kind.is_accessor()), is_declaration: class.is_declaration, }; } @@ -140,7 +166,8 @@ pub(super) struct ResolvedPrivateProp<'a, 'b> { pub is_static: bool, /// `true` if is a private method pub is_method: bool, - /// `true` if is a private accessor property + /// `true` if is a private accessor property or [`PrivateProp::method_kind`] is + /// `Some(MethodDefinitionKind::Get)` or `Some(MethodDefinitionKind::Set)` pub is_accessor: bool, /// `true` if class which defines this property is a class declaration pub is_declaration: bool, diff --git a/crates/oxc_transformer/src/es2022/class_properties/private_field.rs b/crates/oxc_transformer/src/es2022/class_properties/private_field.rs index d96b28f3e9787..445b2964faa91 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/private_field.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/private_field.rs @@ -3,6 +3,7 @@ use std::mem; +use oxc_allocator::Vec as ArenaVec; use oxc_ast::{ast::*, NONE}; use oxc_span::SPAN; use oxc_syntax::{reference::ReferenceId, symbol::SymbolId}; @@ -14,7 +15,7 @@ use crate::{common::helper_loader::Helper, TransformCtx}; use super::{ utils::{ - create_assignment, create_underscore_ident_name, + create_assignment, create_bind_call, create_call_call, create_underscore_ident_name, debug_assert_expr_is_not_parenthesis_or_typescript_syntax, }, ClassProperties, ResolvedPrivateProp, @@ -26,6 +27,12 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// Not loose: /// * Instance prop: `object.#prop` -> `_classPrivateFieldGet2(_prop, object)` /// * Static prop: `object.#prop` -> `_assertClassBrand(Class, object, _prop)._` + /// * Instance method: `object.#method` -> `_assertClassBrand(_Class_brand, object, _prop)` + /// * Static method: `object.#method` -> `_assertClassBrand(Class, object, _prop)` + /// * Instance getter: `object.#getter` -> `get_getter.call(_assertClassBrand(_Class_brand, object))` + /// * Static getter: `object.#getter` -> `get_getter.call(_assertClassBrand(Class, object))` + /// * Instance setter: `object.#setter` -> `set_setter.bind(_assertClassBrand(_Class_brand, object))` + /// * Static setter: `object.#setter` -> `set_setter.bind(_assertClassBrand(Class, object))` /// /// Loose: `object.#prop` -> `_classPrivateFieldLooseBase(object, _prop)[_prop]` // @@ -37,12 +44,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ctx: &mut TraverseCtx<'a>, ) { let Expression::PrivateFieldExpression(field_expr) = expr else { unreachable!() }; - - if let Some(replacement) = - self.transform_private_field_expression_impl(field_expr, false, ctx) - { - *expr = replacement; - } + *expr = self.transform_private_field_expression_impl(field_expr, false, ctx); } fn transform_private_field_expression_impl( @@ -50,7 +52,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { field_expr: &mut PrivateFieldExpression<'a>, is_assignment: bool, ctx: &mut TraverseCtx<'a>, - ) -> Option> { + ) -> Expression<'a> { let ResolvedPrivateProp { prop_binding, class_bindings, @@ -58,30 +60,25 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { is_method, is_accessor, is_declaration, - } = self.classes_stack.find_private_prop(&field_expr.field); + } = self.classes_stack.find_private_prop(&field_expr.field, is_assignment); let span = field_expr.span; let object = ctx.ast.move_expression(&mut field_expr.object); if self.private_fields_as_properties { // `_classPrivateFieldLooseBase(object, _prop)[_prop]` - return Some(Expression::from(Self::create_private_field_member_expr_loose( + return Expression::from(Self::create_private_field_member_expr_loose( object, prop_binding, span, self.ctx, ctx, - ))); + )); } let prop_ident = prop_binding.create_read_expression(ctx); - let replacement = if is_static { - if is_assignment && (is_method || is_accessor) { - // TODO: Handle assignment to static private method or static accessor - return None; - } - + if is_static { // TODO: Ensure there are tests for nested classes with references to private static props // of outer class inside inner class, to make sure we're getting the right `class_bindings`. @@ -92,19 +89,65 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { &object, ctx, ) { - // `_prop._` - ctx.symbols_mut().delete_resolved_reference(class_symbol_id, object_reference_id); - if is_method || is_accessor { - prop_ident + if is_method { + if is_accessor || is_assignment { + if is_assignment { + // `toSetter(_prop.bind(object), [])._` + let call = create_bind_call(prop_ident, object, span, ctx); + self.create_to_setter(call, ctx.ast.vec(), span, ctx) + } else { + // `_prop.call(object)` + let arguments = ctx.ast.vec1(Argument::from(object)); + create_call_call(prop_ident, arguments, span, ctx) + } + } else { + ctx.symbols_mut() + .delete_resolved_reference(class_symbol_id, object_reference_id); + // `_prop` + prop_ident + } } else { + ctx.symbols_mut() + .delete_resolved_reference(class_symbol_id, object_reference_id); + // `_prop._` Self::create_underscore_member_expression(prop_ident, span, ctx) } } else { // `_assertClassBrand(Class, object, _prop)._` let class_binding = class_bindings.get_or_init_static_binding(ctx); let class_ident = class_binding.create_read_expression(ctx); - if is_method || is_accessor { - self.create_assert_class_brand(class_ident, object, prop_ident, span, ctx) + if is_method { + let prop_method = if is_accessor { + if is_assignment { + // `_prop.call(_assertClassBrand(Class, object))` + self.create_setter_with_assert_class_brand( + class_ident, + object, + prop_ident, + None, + SPAN, + ctx, + ) + } else { + // `_prop.bind(_assertClassBrand(Class, object))` + self.create_getter_with_assert_class_brand( + class_ident, + object, + prop_ident, + span, + ctx, + ) + } + } else { + self.create_assert_class_brand(class_ident, object, prop_ident, span, ctx) + }; + + if is_assignment { + // `toSetter(prop_method, [])._` + self.create_to_setter(prop_method, ctx.ast.vec(), span, ctx) + } else { + prop_method + } } else { self.create_assert_class_brand_underscore( class_ident, @@ -115,22 +158,46 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ) } } - } else if is_assignment { - if is_method || is_accessor { - // TODO: Handle assignment to private method or accessor - return None; + } else if is_method { + let brand_ident = class_bindings.brand().create_read_expression(ctx); + let prop_method = if is_accessor { + if is_assignment { + // `_prop.call(_assertClassBrand(_Class_brand, object))` + self.create_setter_with_assert_class_brand( + brand_ident, + object, + prop_ident, + None, + SPAN, + ctx, + ) + } else { + // `_prop.bind(_assertClassBrand(_Class_brand, object))` + self.create_getter_with_assert_class_brand( + brand_ident, + object, + prop_ident, + span, + ctx, + ) + } + } else { + // `_assertClassBrand(_Class_brand, object, _prop)` + self.create_assert_class_brand(brand_ident, object, prop_ident, span, ctx) + }; + if is_assignment { + // `_toSetter(prop_method, [])._` + self.create_to_setter(prop_method, ctx.ast.vec(), span, ctx) + } else { + prop_method } + } else if is_assignment { // `_toSetter(_classPrivateFieldSet2, [_prop, object])._` - self.create_to_setter(prop_ident, object, span, ctx) - } else if is_method || is_accessor { - let brand_ident = class_bindings.brand().create_read_expression(ctx); - self.create_assert_class_brand(brand_ident, object, prop_ident, span, ctx) + self.create_to_setter_for_private_field_set(prop_ident, object, span, ctx) } else { // `_classPrivateFieldGet2(_prop, object)` self.create_private_field_get(prop_ident, object, span, ctx) - }; - - Some(replacement) + } } /// Check if can use shorter version of static private prop transform. @@ -171,6 +238,8 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// Not loose: /// * Instance prop: `object.#prop(arg)` -> `_classPrivateFieldGet2(_prop, object).call(object, arg)` /// * Static prop: `object.#prop(arg)` -> `_assertClassBrand(Class, object, _prop)._.call(object, arg)` + /// * Instance method: `object.#method(arg)` -> `_assertClassBrand(_Class_brand, object, _prop).call(object, arg)` + /// * Static method: `object.#method(arg)` -> `_assertClassBrand(Class, object, _prop).call(object, arg)` /// /// Loose: `object.#prop(arg)` -> `_classPrivateFieldLooseBase(object, _prop)[_prop](arg)` /// @@ -204,7 +273,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { if self.private_fields_as_properties { // `object.#prop(arg)` -> `_classPrivateFieldLooseBase(object, _prop)[_prop](arg)` let ResolvedPrivateProp { prop_binding, is_method, is_accessor, .. } = - self.classes_stack.find_private_prop(&field_expr.field); + self.classes_stack.find_private_prop(&field_expr.field, false); if is_method || is_accessor { return; @@ -281,7 +350,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { is_method, is_accessor, is_declaration, - } = self.classes_stack.find_private_prop(&field_expr.field); + } = self.classes_stack.find_private_prop(&field_expr.field, false); let span = field_expr.span; let prop_ident = prop_binding.create_read_expression(ctx); @@ -324,9 +393,20 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { // Make 2 copies of `object` let (object1, object2) = self.duplicate_object(object, ctx); - let assert_obj = if is_method || is_accessor { - // `_assertClassBrand(Class, object, _prop)._` - self.create_assert_class_brand(class_ident, object1, prop_ident, span, ctx) + let assert_obj = if is_method { + if is_accessor { + // `_prop.call(_assertClassBrand(_Class_brand, object))` + self.create_getter_with_assert_class_brand( + class_ident, + object1, + prop_ident, + span, + ctx, + ) + } else { + // `_assertClassBrand(Class, object, _prop)._` + self.create_assert_class_brand(class_ident, object1, prop_ident, span, ctx) + } } else { // `_assertClassBrand(Class, object, _prop)._` self.create_assert_class_brand_underscore( @@ -345,10 +425,22 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { // Make 2 copies of `object` let (object1, object2) = self.duplicate_object(object, ctx); - let callee = if is_method || is_accessor { + let callee = if is_method { // `(_Class_brand, this)` let brand_ident = self.current_class().bindings.brand().create_read_expression(ctx); - self.create_assert_class_brand(brand_ident, object1, prop_ident, span, ctx) + if is_accessor { + // `_prop.call(_assertClassBrand(_Class_brand, object))` + self.create_getter_with_assert_class_brand( + brand_ident, + object1, + prop_ident, + span, + ctx, + ) + } else { + // `_assertClassBrand(_Class_brand, object, _prop)` + self.create_assert_class_brand(brand_ident, object1, prop_ident, span, ctx) + } } else { // `_classPrivateFieldGet2(_prop, object)` self.create_private_field_get(prop_ident, object1, span, ctx) @@ -400,7 +492,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { is_method, is_accessor, is_declaration, - } = self.classes_stack.find_private_prop(&field_expr.field); + } = self.classes_stack.find_private_prop(&field_expr.field, true); if is_method || is_accessor { return; @@ -789,7 +881,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { is_method, is_accessor, is_declaration, - } = self.classes_stack.find_private_prop(&field_expr.field); + } = self.classes_stack.find_private_prop(&field_expr.field, true); if is_method || is_accessor { return; @@ -1584,7 +1676,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { // ``` // But this is not needed, so we omit it. let ResolvedPrivateProp { prop_binding, is_method, is_accessor, .. } = - self.classes_stack.find_private_prop(&field_expr.field); + self.classes_stack.find_private_prop(&field_expr.field, false); if is_method || is_accessor { return None; @@ -1658,11 +1750,8 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let AssignmentTarget::PrivateFieldExpression(private_field) = target else { unreachable!() }; - if let Some(replacement) = - self.transform_private_field_expression_impl(private_field, true, ctx) - { - *target = AssignmentTarget::from(replacement.into_member_expression()); - } + let replacement = self.transform_private_field_expression_impl(private_field, true, ctx); + *target = AssignmentTarget::from(replacement.into_member_expression()); } /// Duplicate object to be used in get/set pair. @@ -1768,28 +1857,35 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ) } - /// `_toSetter(_classPrivateFieldSet2, [_prop, object])._` + /// `_toSetter(func, func_arguments)._` fn create_to_setter( + &self, + func: Expression<'a>, + func_arguments: ArenaVec<'a, ArrayExpressionElement<'a>>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let func_arguments = ctx.ast.expression_array(span, func_arguments, None); + let arguments = + ctx.ast.vec_from_array([Argument::from(func), Argument::from(func_arguments)]); + let call = self.ctx.helper_call_expr(Helper::ToSetter, span, arguments, ctx); + Self::create_underscore_member_expression(call, span, ctx) + } + + /// `_toSetter(_classPrivateFieldSet2, [_prop, object])._` + fn create_to_setter_for_private_field_set( &self, prop_ident: Expression<'a>, object: Expression<'a>, span: Span, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let arguments = ctx.ast.expression_array( - SPAN, - ctx.ast.vec_from_array([ - ArrayExpressionElement::from(prop_ident), - ArrayExpressionElement::from(object), - ]), - None, - ); let arguments = ctx.ast.vec_from_array([ - Argument::from(self.ctx.helper_load(Helper::ClassPrivateFieldSet2, ctx)), - Argument::from(arguments), + ArrayExpressionElement::from(prop_ident), + ArrayExpressionElement::from(object), ]); - let call = self.ctx.helper_call_expr(Helper::ToSetter, span, arguments, ctx); - Self::create_underscore_member_expression(call, span, ctx) + let helper = self.ctx.helper_load(Helper::ClassPrivateFieldSet2, ctx); + self.create_to_setter(helper, arguments, span, ctx) } /// `_assertClassBrand(Class, object, value)` or `_assertClassBrand(Class, object, _prop)` @@ -1813,6 +1909,56 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ) } + /// `_assertClassBrand(Class, object)` + fn create_assert_class_without_value( + &self, + class_ident: Expression<'a>, + object: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let arguments = + ctx.ast.vec_from_array([Argument::from(class_ident), Argument::from(object)]); + self.ctx.helper_call_expr(Helper::AssertClassBrand, SPAN, arguments, ctx) + } + + /// `_prop.call(_assertClassBrand(_Class, object))` + fn create_getter_with_assert_class_brand( + &mut self, + class_ident: Expression<'a>, + object: Expression<'a>, + prop: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let context = self.create_assert_class_without_value(class_ident, object, ctx); + let arguments = ctx.ast.vec1(Argument::from(context)); + create_call_call(prop, arguments, span, ctx) + } + + /// * Value: + /// `_prop.bind(_assertClassBrand(_Class, object))` + /// + /// * Without value: + /// `_prop.call(_assertClassBrand(_Class, object), value)` + fn create_setter_with_assert_class_brand( + &mut self, + class_ident: Expression<'a>, + object: Expression<'a>, + prop: Expression<'a>, + value: Option>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let context = self.create_assert_class_without_value(class_ident, object, ctx); + if let Some(value) = value { + let arguments = + ctx.ast.vec_from_array([Argument::from(context), Argument::from(value)]); + create_call_call(prop, arguments, span, ctx) + } else { + create_bind_call(prop, context, span, ctx) + } + } + /// `_assertClassBrand(Class, object, _prop)._` fn create_assert_class_brand_underscore( &self, diff --git a/crates/oxc_transformer/src/es2022/class_properties/private_method.rs b/crates/oxc_transformer/src/es2022/class_properties/private_method.rs index 3f7d78395fd64..2513af2055a8a 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/private_method.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/private_method.rs @@ -40,13 +40,15 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { method: &mut MethodDefinition<'a>, ctx: &mut TraverseCtx<'a>, ) -> bool { - let PropertyKey::PrivateIdentifier(ident) = &method.key else { + let MethodDefinition { key, value, span, kind, r#static, .. } = method; + let PropertyKey::PrivateIdentifier(ident) = &key else { return false; }; - let mut function = ctx.ast.move_function(&mut method.value); + let mut function = ctx.ast.move_function(value); + let temp_binding = self.classes_stack.find_private_prop(ident, kind.is_set()).prop_binding; - let temp_binding = self.classes_stack.find_private_prop(ident).prop_binding; + function.span = *span; function.id = Some(temp_binding.create_binding_identifier(ctx)); function.r#type = FunctionType::FunctionDeclaration; @@ -55,11 +57,16 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let scope_id = function.scope_id(); let new_parent_id = ctx.current_scope_id(); ctx.scopes_mut().change_parent_id(scope_id, Some(new_parent_id)); - if !ctx.current_scope_flags().is_strict_mode() { - *ctx.scopes_mut().get_flags_mut(scope_id) -= ScopeFlags::StrictMode; + let is_strict_mode = ctx.current_scope_flags().is_strict_mode(); + let flags = ctx.scopes_mut().get_flags_mut(scope_id); + *flags -= ScopeFlags::GetAccessor | ScopeFlags::SetAccessor; + if !is_strict_mode { + // TODO: Needs to remove all child scopes' strict mode flag if child scope + // is inherited from this scope. + *flags -= ScopeFlags::StrictMode; } - PrivateMethodVisitor::new(method.r#static, self, ctx) + PrivateMethodVisitor::new(*r#static, self, ctx) .visit_function(&mut function, ScopeFlags::Function); let function = ctx.ast.alloc(function); diff --git a/crates/oxc_transformer/src/es2022/class_properties/utils.rs b/crates/oxc_transformer/src/es2022/class_properties/utils.rs index 55f2c0188c302..21e862187a682 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/utils.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/utils.rs @@ -3,7 +3,8 @@ use std::path::PathBuf; -use oxc_ast::ast::*; +use oxc_allocator::Vec as Arena; +use oxc_ast::{ast::*, NONE}; use oxc_span::SPAN; use oxc_syntax::reference::ReferenceFlags; use oxc_traverse::{BoundIdentifier, TraverseCtx}; @@ -81,3 +82,28 @@ pub(super) fn create_prototype_member<'a>( let static_member = ctx.ast.member_expression_static(SPAN, object, property, false); Expression::from(static_member) } + +/// `object` -> `object.bind(this)`. +pub(super) fn create_bind_call<'a>( + callee: Expression<'a>, + this: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, +) -> Expression<'a> { + let property = ctx.ast.identifier_name(SPAN, Atom::from("bind")); + let callee = Expression::from(ctx.ast.member_expression_static(SPAN, callee, property, false)); + let arguments = ctx.ast.vec1(Argument::from(this)); + ctx.ast.expression_call(span, callee, NONE, arguments, false) +} + +/// `object` -> `object.call(...arguments)`. +pub(super) fn create_call_call<'a>( + callee: Expression<'a>, + arguments: Arena<'a, Argument<'a>>, + span: Span, + ctx: &mut TraverseCtx<'a>, +) -> Expression<'a> { + let property = ctx.ast.identifier_name(SPAN, Atom::from("call")); + let callee = Expression::from(ctx.ast.member_expression_static(SPAN, callee, property, false)); + ctx.ast.expression_call(span, callee, NONE, arguments, false) +} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/class-binding/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/class-binding/output.js new file mode 100644 index 0000000000000..b60fc1cc02778 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/class-binding/output.js @@ -0,0 +1,11 @@ +var _A; +var _A_brand = new WeakSet(); +class A { + constructor() { + babelHelpers.classPrivateMethodInitSpec(this, _A_brand); + } +} +_A = A; +function _get_getA() { + return _A; +} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/preserve-comments/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/preserve-comments/output.js new file mode 100644 index 0000000000000..51d2d41eb0ff8 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/preserve-comments/output.js @@ -0,0 +1,10 @@ +var _C_brand = new WeakSet(); +class C { + constructor() { + babelHelpers.classPrivateMethodInitSpec(this, _C_brand); + } +} +function _get_a() { + return 42; +} +function _set_a(v) {} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/tagged-template/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/tagged-template/output.js new file mode 100644 index 0000000000000..cc0dc3d4def49 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/accessors/tagged-template/output.js @@ -0,0 +1,11 @@ +var _Foo_brand = new WeakSet(); +class Foo { + constructor() { + babelHelpers.classPrivateMethodInitSpec(this, _Foo_brand); + _get_tag.call(babelHelpers.assertClassBrand(_Foo_brand, this)).bind(this)``; + } +} +function _get_tag() { + return () => this; +} +new Foo(); diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/destructure-set/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/destructure-set/output.js new file mode 100644 index 0000000000000..d2fe4f94116c1 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/destructure-set/output.js @@ -0,0 +1,10 @@ +class C { + constructor() { + [babelHelpers.toSetter(_set_p.bind(C), [])._] = [0]; + } +} +function _set_p(v) { + _q._ = v; +} +var _q = { _: void 0 }; +new C(); diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/preserve-comments/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/preserve-comments/output.js new file mode 100644 index 0000000000000..548f31dc0012a --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/preserve-comments/output.js @@ -0,0 +1,5 @@ +class C {} +function _get_a() { + return 42; +} +function _set_a(v) {} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/tagged-template/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/tagged-template/output.js new file mode 100644 index 0000000000000..ef09b7d2a8ef1 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-private-methods/test/fixtures/static-accessors/tagged-template/output.js @@ -0,0 +1,11 @@ +class Foo { + static test() { + const receiver = _get_tag.call(babelHelpers.assertClassBrand(Foo, this)).bind(this)``; + expect(receiver).toBe(this); + } +} +function _get_tag() { + return function() { + return this; + }; +} diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index 36e768c979910..c3c1c2f3b807c 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 641/1095 +Passed: 645/1095 # All Passed: * babel-plugin-transform-logical-assignment-operators @@ -462,25 +462,19 @@ x Output mismatch x Output mismatch -# babel-plugin-transform-private-methods (26/148) +# babel-plugin-transform-private-methods (30/148) * accessors/arguments/input.js x Output mismatch * accessors/basic/input.js x Output mismatch -* accessors/class-binding/input.js -x Output mismatch - * accessors/destructuring/input.js x Output mismatch * accessors/get-only-setter/input.js x Output mismatch -* accessors/preserve-comments/input.js -x Output mismatch - * accessors/reassignment/input.js x Output mismatch @@ -488,7 +482,9 @@ x Output mismatch x Output mismatch * accessors/tagged-template/input.js -x Output mismatch +Scope flags mismatch: +after transform: ScopeId(3): ScopeFlags(StrictMode | Function | Arrow) +rebuilt : ScopeId(4): ScopeFlags(Function | Arrow) * accessors/updates/input.js x Output mismatch @@ -789,20 +785,16 @@ x Output mismatch * static-accessors/basic/input.js x Output mismatch -* static-accessors/destructure-set/input.js -x Output mismatch - * static-accessors/get-only-setter/input.js x Output mismatch -* static-accessors/preserve-comments/input.js -x Output mismatch - * static-accessors/set-only-getter/input.js x Output mismatch * static-accessors/tagged-template/input.js -x Output mismatch +Scope flags mismatch: +after transform: ScopeId(3): ScopeFlags(StrictMode | Function) +rebuilt : ScopeId(4): ScopeFlags(Function) * static-accessors/updates/input.js x Output mismatch diff --git a/tasks/transform_conformance/snapshots/babel_exec.snap.md b/tasks/transform_conformance/snapshots/babel_exec.snap.md index 4f5b236567f39..00bcb2309f997 100644 --- a/tasks/transform_conformance/snapshots/babel_exec.snap.md +++ b/tasks/transform_conformance/snapshots/babel_exec.snap.md @@ -2,7 +2,7 @@ commit: 54a8389f node: v22.12.0 -Passed: 251 of 362 (69.34%) +Passed: 270 of 362 (74.59%) Failures: @@ -117,24 +117,11 @@ TypeError: Cannot read properties of undefined (reading 'x') at Foo.test (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js:25:63) at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js:68:12 -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-arguments-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-basic-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-class-binding-exec.test.js -AssertionError: expected [Function _get_getA] to be [Function A] // Object.is equality - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-class-binding-exec.test.js:22:26 - ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-get-only-setter-exec.test.js AssertionError: expected function to throw an error, but it didn't - at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-get-only-setter-exec.test.js:14:77) + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-get-only-setter-exec.test.js:14:82) at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-get-only-setter-exec.test.js:20:13 -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-helper-exec.test.js -Private field '#foo' must be declared in an enclosing class - ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-loose-basic-exec.test.js ReferenceError: _Cl_brand is not defined at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-loose-basic-exec.test.js:10:38) @@ -195,35 +182,22 @@ ReferenceError: _Cl_brand is not defined ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsProperties-updates-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-basic-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-class-binding-exec.test.js -AssertionError: expected [Function _get_getA] to be [Function A] // Object.is equality - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-class-binding-exec.test.js:22:26 - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js -AssertionError: expected [Function _set_privateFieldValue] to be undefined - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1236:20) - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17) - at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25) - at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js:14:71) - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js:20:13 - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-helper-exec.test.js -Private field '#foo' must be declared in an enclosing class - ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-set-only-getter-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-set-only-getter-exec.test.js:15:103) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-set-only-getter-exec.test.js:22:13 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-privateFieldsAsSymbols-updates-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-reassignment-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-reassignment-exec.test.js:22:26 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-set-only-getter-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-set-only-getter-exec.test.js:21:108) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-set-only-getter-exec.test.js:30:13 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-updates-bigint-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class @@ -231,9 +205,6 @@ Private field '#privateFieldValue' must be declared in an enclosing class ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-updates-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-context-exec.test.js -Private field '#getStatus' must be declared in an enclosing class - ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-loose-assignment-exec.test.js ReferenceError: _Foo_brand is not defined at new Foo (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-loose-assignment-exec.test.js:8:38) @@ -308,14 +279,14 @@ ReferenceError: _Sub_brand is not defined at new Sub (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsProperties-super-exec.test.js:16:38) at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsProperties-super-exec.test.js:29:9 -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-context-exec.test.js -Private field '#getStatus' must be declared in an enclosing class - ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-read-only-exec.test.js -Private field '#method' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at new A (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-read-only-exec.test.js:17:89) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-read-only-exec.test.js:23:2 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-reassignment-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-reassignment-exec.test.js:22:26 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-loose-basic-exec.test.js TypeError: attempted to use private field on non-instance @@ -402,29 +373,14 @@ TypeError: attempted to use private field on non-instance at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsProperties-this-exec.test.js:27:25 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-reassignment-exec.test.js -Private field '#privateStaticMethod' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-read-only-exec.test.js -Private field '#method' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-reassignment-exec.test.js:7:72) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-reassignment-exec.test.js:11:2 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-reassignment-exec.test.js -Private field '#privateStaticMethod' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-access-in-static-field-initializer-exec.test.js -Private field '#p' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-basic-exec.test.js -Private field '#privateStaticFieldValue' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-destructure-set-exec.test.js -Private field '#p' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-get-only-setter-exec.test.js -AssertionError: expected [Function _set_privateStaticFieldValue] to be undefined - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1236:20) - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17) - at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25) - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-get-only-setter-exec.test.js:13:42 +AssertionError: expected function to throw an error, but it didn't + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-reassignment-exec.test.js:7:72) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-reassignment-exec.test.js:11:2 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-loose-access-in-static-field-initializer-exec.test.js TypeError: attempted to use private field on non-instance @@ -482,30 +438,18 @@ TypeError: attempted to use private field on non-instance ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsProperties-updates-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-access-in-static-field-initializer-exec.test.js -Private field '#p' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-basic-exec.test.js -Private field '#privateStaticFieldValue' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-destructure-set-exec.test.js -Private field '#p' must be declared in an enclosing class - -./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js -AssertionError: expected [Function _set_privateStaticFieldValue] to be undefined - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1236:20) - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17) - at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25) - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js:13:42 - ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-set-only-getter-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-set-only-getter-exec.test.js:9:71) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-set-only-getter-exec.test.js:18:13 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-updates-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-set-only-getter-exec.test.js -Private field '#privateFieldValue' must be declared in an enclosing class +AssertionError: expected function to throw an error, but it didn't + at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-set-only-getter-exec.test.js:9:71) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-set-only-getter-exec.test.js:18:13 ./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-updates-exec.test.js Private field '#privateFieldValue' must be declared in an enclosing class