Skip to content

Commit

Permalink
refactor(transformer): remove nested match in logical assignment oper…
Browse files Browse the repository at this point in the history
…ator transform (#5897)

No need to convert `AssignmentTarget` to `MemberExpression`, and then match on `MemberExpression::StaticMemberExpression` etc. Can just match on `AssignmentTarget::StaticMemberExpression` etc directly.

Diff is large due to big block of code having less indentation, but contains no changes apart from those described above.
  • Loading branch information
overlookmotel committed Sep 20, 2024
1 parent d335a67 commit b11d91c
Showing 1 changed file with 169 additions and 174 deletions.
343 changes: 169 additions & 174 deletions crates/oxc_transformer/src/es2021/logical_assignment_operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {

// TODO: refactor this block, add tests, cover private identifier
match &mut assignment_expr.left {
// `a &&= c` -> `a && (a = c)`
AssignmentTarget::AssignmentTargetIdentifier(ident) => {
let reference = ctx.symbols_mut().get_reference_mut(ident.reference_id().unwrap());
*reference.flags_mut() = ReferenceFlags::Read;
Expand All @@ -128,191 +129,185 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
),
);
}
left @ match_member_expression!(AssignmentTarget) => {
let member_expr = left.to_member_expression_mut();
let op = AssignmentOperator::Assign;

// `a.b &&= c` -> `var _a; (_a = a).b && (_a.b = c)`
match member_expr {
MemberExpression::StaticMemberExpression(static_expr) => {
if let Some(ident) = self.maybe_generate_memoised(&static_expr.object, ctx)
{
// (_o = o).a
let right = ctx.ast.move_expression(&mut static_expr.object);
let target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
),
);
let object = ctx.ast.expression_assignment(SPAN, op, target, right);
left_expr = Expression::from(ctx.ast.member_expression_static(
SPAN,
object,
static_expr.property.clone_in(ctx.ast.allocator),
false,
));

// (_o.a = 1)
let assign_expr = ctx.ast.member_expression_static(
SPAN,
ctx.ast.expression_from_identifier_reference(ident),
static_expr.property.clone_in(ctx.ast.allocator),
false,
);
assign_target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_member_expression(assign_expr),
);
} else {
// transform `obj.x ||= 1` to `obj.x || (obj.x = 1)`
let object = ctx.ast.move_expression(&mut static_expr.object);

// TODO: We should use static_expr.clone_in instead of cloning the properties,
// but currently clone_in will get rid of IdentifierReference's reference_id
let static_expr_cloned = ctx.ast.static_member_expression(
static_expr.span,
Self::clone_expression(&object, ctx),
static_expr.property.clone_in(ctx.ast.allocator),
static_expr.optional,
);

left_expr = ctx.ast.expression_member(
ctx.ast.member_expression_from_static(static_expr_cloned),
);

let member_expr_moved = ctx.ast.member_expression_static(
static_expr.span,
object,
static_expr.property.clone_in(ctx.ast.allocator),
static_expr.optional,
);

assign_target = AssignmentTarget::from(
ctx.ast
.simple_assignment_target_member_expression(member_expr_moved),
);
};
// `a.b &&= c` -> `var _a; (_a = a).b && (_a.b = c)`
AssignmentTarget::StaticMemberExpression(static_expr) => {
if let Some(ident) = self.maybe_generate_memoised(&static_expr.object, ctx) {
// (_o = o).a
let right = ctx.ast.move_expression(&mut static_expr.object);
let target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
),
);
let object = ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
target,
right,
);
left_expr = Expression::from(ctx.ast.member_expression_static(
SPAN,
object,
static_expr.property.clone_in(ctx.ast.allocator),
false,
));

// (_o.a = 1)
let assign_expr = ctx.ast.member_expression_static(
SPAN,
ctx.ast.expression_from_identifier_reference(ident),
static_expr.property.clone_in(ctx.ast.allocator),
false,
);
assign_target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_member_expression(assign_expr),
);
} else {
// transform `obj.x ||= 1` to `obj.x || (obj.x = 1)`
let object = ctx.ast.move_expression(&mut static_expr.object);

// TODO: We should use static_expr.clone_in instead of cloning the properties,
// but currently clone_in will get rid of IdentifierReference's reference_id
let static_expr_cloned = ctx.ast.static_member_expression(
static_expr.span,
Self::clone_expression(&object, ctx),
static_expr.property.clone_in(ctx.ast.allocator),
static_expr.optional,
);

left_expr = ctx.ast.expression_member(
ctx.ast.member_expression_from_static(static_expr_cloned),
);

let member_expr_moved = ctx.ast.member_expression_static(
static_expr.span,
object,
static_expr.property.clone_in(ctx.ast.allocator),
static_expr.optional,
);

assign_target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_member_expression(member_expr_moved),
);
};
}
// `a[b.y] &&= c;` ->
// `var _a, _b$y; (_a = a)[_b$y = b.y] && (_a[_b$y] = c);`
AssignmentTarget::ComputedMemberExpression(computed_expr) => {
if let Some(ident) = self.maybe_generate_memoised(&computed_expr.object, ctx) {
// (_o = object)
let right = ctx.ast.move_expression(&mut computed_expr.object);
let target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
),
);
let object = ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
target,
right,
);

let mut expression = ctx.ast.move_expression(&mut computed_expr.expression);

// _b = expression
let property = self.maybe_generate_memoised(&expression, ctx);

