mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(minifier): compress a = a + b to a += b (#8314)
Compress `a = a + b` to `a += b` and for other binary operators that are possible to.
This commit is contained in:
parent
9ea4e31ba3
commit
e6fe84d674
3 changed files with 107 additions and 6 deletions
|
|
@ -140,6 +140,9 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
Self::swap_binary_expressions(e);
|
||||
self.try_compress_type_of_equal_string(e);
|
||||
}
|
||||
Expression::AssignmentExpression(e) => {
|
||||
self.try_compress_normal_assignment_to_combined_assignment(e, ctx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +150,9 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
if let Some(folded_expr) = match expr {
|
||||
Expression::Identifier(ident) => self.try_compress_undefined(ident, ctx),
|
||||
Expression::BooleanLiteral(_) => self.try_compress_boolean(expr, ctx),
|
||||
Expression::AssignmentExpression(e) => Self::try_compress_assignment_expression(e, ctx),
|
||||
Expression::AssignmentExpression(e) => {
|
||||
Self::try_compress_assignment_to_update_expression(e, ctx)
|
||||
}
|
||||
Expression::LogicalExpression(e) => Self::try_compress_is_null_or_undefined(e, ctx),
|
||||
Expression::NewExpression(e) => Self::try_fold_new_expression(e, ctx),
|
||||
Expression::TemplateLiteral(t) => Self::try_fold_template_literal(t, ctx),
|
||||
|
|
@ -525,7 +530,32 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_compress_assignment_expression(
|
||||
/// Compress `a = a + b` to `a += b`
|
||||
fn try_compress_normal_assignment_to_combined_assignment(
|
||||
&mut self,
|
||||
expr: &mut AssignmentExpression<'a>,
|
||||
ctx: Ctx<'a, 'b>,
|
||||
) {
|
||||
if !matches!(expr.operator, AssignmentOperator::Assign) {
|
||||
return;
|
||||
}
|
||||
let AssignmentTarget::AssignmentTargetIdentifier(write_id_ref) = &mut expr.left else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Expression::BinaryExpression(binary_expr) = &mut expr.right else { return };
|
||||
let Some(new_op) = binary_expr.operator.to_assignment_operator() else { return };
|
||||
let Expression::Identifier(read_id_ref) = &mut binary_expr.left else { return };
|
||||
if write_id_ref.name != read_id_ref.name {
|
||||
return;
|
||||
}
|
||||
|
||||
expr.operator = new_op;
|
||||
expr.right = ctx.ast.move_expression(&mut binary_expr.right);
|
||||
self.changed = true;
|
||||
}
|
||||
|
||||
fn try_compress_assignment_to_update_expression(
|
||||
expr: &mut AssignmentExpression<'a>,
|
||||
ctx: Ctx<'a, 'b>,
|
||||
) -> Option<Expression<'a>> {
|
||||
|
|
@ -1020,6 +1050,58 @@ mod test {
|
|||
test("x !== false", "x !== !1");
|
||||
}
|
||||
|
||||
/// Based on https://github.com/terser/terser/blob/58ba5c163fa1684f2a63c7bc19b7ebcf85b74f73/test/compress/assignment.js
|
||||
#[test]
|
||||
fn test_fold_normal_assignment_to_combined_assignment() {
|
||||
test("x = x + 3", "x += 3");
|
||||
test("x = x - 3", "x -= 3");
|
||||
test("x = x / 3", "x /= 3");
|
||||
test("x = x * 3", "x *= 3");
|
||||
test("x = x >> 3", "x >>= 3");
|
||||
test("x = x << 3", "x <<= 3");
|
||||
test("x = x >>> 3", "x >>>= 3");
|
||||
test("x = x | 3", "x |= 3");
|
||||
test("x = x ^ 3", "x ^= 3");
|
||||
test("x = x % 3", "x %= 3");
|
||||
test("x = x & 3", "x &= 3");
|
||||
test("x = x + g()", "x += g()");
|
||||
test("x = x - g()", "x -= g()");
|
||||
test("x = x / g()", "x /= g()");
|
||||
test("x = x * g()", "x *= g()");
|
||||
test("x = x >> g()", "x >>= g()");
|
||||
test("x = x << g()", "x <<= g()");
|
||||
test("x = x >>> g()", "x >>>= g()");
|
||||
test("x = x | g()", "x |= g()");
|
||||
test("x = x ^ g()", "x ^= g()");
|
||||
test("x = x % g()", "x %= g()");
|
||||
test("x = x & g()", "x &= g()");
|
||||
|
||||
test_same("x = 3 + x");
|
||||
test_same("x = 3 - x");
|
||||
test_same("x = 3 / x");
|
||||
test_same("x = 3 * x");
|
||||
test_same("x = 3 >> x");
|
||||
test_same("x = 3 << x");
|
||||
test_same("x = 3 >>> x");
|
||||
test_same("x = 3 | x");
|
||||
test_same("x = 3 ^ x");
|
||||
test_same("x = 3 % x");
|
||||
test_same("x = 3 & x");
|
||||
test_same("x = g() + x");
|
||||
test_same("x = g() - x");
|
||||
test_same("x = g() / x");
|
||||
test_same("x = g() * x");
|
||||
test_same("x = g() >> x");
|
||||
test_same("x = g() << x");
|
||||
test_same("x = g() >>> x");
|
||||
test_same("x = g() | x");
|
||||
test_same("x = g() ^ x");
|
||||
test_same("x = g() % x");
|
||||
test_same("x = g() & x");
|
||||
|
||||
test_same("x = (x -= 2) ^ x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_subtraction_assignment() {
|
||||
test("x -= 1", "--x");
|
||||
|
|
|
|||
|
|
@ -313,6 +313,25 @@ impl BinaryOperator {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get [`AssignmentOperator`] corresponding to this [`BinaryOperator`].
|
||||
pub fn to_assignment_operator(self) -> Option<AssignmentOperator> {
|
||||
match self {
|
||||
Self::Addition => Some(AssignmentOperator::Addition),
|
||||
Self::Subtraction => Some(AssignmentOperator::Subtraction),
|
||||
Self::Multiplication => Some(AssignmentOperator::Multiplication),
|
||||
Self::Division => Some(AssignmentOperator::Division),
|
||||
Self::Remainder => Some(AssignmentOperator::Remainder),
|
||||
Self::Exponential => Some(AssignmentOperator::Exponential),
|
||||
Self::ShiftLeft => Some(AssignmentOperator::ShiftLeft),
|
||||
Self::ShiftRight => Some(AssignmentOperator::ShiftRight),
|
||||
Self::ShiftRightZeroFill => Some(AssignmentOperator::ShiftRightZeroFill),
|
||||
Self::BitwiseOR => Some(AssignmentOperator::BitwiseOR),
|
||||
Self::BitwiseXOR => Some(AssignmentOperator::BitwiseXOR),
|
||||
Self::BitwiseAnd => Some(AssignmentOperator::BitwiseAnd),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The string representation of this operator as it appears in source code.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
-------------------------------------------------------------------------------------
|
||||
72.14 kB | 23.68 kB | 23.70 kB | 8.61 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 59.79 kB | 59.82 kB | 19.42 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 59.78 kB | 59.82 kB | 19.42 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 90.06 kB | 90.07 kB | 32.07 kB | 31.95 kB | jquery.js
|
||||
287.63 kB | 90.05 kB | 90.07 kB | 32.07 kB | 31.95 kB | jquery.js
|
||||
|
||||
342.15 kB | 118.16 kB | 118.14 kB | 44.52 kB | 44.37 kB | vue.js
|
||||
|
||||
|
|
@ -19,9 +19,9 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
|
||||
2.14 MB | 726.02 kB | 724.14 kB | 180.14 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.01 MB | 1.01 MB | 331.84 kB | 331.56 kB | echarts.js
|
||||
3.20 MB | 1.01 MB | 1.01 MB | 331.82 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.32 MB | 2.31 MB | 492.75 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.50 MB | 3.49 MB | 909.11 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.50 MB | 3.49 MB | 909.10 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue