mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +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,8 +207,13 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> {
|
||||||
}
|
}
|
||||||
// `object.#prop++`, `--object.#prop`
|
// `object.#prop++`, `--object.#prop`
|
||||||
Expression::UpdateExpression(_) => {
|
Expression::UpdateExpression(_) => {
|
||||||
|
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);
|
self.class_properties.transform_update_expression(expr, self.ctx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// `object?.#prop`
|
// `object?.#prop`
|
||||||
Expression::ChainExpression(_) => {
|
Expression::ChainExpression(_) => {
|
||||||
self.class_properties.transform_chain_expression(expr, self.ctx);
|
self.class_properties.transform_chain_expression(expr, self.ctx);
|
||||||
|
|
@ -540,4 +545,17 @@ impl<'a, 'ctx, 'v> StaticInitializerVisitor<'a, 'ctx, 'v> {
|
||||||
.transform_assignment_expression_for_super_assignment_target(expr, self.ctx);
|
.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
|
//! ES2022: Class Properties
|
||||||
//! Transform of `super` expressions.
|
//! 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_ast::ast::*;
|
||||||
use oxc_span::SPAN;
|
use oxc_span::SPAN;
|
||||||
use oxc_traverse::TraverseCtx;
|
use oxc_traverse::{BoundIdentifier, TraverseCtx};
|
||||||
|
|
||||||
use crate::Helper;
|
use crate::Helper;
|
||||||
|
|
||||||
use super::ClassProperties;
|
use super::{utils::create_assignment, ClassProperties};
|
||||||
|
|
||||||
impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||||
/// Transform static member expression where object is `super`.
|
/// 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:
|
/// Member:
|
||||||
/// `_superPropGet(_Class, prop, _Class)`
|
/// `_superPropGet(_Class, prop, _Class)`
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
commit: 54a8389f
|
commit: 54a8389f
|
||||||
|
|
||||||
Passed: 113/128
|
Passed: 114/129
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-class-static-block
|
* babel-plugin-transform-class-static-block
|
||||||
|
|
@ -16,7 +16,7 @@ Passed: 113/128
|
||||||
* regexp
|
* regexp
|
||||||
|
|
||||||
|
|
||||||
# babel-plugin-transform-class-properties (14/17)
|
# babel-plugin-transform-class-properties (15/18)
|
||||||
* static-super-tagged-template/input.js
|
* static-super-tagged-template/input.js
|
||||||
x Output mismatch
|
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