Skip to content

Commit

Permalink
feat(transformer/class-properties): transform super call expression t…
Browse files Browse the repository at this point in the history
…hat is inside static prop initializer (#7831)
  • Loading branch information
Dunqing committed Dec 13, 2024
1 parent c039a5a commit 6bc530d
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 16 deletions.
5 changes: 5 additions & 0 deletions crates/oxc_ast/src/ast_impl/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ impl<'a> Expression<'a> {
matches!(self, Expression::CallExpression(_))
}

#[allow(missing_docs)]
pub fn is_super(&self) -> bool {
matches!(self, Expression::Super(_))
}

#[allow(missing_docs)]
pub fn is_super_call_expression(&self) -> bool {
matches!(self, Expression::CallExpression(expr) if matches!(&expr.callee, Expression::Super(_)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> {
}
// `object.#prop()`
Expression::CallExpression(_) => {
self.class_properties.transform_super_call_expression(expr, self.ctx);
self.class_properties.transform_call_expression(expr, self.ctx);
}
// `object.#prop = value`, `object.#prop += value`, `object.#prop ??= value` etc
Expand Down
83 changes: 69 additions & 14 deletions crates/oxc_transformer/src/es2022/class_properties/supers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! ES2022: Class Properties
//! Transform of `super` expressions.
use oxc_allocator::Vec as ArenaVec;
use oxc_ast::ast::*;
use oxc_span::SPAN;
use oxc_traverse::TraverseCtx;

use crate::Helper;
Expand All @@ -21,20 +23,21 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>,
) {
let Expression::StaticMemberExpression(member) = expr else { unreachable!() };
if matches!(member.object, Expression::Super(_)) {
*expr = self.transform_static_member_expression_impl(member, ctx);
if member.object.is_super() {
*expr = self.transform_static_member_expression_impl(member, false, ctx);
}
}

fn transform_static_member_expression_impl(
&mut self,
member: &mut StaticMemberExpression<'a>,
is_callee: bool,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let property = &member.property;
let property =
ctx.ast.expression_string_literal(property.span, property.name.clone(), None);
self.create_super_prop_get(member.span, property, ctx)
self.create_super_prop_get(member.span, property, is_callee, ctx)
}

/// Transform computed member expression where object is `super`.
Expand All @@ -49,35 +52,87 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>,
) {
let Expression::ComputedMemberExpression(member) = expr else { unreachable!() };
if matches!(member.object, Expression::Super(_)) {
*expr = self.transform_computed_member_expression_impl(member, ctx);
if member.object.is_super() {
*expr = self.transform_computed_member_expression_impl(member, false, ctx);
}
}

fn transform_computed_member_expression_impl(
&mut self,
member: &mut ComputedMemberExpression<'a>,
is_callee: bool,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let property = ctx.ast.move_expression(&mut member.expression);
self.create_super_prop_get(member.span, property, ctx)
self.create_super_prop_get(member.span, property, is_callee, ctx)
}

/// `_superPropGet(_Class, prop, _Class)`
/// Transform call expression where callee contains `super`.
///
/// `super.method()` -> `_superPropGet(_Class, "method", _Class, 2)([])`
/// `super.method(1)` -> `_superPropGet(_Class, "method", _Class, 2)([1])`
//
// `#[inline]` so that compiler sees that `expr` is an `Expression::CallExpression`.
#[inline]
pub(super) fn transform_super_call_expression(
&mut self,
expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
let Expression::CallExpression(call) = expr else { unreachable!() };
let callee = &mut call.callee;
match callee {
Expression::StaticMemberExpression(member) if member.object.is_super() => {
*callee = self.transform_static_member_expression_impl(member, true, ctx);
}
Expression::ComputedMemberExpression(member) if member.object.is_super() => {
*callee = self.transform_computed_member_expression_impl(member, true, ctx);
}
_ => return,
};
Self::transform_super_call_expression_arguments(&mut call.arguments, ctx);
}

/// [A, B, C] -> [[A, B, C]]
pub(super) fn transform_super_call_expression_arguments(
arguments: &mut ArenaVec<'a, Argument<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
let owned_arguments = ctx.ast.move_vec(arguments);
let elements =
ctx.ast.vec_from_iter(owned_arguments.into_iter().map(ArrayExpressionElement::from));
let array = ctx.ast.expression_array(SPAN, elements, None);
arguments.push(Argument::from(array));
}

/// Member:
/// `_superPropGet(_Class, prop, _Class)`
///
/// Callee:
/// `_superPropGet(_Class, prop, _Class, 2)`
fn create_super_prop_get(
&mut self,
span: Span,
property: Expression<'a>,
is_callee: bool,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let class_binding = self.get_temp_binding(ctx);
// `(_Class, prop, _Class)`
let arguments = ctx.ast.vec_from_array([
Argument::from(class_binding.create_read_expression(ctx)),
Argument::from(property),
Argument::from(class_binding.create_read_expression(ctx)),
]);
// `_superPropGet(_Class, prop, _Class)`

let ident1 = Argument::from(class_binding.create_read_expression(ctx));
let ident2 = Argument::from(class_binding.create_read_expression(ctx));
let property = Argument::from(property);

let arguments = if is_callee {
// `(_Class, prop, _Class, 2)`
let two = ctx.ast.expression_numeric_literal(SPAN, 2.0, None, NumberBase::Decimal);
ctx.ast.vec_from_array([ident1, property, ident2, Argument::from(two)])
} else {
// `(_Class, prop, _Class)`
ctx.ast.vec_from_array([ident1, property, ident2])
};

// `_superPropGet(_Class, prop, _Class)` or `_superPropGet(_Class, prop, _Class, 2)`
self.ctx.helper_call_expr(Helper::SuperPropGet, span, arguments, ctx)
}
}
4 changes: 2 additions & 2 deletions tasks/transform_conformance/snapshots/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: 54a8389f

Passed: 108/122
Passed: 109/123

# All Passed:
* babel-plugin-transform-class-static-block
Expand All @@ -16,7 +16,7 @@ Passed: 108/122
* regexp


# babel-plugin-transform-class-properties (9/11)
# babel-plugin-transform-class-properties (10/12)
* typescript/optional-call/input.ts
Symbol reference IDs mismatch for "X":
after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(11), ReferenceId(16)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class C {
static foo = super.method();
static bar = super.method(1);
static bar = super.method(1, 2, 3);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var _C;
class C {}
_C = C;
babelHelpers.defineProperty(C, "foo", babelHelpers.superPropGet(_C, "method", _C, 2)([]));
babelHelpers.defineProperty(C, "bar", babelHelpers.superPropGet(_C, "method", _C, 2)([1]));
babelHelpers.defineProperty(C, "bar", babelHelpers.superPropGet(_C, "method", _C, 2)([1, 2, 3]));

0 comments on commit 6bc530d

Please sign in to comment.