if let Some(ref property) = property {
let left = AssignmentTarget::from(
ctx.ast.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(property, ReferenceFlags::Write),
),
);
expression = ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
left,
expression,
);
}
// `a[b.y] &&= c;` ->
// `var _a, _b$y; (_a = a)[_b$y = b.y] && (_a[_b$y] = c);`
MemberExpression::ComputedMemberExpression(computed_expr) => {
if let Some(ident) =
self.maybe_generate_memoised(&computed_expr.object, ctx)

// _o[_b]
assign_target = AssignmentTarget::from(ctx.ast.member_expression_computed(
SPAN,
ctx.ast.expression_from_identifier_reference(
ctx.clone_identifier_reference(&ident, ReferenceFlags::Read),
),
property.map_or_else(
|| expression.clone_in(ctx.ast.allocator),
|ident| ctx.ast.expression_from_identifier_reference(ident),
),
false,
));

left_expr = Expression::from(
ctx.ast.member_expression_computed(SPAN, object, expression, false),
);
} else {
// transform `obj[++key] ||= 1` to `obj[_key = ++key] || (obj[_key] = 1)`
let property_ident =
self.maybe_generate_memoised(&computed_expr.expression, ctx);

let object = ctx.ast.move_expression(&mut computed_expr.object);
let mut expression = ctx.ast.move_expression(&mut computed_expr.expression);

// TODO: ideally we should use computed_expr.clone_in instead of cloning the properties,
// but currently clone_in will get rid of IdentifierReference's reference_id
let new_compute_expr = ctx.ast.computed_member_expression(
computed_expr.span,
Self::clone_expression(&object, ctx),
{
// (_o = object)
let right = ctx.ast.move_expression(&mut computed_expr.object);
let target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
),
);
let object = ctx.ast.expression_assignment(SPAN, op, target, right);

let mut expression =
ctx.ast.move_expression(&mut computed_expr.expression);

// _b = expression
let property = self.maybe_generate_memoised(&expression, ctx);

if let Some(ref property) = property {
// _key = ++key
if let Some(property_ident) = &property_ident {
let left = AssignmentTarget::from(
ctx.ast.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(
property,
property_ident,
ReferenceFlags::Write,
),
),
);
expression =
ctx.ast.expression_assignment(SPAN, op, left, expression);
}

// _o[_b]
assign_target =
AssignmentTarget::from(ctx.ast.member_expression_computed(
ctx.ast.expression_assignment(
SPAN,
ctx.ast.expression_from_identifier_reference(
ctx.clone_identifier_reference(
&ident,
ReferenceFlags::Read,
),
),
property.map_or_else(
|| expression.clone_in(ctx.ast.allocator),
|ident| ctx.ast.expression_from_identifier_reference(ident),
),
false,
));

left_expr = Expression::from(
ctx.ast.member_expression_computed(SPAN, object, expression, false),
);
} else {
// transform `obj[++key] ||= 1` to `obj[_key = ++key] || (obj[_key] = 1)`
let property_ident =
self.maybe_generate_memoised(&computed_expr.expression, ctx);

let object = ctx.ast.move_expression(&mut computed_expr.object);
let mut expression =
ctx.ast.move_expression(&mut computed_expr.expression);

// TODO: ideally we should use computed_expr.clone_in instead of cloning the properties,
// but currently clone_in will get rid of IdentifierReference's reference_id
let new_compute_expr = ctx.ast.computed_member_expression(
computed_expr.span,
Self::clone_expression(&object, ctx),
{
// _key = ++key
if let Some(property_ident) = &property_ident {
let left = AssignmentTarget::from(
ctx.ast
.simple_assignment_target_from_identifier_reference(
ctx.clone_identifier_reference(
property_ident,
ReferenceFlags::Write,
),
),
);
ctx.ast.expression_assignment(
SPAN,
op,
left,
ctx.ast.move_expression(&mut expression),
)
} else {
Self::clone_expression(&expression, ctx)
}
},
computed_expr.optional,
);

left_expr = ctx.ast.expression_member(
ctx.ast.member_expression_from_computed(new_compute_expr),
);

// obj[_key] = 1
let new_compute_expr = ctx.ast.computed_member_expression(
computed_expr.span,
object,
{
if let Some(property_ident) = property_ident {
ctx.ast.expression_from_identifier_reference(property_ident)
} else {
expression
}
},
computed_expr.optional,
);

assign_target = AssignmentTarget::from(
ctx.ast.simple_assignment_target_member_expression(
ctx.ast.member_expression_from_computed(new_compute_expr),
),
);
};
}
MemberExpression::PrivateFieldExpression(_) => return,
}
AssignmentOperator::Assign,
left,
ctx.ast.move_expression(&mut expression),
)
} else {
Self::clone_expression(&expression, ctx)
}
},
computed_expr.optional,
);

left_expr = ctx.ast.expression_member(
ctx.ast.member_expression_from_computed(new_compute_expr),
);

// obj[_key] = 1
let new_compute_expr = ctx.ast.computed_member_expression(
computed_expr.span,
object,
{
if let Some(property_ident) = property_ident {
ctx.ast.expression_from_identifier_reference(property_ident)
} else {
expression
}
},
computed_expr.optional,
);

assign_target =
AssignmentTarget::from(ctx.ast.simple_assignment_target_member_expression(
ctx.ast.member_expression_from_computed(new_compute_expr),
));
};
}
// TODO
#[allow(clippy::match_same_arms)]
AssignmentTarget::PrivateFieldExpression(_) => return,
// All other are TypeScript syntax.

// It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is not simple.
Expand Down

0 comments on commit b11d91c

Please sign in to comment.