Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(transformer/class-properties): transform super call expression that is inside static prop initializer #7831

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]));
Loading