diff --git a/crates/oxc_transformer/src/common/helper_loader.rs b/crates/oxc_transformer/src/common/helper_loader.rs index 77d4ffc8a22811..a91ab7029005a0 100644 --- a/crates/oxc_transformer/src/common/helper_loader.rs +++ b/crates/oxc_transformer/src/common/helper_loader.rs @@ -145,6 +145,7 @@ pub enum Helper { WrapAsyncGenerator, DefineProperty, ClassPrivateFieldInitSpec, + ClassPrivateFieldGet, } impl Helper { @@ -158,6 +159,7 @@ impl Helper { Self::WrapAsyncGenerator => "wrapAsyncGenerator", Self::DefineProperty => "defineProperty", Self::ClassPrivateFieldInitSpec => "classPrivateFieldInitSpec", + Self::ClassPrivateFieldGet => "classPrivateFieldGet", } } } diff --git a/crates/oxc_transformer/src/es2022/class_properties.rs b/crates/oxc_transformer/src/es2022/class_properties.rs index 82a793de87539c..700c7cbb5c8753 100644 --- a/crates/oxc_transformer/src/es2022/class_properties.rs +++ b/crates/oxc_transformer/src/es2022/class_properties.rs @@ -73,7 +73,7 @@ use rustc_hash::FxHashMap; use serde::Deserialize; -use oxc_allocator::{Address, GetAddress}; +use oxc_allocator::{Address, Box as ArenaBox, GetAddress}; use oxc_ast::{ast::*, NONE}; use oxc_data_structures::stack::SparseStack; use oxc_span::SPAN; @@ -169,10 +169,18 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { } impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> { + // ---------- Class transform ---------- + fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - if let Expression::ClassExpression(class) = expr { - self.transform_class_expression_start(class, ctx); - self.transform_class_expression_finish(expr, ctx); + match expr { + Expression::ClassExpression(class) => { + self.transform_class_expression_start(class, ctx); + self.transform_class_expression_finish(expr, ctx); + } + Expression::PrivateFieldExpression(_) => { + self.transform_private_field_expression(expr, ctx); + } + _ => {} } } @@ -922,6 +930,58 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { class.body.body.insert(0, ctor); } + // ---------- Private properties transform ---------- + + /// Transform private field expression. + /// + /// `this.#x` -> `_classPrivateFieldGet(_x, this)`. + // `#[inline]` so that compiler sees that `expr` is an `Expression::PrivateFieldExpression`. + #[inline] + fn transform_private_field_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let owned_expr = ctx.ast.move_expression(expr); + let Expression::PrivateFieldExpression(field_expr) = owned_expr else { unreachable!() }; + *expr = self.transform_private_field_expression_impl(field_expr, ctx); + } + + fn transform_private_field_expression_impl( + &self, + field_expr: ArenaBox<'a, PrivateFieldExpression<'a>>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let field_expr = field_expr.unbox(); + + let prop = self.lookup_private_property(&field_expr.field); + if prop.is_static && false { + // TODO + todo!(); + } else { + let arguments = ctx.ast.vec_from_iter([ + Argument::from(prop.binding.create_read_expression(ctx)), + Argument::from(field_expr.object), + ]); + self.ctx.helper_call_expr(Helper::ClassPrivateFieldGet, field_expr.span, arguments, ctx) + } + } + + fn lookup_private_property(&self, ident: &PrivateIdentifier<'a>) -> &PrivateProp<'a> { + // Check for binding in closest class first, then enclosing classes + // TODO: Check there are tests for bindings in enclosing classes. + for props in self.private_props_stack.as_slice().iter().rev() { + if let Some(prop) = props.get(&ident.name) { + return prop; + } + } + // It's a syntax error for a private property to be used if it's not defined in the class + // TODO: Record an error instead + unreachable!(); + } + + // ---------- Utility methods ---------- + /// `super(...args);` fn create_super_call_stmt( binding: &BoundIdentifier<'a>,