mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(transformer/class-properties): transform super assignment expressions within static prop initializer (#7991)
Alternative of #7956 and #7959. Unlike the previous method, adapting duplicating the same logic rather than making the same logic transform function to be generic
This commit is contained in:
parent
e3d0889009
commit
cc57db38e1
7 changed files with 295 additions and 4 deletions
|
|
@ -159,6 +159,7 @@ pub enum Helper {
|
||||||
ClassPrivateFieldLooseKey,
|
ClassPrivateFieldLooseKey,
|
||||||
ClassPrivateFieldLooseBase,
|
ClassPrivateFieldLooseBase,
|
||||||
SuperPropGet,
|
SuperPropGet,
|
||||||
|
SuperPropSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Helper {
|
impl Helper {
|
||||||
|
|
@ -183,6 +184,7 @@ impl Helper {
|
||||||
Self::ClassPrivateFieldLooseKey => "classPrivateFieldLooseKey",
|
Self::ClassPrivateFieldLooseKey => "classPrivateFieldLooseKey",
|
||||||
Self::ClassPrivateFieldLooseBase => "classPrivateFieldLooseBase",
|
Self::ClassPrivateFieldLooseBase => "classPrivateFieldLooseBase",
|
||||||
Self::SuperPropGet => "superPropGet",
|
Self::SuperPropGet => "superPropGet",
|
||||||
|
Self::SuperPropSet => "superPropSet",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1608,7 +1608,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||||
/// * Anything else `foo()` -> `_foo = foo()`, `_foo`
|
/// * Anything else `foo()` -> `_foo = foo()`, `_foo`
|
||||||
///
|
///
|
||||||
/// Returns 2 `Expression`s. The first must be inserted into output first.
|
/// Returns 2 `Expression`s. The first must be inserted into output first.
|
||||||
fn duplicate_object(
|
pub(super) fn duplicate_object(
|
||||||
&self,
|
&self,
|
||||||
object: Expression<'a>,
|
object: Expression<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
|
|
||||||
|
|
@ -195,9 +195,15 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> {
|
||||||
self.transform_call_expression_if_super_member_expression(call_expr);
|
self.transform_call_expression_if_super_member_expression(call_expr);
|
||||||
self.class_properties.transform_call_expression(expr, self.ctx);
|
self.class_properties.transform_call_expression(expr, self.ctx);
|
||||||
}
|
}
|
||||||
|
// `super.prop = value`, `super.prop += value`, `super.prop ??= value` or
|
||||||
// `object.#prop = value`, `object.#prop += value`, `object.#prop ??= value` etc
|
// `object.#prop = value`, `object.#prop += value`, `object.#prop ??= value` etc
|
||||||
Expression::AssignmentExpression(_) => {
|
Expression::AssignmentExpression(_) => {
|
||||||
self.class_properties.transform_assignment_expression(expr, self.ctx);
|
self.transform_assignment_expression_if_super_member_assignment_target(expr);
|
||||||
|
// Check again if it's an assignment expression, because it could have been transformed
|
||||||
|
// to other expression.
|
||||||
|
if matches!(expr, Expression::AssignmentExpression(_)) {
|
||||||
|
self.class_properties.transform_assignment_expression(expr, self.ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// `object.#prop++`, `--object.#prop`
|
// `object.#prop++`, `--object.#prop`
|
||||||
Expression::UpdateExpression(_) => {
|
Expression::UpdateExpression(_) => {
|
||||||
|
|
@ -521,4 +527,14 @@ impl<'a, 'ctx, 'v> StaticInitializerVisitor<'a, 'ctx, 'v> {
|
||||||
.transform_call_expression_for_super_member_expr(call_expr, self.ctx);
|
.transform_call_expression_for_super_member_expr(call_expr, self.ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transform_assignment_expression_if_super_member_assignment_target(
|
||||||
|
&mut self,
|
||||||
|
expr: &mut Expression<'a>,
|
||||||
|
) {
|
||||||
|
if self.this_depth == 0 {
|
||||||
|
self.class_properties
|
||||||
|
.transform_assignment_expression_for_super_assignment_target(expr, self.ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,152 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||||
arguments.push(Argument::from(array));
|
arguments.push(Argument::from(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transform assignment expression where the left-hand side is a member expression with `super`.
|
||||||
|
///
|
||||||
|
/// * `super.prop = value`
|
||||||
|
/// -> `_superPropSet(_Class, "prop", value, _Class, 1)`
|
||||||
|
/// * `super.prop += value`
|
||||||
|
/// -> `_superPropSet(_Class, "prop", _superPropGet(_Class, "prop", _Class) + value, _Class, 1)`
|
||||||
|
/// * `super.prop &&= value`
|
||||||
|
/// -> `_superPropGet(_Class, "prop", _Class) && _superPropSet(_Class, "prop", value, _Class, 1)`
|
||||||
|
///
|
||||||
|
/// * `super[prop] = value`
|
||||||
|
/// -> `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
/// * `super[prop] += value`
|
||||||
|
/// -> `_superPropSet(_Class, prop, _superPropGet(_Class, prop, _Class) + value, _Class, 1)`
|
||||||
|
/// * `super[prop] &&= value`
|
||||||
|
/// -> `_superPropGet(_Class, prop, _Class) && _superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
//
|
||||||
|
// `#[inline]` so can bail out fast without a function call if `left` 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_assignment_expression_for_super_assignment_target(
|
||||||
|
&mut self,
|
||||||
|
expr: &mut Expression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
let Expression::AssignmentExpression(assign_expr) = expr else { unreachable!() };
|
||||||
|
match &assign_expr.left {
|
||||||
|
AssignmentTarget::StaticMemberExpression(member) if member.object.is_super() => {
|
||||||
|
self.transform_assignment_expression_for_super_static_member_expr(expr, ctx);
|
||||||
|
}
|
||||||
|
AssignmentTarget::ComputedMemberExpression(member) if member.object.is_super() => {
|
||||||
|
self.transform_assignment_expression_for_super_computed_member_expr(expr, ctx);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform assignment expression where the left-hand side is a static member expression
|
||||||
|
/// with `super`.
|
||||||
|
///
|
||||||
|
/// * `super.prop = value`
|
||||||
|
/// -> `_superPropSet(_Class, "prop", value, _Class, 1)`
|
||||||
|
/// * `super.prop += value`
|
||||||
|
/// -> `_superPropSet(_Class, "prop", _superPropGet(_Class, "prop", _Class) + value, _Class, 1)`
|
||||||
|
/// * `super.prop &&= value`
|
||||||
|
/// -> `_superPropGet(_Class, "prop", _Class) && _superPropSet(_Class, "prop", value, _Class, 1)`
|
||||||
|
//
|
||||||
|
// `#[inline]` so that compiler sees that `expr` is an `Expression::AssignmentExpression`.
|
||||||
|
#[inline]
|
||||||
|
fn transform_assignment_expression_for_super_static_member_expr(
|
||||||
|
&mut self,
|
||||||
|
expr: &mut Expression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
let Expression::AssignmentExpression(assign_expr) = ctx.ast.move_expression(expr) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let AssignmentExpression { span, operator, right: value, left } = assign_expr.unbox();
|
||||||
|
let AssignmentTarget::StaticMemberExpression(member) = left else { unreachable!() };
|
||||||
|
let property = ctx.ast.expression_string_literal(
|
||||||
|
member.property.span,
|
||||||
|
member.property.name.clone(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
*expr =
|
||||||
|
self.transform_super_assignment_expression_impl(span, operator, property, value, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform assignment expression where the left-hand side is a computed member expression
|
||||||
|
/// with `super` as member expr object.
|
||||||
|
///
|
||||||
|
/// * `super[prop] = value`
|
||||||
|
/// -> `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
/// * `super[prop] += value`
|
||||||
|
/// -> `_superPropSet(_Class, prop, _superPropGet(_Class, prop, _Class) + value, _Class, 1)`
|
||||||
|
/// * `super[prop] &&= value`
|
||||||
|
/// -> `_superPropGet(_Class, prop, _Class) && _superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
///
|
||||||
|
// `#[inline]` so that compiler sees that `expr` is an `Expression::AssignmentExpression`.
|
||||||
|
#[inline]
|
||||||
|
fn transform_assignment_expression_for_super_computed_member_expr(
|
||||||
|
&mut self,
|
||||||
|
expr: &mut Expression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
let Expression::AssignmentExpression(assign_expr) = ctx.ast.move_expression(expr) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let AssignmentExpression { span, operator, right: value, left } = assign_expr.unbox();
|
||||||
|
let AssignmentTarget::ComputedMemberExpression(member) = left else { unreachable!() };
|
||||||
|
let property = member.unbox().expression;
|
||||||
|
*expr =
|
||||||
|
self.transform_super_assignment_expression_impl(span, operator, property, value, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform assignment expression where the left-hand side is a member expression with `super`
|
||||||
|
/// as member expr object.
|
||||||
|
///
|
||||||
|
/// * `=` -> `_superPropSet(_Class, <prop>, <value>, _Class, 1)`
|
||||||
|
/// * `+=` -> `_superPropSet(_Class, <prop>, _superPropGet(_Class, <prop>, _Class) + <value>, 1)`
|
||||||
|
/// * `&&=` -> `_superPropGet(_Class, <prop>, _Class) && _superPropSet(_Class, <prop>, <value>, _Class, 1)`
|
||||||
|
fn transform_super_assignment_expression_impl(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
operator: AssignmentOperator,
|
||||||
|
property: Expression<'a>,
|
||||||
|
value: Expression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) -> Expression<'a> {
|
||||||
|
if operator == AssignmentOperator::Assign {
|
||||||
|
// `super[prop] = value` -> `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
self.create_super_prop_set(span, property, value, ctx)
|
||||||
|
} else {
|
||||||
|
// Make 2 copies of `object`
|
||||||
|
let (property1, property2) = self.duplicate_object(property, ctx);
|
||||||
|
|
||||||
|
if let Some(operator) = operator.to_binary_operator() {
|
||||||
|
// `super[prop] += value`
|
||||||
|
// -> `_superPropSet(_Class, prop, _superPropGet(_Class, prop, _Class) + value, _Class, 1)`
|
||||||
|
|
||||||
|
// `_superPropGet(_Class, prop, _Class)`
|
||||||
|
let get_call = self.create_super_prop_get(SPAN, property2, false, ctx);
|
||||||
|
|
||||||
|
// `_superPropGet(_Class, prop, _Class) + value`
|
||||||
|
let value = ctx.ast.expression_binary(SPAN, get_call, operator, value);
|
||||||
|
|
||||||
|
// `_superPropSet(_Class, prop, _superPropGet(_Class, prop, _Class) + value, 1)`
|
||||||
|
self.create_super_prop_set(span, property1, value, ctx)
|
||||||
|
} else if let Some(operator) = operator.to_logical_operator() {
|
||||||
|
// `super[prop] &&= value`
|
||||||
|
// -> `_superPropGet(_Class, prop, _Class) && _superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
|
||||||
|
// `_superPropGet(_Class, prop, _Class)`
|
||||||
|
let get_call = self.create_super_prop_get(SPAN, property1, false, ctx);
|
||||||
|
|
||||||
|
// `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
let set_call = self.create_super_prop_set(span, property2, value, ctx);
|
||||||
|
|
||||||
|
// `_superPropGet(_Class, prop, _Class) && _superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
ctx.ast.expression_logical(span, get_call, operator, set_call)
|
||||||
|
} else {
|
||||||
|
// The above covers all types of `AssignmentOperator`
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Member:
|
/// Member:
|
||||||
/// `_superPropGet(_Class, prop, _Class)`
|
/// `_superPropGet(_Class, prop, _Class)`
|
||||||
///
|
///
|
||||||
|
|
@ -155,4 +301,28 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||||
// `_superPropGet(_Class, prop, _Class)` or `_superPropGet(_Class, prop, _Class, 2)`
|
// `_superPropGet(_Class, prop, _Class)` or `_superPropGet(_Class, prop, _Class, 2)`
|
||||||
self.ctx.helper_call_expr(Helper::SuperPropGet, span, arguments, ctx)
|
self.ctx.helper_call_expr(Helper::SuperPropGet, span, arguments, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `_superPropSet(_Class, prop, value, _Class, 1)`
|
||||||
|
fn create_super_prop_set(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
property: Expression<'a>,
|
||||||
|
value: Expression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) -> Expression<'a> {
|
||||||
|
let temp_binding = self.current_class_mut().bindings.get_or_init_static_binding(ctx);
|
||||||
|
let arguments = ctx.ast.vec_from_array([
|
||||||
|
Argument::from(temp_binding.create_read_expression(ctx)),
|
||||||
|
Argument::from(property),
|
||||||
|
Argument::from(value),
|
||||||
|
Argument::from(temp_binding.create_read_expression(ctx)),
|
||||||
|
Argument::from(ctx.ast.expression_numeric_literal(
|
||||||
|
SPAN,
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
NumberBase::Decimal,
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
self.ctx.helper_call_expr(Helper::SuperPropSet, span, arguments, ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
commit: 54a8389f
|
commit: 54a8389f
|
||||||
|
|
||||||
Passed: 112/127
|
Passed: 113/128
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-class-static-block
|
* babel-plugin-transform-class-static-block
|
||||||
|
|
@ -16,7 +16,7 @@ Passed: 112/127
|
||||||
* regexp
|
* regexp
|
||||||
|
|
||||||
|
|
||||||
# babel-plugin-transform-class-properties (13/16)
|
# babel-plugin-transform-class-properties (14/17)
|
||||||
* static-super-tagged-template/input.js
|
* static-super-tagged-template/input.js
|
||||||
x Output mismatch
|
x Output mismatch
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
const ident = "A";
|
||||||
|
|
||||||
|
class Outer {
|
||||||
|
static B = () => {
|
||||||
|
// Transform
|
||||||
|
super.A = 1;
|
||||||
|
super.A += 1;
|
||||||
|
super.A -= 1;
|
||||||
|
super.A &&= 1;
|
||||||
|
super.A ||= 1;
|
||||||
|
|
||||||
|
super[ident] = 1;
|
||||||
|
super[ident] += 1;
|
||||||
|
super[ident] -= 1;
|
||||||
|
super[ident] &&= 1;
|
||||||
|
super[ident] ||= 1;
|
||||||
|
|
||||||
|
class Inner {
|
||||||
|
method() {
|
||||||
|
// Don't transform
|
||||||
|
super.A = 1;
|
||||||
|
super.A += 1;
|
||||||
|
super.A -= 1;
|
||||||
|
super.A &&= 1;
|
||||||
|
super.A ||= 1;
|
||||||
|
|
||||||
|
super[ident] = 1;
|
||||||
|
super[ident] += 1;
|
||||||
|
super[ident] -= 1;
|
||||||
|
super[ident] &&= 1;
|
||||||
|
super[ident] ||= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static staticMethod() {
|
||||||
|
// Don't transform
|
||||||
|
super.A = 1;
|
||||||
|
super.A += 1;
|
||||||
|
super.A -= 1;
|
||||||
|
super.A &&= 1;
|
||||||
|
super.A ||= 1;
|
||||||
|
|
||||||
|
super[ident] = 1;
|
||||||
|
super[ident] += 1;
|
||||||
|
super[ident] -= 1;
|
||||||
|
super[ident] &&= 1;
|
||||||
|
super[ident] ||= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
var _Outer;
|
||||||
|
|
||||||
|
const ident = "A";
|
||||||
|
|
||||||
|
class Outer {}
|
||||||
|
|
||||||
|
_Outer = Outer;
|
||||||
|
babelHelpers.defineProperty(Outer, "B", () => {
|
||||||
|
// Transform
|
||||||
|
babelHelpers.superPropSet(_Outer, "A", 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropSet(_Outer, "A", babelHelpers.superPropGet(_Outer, "A", _Outer) + 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropSet(_Outer, "A", babelHelpers.superPropGet(_Outer, "A", _Outer) - 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropGet(_Outer, "A", _Outer) && babelHelpers.superPropSet(_Outer, "A", 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropGet(_Outer, "A", _Outer) || babelHelpers.superPropSet(_Outer, "A", 1, _Outer, 1);
|
||||||
|
|
||||||
|
babelHelpers.superPropSet(_Outer, ident, 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropSet(_Outer, ident, babelHelpers.superPropGet(_Outer, ident, _Outer) + 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropSet(_Outer, ident, babelHelpers.superPropGet(_Outer, ident, _Outer) - 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropGet(_Outer, ident, _Outer) && babelHelpers.superPropSet(_Outer, ident, 1, _Outer, 1);
|
||||||
|
babelHelpers.superPropGet(_Outer, ident, _Outer) || babelHelpers.superPropSet(_Outer, ident, 1, _Outer, 1);
|
||||||
|
|
||||||
|
class Inner {
|
||||||
|
method() {
|
||||||
|
// Don't transform
|
||||||
|
super.A = 1;
|
||||||
|
super.A += 1;
|
||||||
|
super.A -= 1;
|
||||||
|
super.A &&= 1;
|
||||||
|
super.A ||= 1;
|
||||||
|
|
||||||
|
super[ident] = 1;
|
||||||
|
super[ident] += 1;
|
||||||
|
super[ident] -= 1;
|
||||||
|
super[ident] &&= 1;
|
||||||
|
super[ident] ||= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static staticMethod() {
|
||||||
|
// Don't transform
|
||||||
|
super.A = 1;
|
||||||
|
super.A += 1;
|
||||||
|
super.A -= 1;
|
||||||
|
super.A &&= 1;
|
||||||
|
super.A ||= 1;
|
||||||
|
|
||||||
|
super[ident] = 1;
|
||||||
|
super[ident] += 1;
|
||||||
|
super[ident] -= 1;
|
||||||
|
super[ident] &&= 1;
|
||||||
|
super[ident] ||= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue