mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer/class-properties): transform super update expressions within static prop initializer (#7997)
This commit is contained in:
parent
5a23d72b37
commit
3ea4109d14
5 changed files with 383 additions and 6 deletions
|
|
@ -207,7 +207,12 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> {
|
|||
}
|
||||
// `object.#prop++`, `--object.#prop`
|
||||
Expression::UpdateExpression(_) => {
|
||||
self.class_properties.transform_update_expression(expr, self.ctx);
|
||||
self.transform_update_expression_if_super_member_assignment_target(expr);
|
||||
// Check again if it's an update expression, because it could have been transformed
|
||||
// to other expression.
|
||||
if matches!(expr, Expression::UpdateExpression(_)) {
|
||||
self.class_properties.transform_update_expression(expr, self.ctx);
|
||||
}
|
||||
}
|
||||
// `object?.#prop`
|
||||
Expression::ChainExpression(_) => {
|
||||
|
|
@ -540,4 +545,17 @@ impl<'a, 'ctx, 'v> StaticInitializerVisitor<'a, 'ctx, 'v> {
|
|||
.transform_assignment_expression_for_super_assignment_target(expr, self.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// `#[inline]` into visitor to keep common path where assignment expression isn't
|
||||
// `super.prop++` fast
|
||||
#[inline]
|
||||
fn transform_update_expression_if_super_member_assignment_target(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
) {
|
||||
if self.this_depth == 0 {
|
||||
self.class_properties
|
||||
.transform_update_expression_for_super_assignment_target(expr, self.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
//! ES2022: Class Properties
|
||||
//! Transform of `super` expressions.
|
||||
|
||||
use oxc_allocator::Vec as ArenaVec;
|
||||
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::SPAN;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
use oxc_traverse::{BoundIdentifier, TraverseCtx};
|
||||
|
||||
use crate::Helper;
|
||||
|
||||
use super::ClassProperties;
|
||||
use super::{utils::create_assignment, ClassProperties};
|
||||
|
||||
impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||
/// Transform static member expression where object is `super`.
|
||||
|
|
@ -271,6 +271,244 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Transform update expression where the argument is a member expression with `super`.
|
||||
///
|
||||
/// * `++super.prop` or `super.prop--`
|
||||
/// See [`Self::transform_update_expression_for_super_static_member_expr`]
|
||||
///
|
||||
/// * `++super[prop]` or `super[prop]--`
|
||||
/// See [`Self::transform_update_expression_for_super_computed_member_expr`]
|
||||
//
|
||||
// `#[inline]` so can bail out fast without a function call if `argument` is not a member expression
|
||||
// with `super` as member expression object (fairly rare).
|
||||
// Actual transform is broken out into separate functions.
|
||||
pub(super) fn transform_update_expression_for_super_assignment_target(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let Expression::UpdateExpression(update_expr) = expr else { unreachable!() };
|
||||
|
||||
match &update_expr.argument {
|
||||
SimpleAssignmentTarget::StaticMemberExpression(member) if member.object.is_super() => {
|
||||
self.transform_update_expression_for_super_static_member_expr(expr, ctx);
|
||||
}
|
||||
SimpleAssignmentTarget::ComputedMemberExpression(member)
|
||||
if member.object.is_super() =>
|
||||
{
|
||||
self.transform_update_expression_for_super_computed_member_expr(expr, ctx);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
/// Transform update expression (`++` or `--`) where argument is a static member expression
|
||||
/// with `super`.
|
||||
///
|
||||
/// * `++super.prop` ->
|
||||
/// ```js
|
||||
/// _superPropSet(
|
||||
/// _Outer,
|
||||
/// "prop",
|
||||
/// (
|
||||
/// _super$prop = _superPropGet(_Outer, "prop", _Outer),
|
||||
/// ++_super$prop
|
||||
/// ),
|
||||
/// _Outer,
|
||||
/// 1
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// * `super.prop--` ->
|
||||
/// ```js
|
||||
/// (
|
||||
/// _superPropSet(
|
||||
/// _Outer,
|
||||
/// "prop",
|
||||
/// (
|
||||
/// _super$prop = _superPropGet(_Outer, "prop", _Outer),
|
||||
/// _super$prop2 = _super$prop--,
|
||||
/// _super$prop
|
||||
/// ),
|
||||
/// _Outer,
|
||||
/// 1
|
||||
/// ),
|
||||
/// _super$prop2
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
// `#[inline]` so that compiler sees that `expr` is an `Expression::UpdateExpression`.
|
||||
#[inline]
|
||||
fn transform_update_expression_for_super_static_member_expr(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let Expression::UpdateExpression(mut update_expr) = ctx.ast.move_expression(expr) else {
|
||||
unreachable!()
|
||||
};
|
||||
let SimpleAssignmentTarget::StaticMemberExpression(member) = &mut update_expr.argument
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let temp_binding =
|
||||
self.ctx.var_declarations.create_uid_var_based_on_node(member.as_ref(), ctx);
|
||||
let property = ctx.ast.expression_string_literal(
|
||||
member.property.span,
|
||||
member.property.name.clone(),
|
||||
None,
|
||||
);
|
||||
*expr =
|
||||
self.transform_super_update_expression_impl(&temp_binding, update_expr, property, ctx);
|
||||
}
|
||||
|
||||
/// Transform update expression (`++` or `--`) where argument is a computed member expression
|
||||
/// with `super`.
|
||||
///
|
||||
/// * `++super[prop]` ->
|
||||
/// ```js
|
||||
/// _superPropSet(
|
||||
/// _Outer,
|
||||
/// prop,
|
||||
/// (
|
||||
/// _super$prop = _superPropGet(_Outer, prop, _Outer),
|
||||
/// ++_super$prop
|
||||
/// ),
|
||||
/// _Outer,
|
||||
/// 1
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// * `super[prop]--` ->
|
||||
/// ```js
|
||||
/// (
|
||||
/// _superPropSet(
|
||||
/// _Outer,
|
||||
/// prop,
|
||||
/// (
|
||||
/// _super$prop = _superPropGet(_Outer, prop, _Outer),
|
||||
/// _super$prop2 = _super$prop--,
|
||||
/// _super$prop
|
||||
/// ),
|
||||
/// _Outer,
|
||||
/// 1
|
||||
/// ),
|
||||
/// _super$prop2
|
||||
/// )
|
||||
/// ```
|
||||
//
|
||||
// `#[inline]` so that compiler sees that `expr` is an `Expression::UpdateExpression`.
|
||||
#[inline]
|
||||
fn transform_update_expression_for_super_computed_member_expr(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let Expression::UpdateExpression(mut update_expr) = ctx.ast.move_expression(expr) else {
|
||||
unreachable!()
|
||||
};
|
||||
let SimpleAssignmentTarget::ComputedMemberExpression(member) = &mut update_expr.argument
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let temp_binding =
|
||||
self.ctx.var_declarations.create_uid_var_based_on_node(member.as_ref(), ctx);
|
||||
let property = ctx.ast.move_expression(&mut member.expression);
|
||||
*expr =
|
||||
self.transform_super_update_expression_impl(&temp_binding, update_expr, property, ctx);
|
||||
}
|
||||
|
||||
/// Transform update expression (`++` or `--`) where argument is a member expression with `super`.
|
||||
///
|
||||
/// * `++super[prop]` ->
|
||||
/// ```js
|
||||
/// _superPropSet(
|
||||
/// _Outer,
|
||||
/// prop,
|
||||
/// (
|
||||
/// _super$prop = _superPropGet(_Outer, prop, _Outer),
|
||||
/// ++_super$prop
|
||||
/// ),
|
||||
/// _Outer,
|
||||
/// 1
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// * `super[prop]--` ->
|
||||
/// ```js
|
||||
/// (
|
||||
/// _superPropSet(
|
||||
/// _Outer,
|
||||
/// prop,
|
||||
/// (
|
||||
/// _super$prop = _superPropGet(_Outer, prop, _Outer),
|
||||
/// _super$prop2 = _super$prop--,
|
||||
/// _super$prop
|
||||
/// ),
|
||||
/// _Outer,
|
||||
/// 1
|
||||
/// ),
|
||||
/// _super$prop2
|
||||
/// )
|
||||
/// ```
|
||||
fn transform_super_update_expression_impl(
|
||||
&mut self,
|
||||
temp_binding: &BoundIdentifier<'a>,
|
||||
mut update_expr: ArenaBox<'a, UpdateExpression<'a>>,
|
||||
property: Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
// Make 2 copies of `property`
|
||||
let (property1, property2) = self.duplicate_object(property, ctx);
|
||||
|
||||
// `_superPropGet(_Class, prop, _Class)`
|
||||
let get_call = self.create_super_prop_get(SPAN, property2, false, ctx);
|
||||
|
||||
// `_super$prop = _superPropGet(_Class, prop, _Class)`
|
||||
let assignment = create_assignment(temp_binding, get_call, ctx);
|
||||
|
||||
// `++_super$prop` / `_super$prop++` (reusing existing `UpdateExpression`)
|
||||
let span = update_expr.span;
|
||||
let prefix = update_expr.prefix;
|
||||
update_expr.span = SPAN;
|
||||
update_expr.argument = temp_binding.create_read_write_simple_target(ctx);
|
||||
let update_expr = Expression::UpdateExpression(update_expr);
|
||||
|
||||
if prefix {
|
||||
// Source = `++super$prop` (prefix `++`)
|
||||
// `(_super$prop = _superPropGet(_Class, prop, _Class), ++_super$prop)`
|
||||
let value = ctx
|
||||
.ast
|
||||
.expression_sequence(SPAN, ctx.ast.vec_from_array([assignment, update_expr]));
|
||||
// `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||
self.create_super_prop_set(span, property1, value, ctx)
|
||||
} else {
|
||||
// Source = `super.prop++` (postfix `++`)
|
||||
// `_super$prop2 = _super$prop++`
|
||||
let temp_binding2 = self.ctx.var_declarations.create_uid_var(&temp_binding.name, ctx);
|
||||
let assignment2 = create_assignment(&temp_binding2, update_expr, ctx);
|
||||
|
||||
// `(_super$prop = _superPropGet(_Class, prop, _Class), _super$prop2 = _super$prop++, _super$prop)`
|
||||
let value = ctx.ast.expression_sequence(
|
||||
SPAN,
|
||||
ctx.ast.vec_from_array([
|
||||
assignment,
|
||||
assignment2,
|
||||
temp_binding.create_read_expression(ctx),
|
||||
]),
|
||||
);
|
||||
|
||||
// `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||
let set_call = self.create_super_prop_set(span, property1, value, ctx);
|
||||
// `(_superPropSet(_Class, prop, value, _Class, 1), _super$prop2)`
|
||||
ctx.ast.expression_sequence(
|
||||
span,
|
||||
ctx.ast.vec_from_array([set_call, temp_binding2.create_read_expression(ctx)]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Member:
|
||||
/// `_superPropGet(_Class, prop, _Class)`
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
commit: 54a8389f
|
||||
|
||||
Passed: 113/128
|
||||
Passed: 114/129
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-class-static-block
|
||||
|
|
@ -16,7 +16,7 @@ Passed: 113/128
|
|||
* regexp
|
||||
|
||||
|
||||
# babel-plugin-transform-class-properties (14/17)
|
||||
# babel-plugin-transform-class-properties (15/18)
|
||||
* static-super-tagged-template/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
let bound = "A";
|
||||
|
||||
class Outer {
|
||||
static B = () => {
|
||||
// Transform
|
||||
super.A++;
|
||||
super.A--;
|
||||
++super.A;
|
||||
--super.A;
|
||||
|
||||
super[bound]++;
|
||||
super[bound]--;
|
||||
++super[bound];
|
||||
--super[bound];
|
||||
|
||||
super[unbound]++;
|
||||
super[unbound]--;
|
||||
++super[unbound];
|
||||
--super[unbound];
|
||||
|
||||
class Inner {
|
||||
method() {
|
||||
// Don't transform
|
||||
super.A++;
|
||||
super.A--;
|
||||
++super.A;
|
||||
--super.A;
|
||||
|
||||
super[bound]++;
|
||||
super[bound]--;
|
||||
++super[bound];
|
||||
--super[bound];
|
||||
|
||||
super[unbound]++;
|
||||
super[unbound]--;
|
||||
++super[unbound];
|
||||
--super[unbound];
|
||||
}
|
||||
|
||||
static staticMethod() {
|
||||
// Don't transform
|
||||
super.A++;
|
||||
super.A--;
|
||||
++super.A;
|
||||
--super.A;
|
||||
|
||||
super[bound]++;
|
||||
super[bound]--;
|
||||
++super[bound];
|
||||
--super[bound];
|
||||
|
||||
super[unbound]++;
|
||||
super[unbound]--;
|
||||
++super[unbound];
|
||||
--super[unbound];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
var _super$A, _super$A2, _super$A3, _super$A4, _super$A5, _super$A6, _super$bound, _super$bound2, _super$bound3, _super$bound4, _super$bound5, _super$bound6, _super$unbound, _unbound, _super$unbound2, _super$unbound3, _unbound2, _super$unbound4, _super$unbound5, _unbound3, _super$unbound6, _unbound4, _Outer;
|
||||
|
||||
let bound = "A";
|
||||
|
||||
class Outer {}
|
||||
|
||||
_Outer = Outer;
|
||||
babelHelpers.defineProperty(Outer, "B", () => {
|
||||
// Transform
|
||||
babelHelpers.superPropSet(_Outer, "A", (_super$A = babelHelpers.superPropGet(_Outer, "A", _Outer), _super$A2 = _super$A++, _super$A), _Outer, 1), _super$A2;
|
||||
babelHelpers.superPropSet(_Outer, "A", (_super$A3 = babelHelpers.superPropGet(_Outer, "A", _Outer), _super$A4 = _super$A3--, _super$A3), _Outer, 1), _super$A4;
|
||||
babelHelpers.superPropSet(_Outer, "A", (_super$A5 = babelHelpers.superPropGet(_Outer, "A", _Outer), ++_super$A5), _Outer, 1);
|
||||
babelHelpers.superPropSet(_Outer, "A", (_super$A6 = babelHelpers.superPropGet(_Outer, "A", _Outer), --_super$A6), _Outer, 1);
|
||||
|
||||
babelHelpers.superPropSet(_Outer, bound, (_super$bound = babelHelpers.superPropGet(_Outer, bound, _Outer), _super$bound2 = _super$bound++, _super$bound), _Outer, 1), _super$bound2;
|
||||
babelHelpers.superPropSet(_Outer, bound, (_super$bound3 = babelHelpers.superPropGet(_Outer, bound, _Outer), _super$bound4 = _super$bound3--, _super$bound3), _Outer, 1), _super$bound4;
|
||||
babelHelpers.superPropSet(_Outer, bound, (_super$bound5 = babelHelpers.superPropGet(_Outer, bound, _Outer), ++_super$bound5), _Outer, 1);
|
||||
babelHelpers.superPropSet(_Outer, bound, (_super$bound6 = babelHelpers.superPropGet(_Outer, bound, _Outer), --_super$bound6), _Outer, 1);
|
||||
|
||||
babelHelpers.superPropSet(_Outer, _unbound = unbound, (_super$unbound = babelHelpers.superPropGet(_Outer, _unbound, _Outer), _super$unbound2 = _super$unbound++, _super$unbound), _Outer, 1), _super$unbound2;
|
||||
babelHelpers.superPropSet(_Outer, _unbound2 = unbound, (_super$unbound3 = babelHelpers.superPropGet(_Outer, _unbound2, _Outer), _super$unbound4 = _super$unbound3--, _super$unbound3), _Outer, 1), _super$unbound4;
|
||||
babelHelpers.superPropSet(_Outer, _unbound3 = unbound, (_super$unbound5 = babelHelpers.superPropGet(_Outer, _unbound3, _Outer), ++_super$unbound5), _Outer, 1);
|
||||
babelHelpers.superPropSet(_Outer, _unbound4 = unbound, (_super$unbound6 = babelHelpers.superPropGet(_Outer, _unbound4, _Outer), --_super$unbound6), _Outer, 1);
|
||||
|
||||
class Inner {
|
||||
method() {
|
||||
// Don't transform
|
||||
super.A++;
|
||||
super.A--;
|
||||
++super.A;
|
||||
--super.A;
|
||||
|
||||
super[bound]++;
|
||||
super[bound]--;
|
||||
++super[bound];
|
||||
--super[bound];
|
||||
|
||||
super[unbound]++;
|
||||
super[unbound]--;
|
||||
++super[unbound];
|
||||
--super[unbound];
|
||||
}
|
||||
|
||||
static staticMethod() {
|
||||
// Don't transform
|
||||
super.A++;
|
||||
super.A--;
|
||||
++super.A;
|
||||
--super.A;
|
||||
|
||||
super[bound]++;
|
||||
super[bound]--;
|
||||
++super[bound];
|
||||
--super[bound];
|
||||
|
||||
super[unbound]++;
|
||||
super[unbound]--;
|
||||
++super[unbound];
|
||||
--super[unbound];
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue