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

This commit is contained in:
Dunqing 2024-12-13 13:47:38 +00:00
parent c039a5ae4f
commit 6bc530d2e2
6 changed files with 88 additions and 16 deletions

View file

@ -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(_)))

View file

@ -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

View file

@ -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;
@ -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`.
@ -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)
}
}

View file

@ -1,6 +1,6 @@
commit: 54a8389f
Passed: 108/122
Passed: 109/123
# All Passed:
* babel-plugin-transform-class-static-block
@ -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)]

View file

@ -0,0 +1,5 @@
class C {
static foo = super.method();
static bar = super.method(1);
static bar = super.method(1, 2, 3);
}

View file

@ -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]));