Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions crates/oxc_minifier/src/ast_passes/collapse.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder};
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};

use crate::{CompressOptions, CompressorPass};
Expand All @@ -8,28 +8,27 @@ use crate::{CompressOptions, CompressorPass};
///
/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
/// TODO: `a = null; b = null;` => `a = b = null`
pub struct Collapse<'a> {
ast: AstBuilder<'a>,
pub struct Collapse {
options: CompressOptions,
}

impl<'a> CompressorPass<'a> for Collapse<'a> {}
impl<'a> CompressorPass<'a> for Collapse {}

impl<'a> Traverse<'a> for Collapse<'a> {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) {
impl<'a> Traverse<'a> for Collapse {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
if self.options.join_vars {
self.join_vars(stmts);
self.join_vars(stmts, ctx);
}
}
}

impl<'a> Collapse<'a> {
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self {
Self { ast, options }
impl<'a> Collapse {
pub fn new(options: CompressOptions) -> Self {
Self { options }
}

/// Join consecutive var statements
fn join_vars(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
fn join_vars(&self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
// Collect all the consecutive ranges that contain joinable vars.
// This is required because Rust prevents in-place vec mutation.
let mut ranges = vec![];
Expand Down Expand Up @@ -72,7 +71,7 @@ impl<'a> Collapse<'a> {
}

// Reconstruct the stmts array by joining consecutive ranges
let mut new_stmts = self.ast.vec_with_capacity(stmts.len() - capacity);
let mut new_stmts = ctx.ast.vec_with_capacity(stmts.len() - capacity);
for (i, stmt) in stmts.drain(..).enumerate() {
if i > 0 && ranges.iter().any(|range| range.contains(&(i - 1)) && range.contains(&i)) {
if let Statement::VariableDeclaration(prev_decl) = new_stmts.last_mut().unwrap() {
Expand Down
58 changes: 28 additions & 30 deletions crates/oxc_minifier/src/ast_passes/fold_constants.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{cmp::Ordering, mem};

use num_bigint::BigInt;
use oxc_ast::{ast::*, AstBuilder};
use oxc_ast::ast::*;
use oxc_span::{GetSpan, Span, SPAN};
use oxc_syntax::{
number::NumberBase,
Expand All @@ -19,22 +19,21 @@ use crate::{
/// Constant Folding
///
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
pub struct FoldConstants<'a> {
ast: AstBuilder<'a>,
pub struct FoldConstants {
evaluate: bool,
}

impl<'a> CompressorPass<'a> for FoldConstants<'a> {}
impl<'a> CompressorPass<'a> for FoldConstants {}

impl<'a> Traverse<'a> for FoldConstants<'a> {
impl<'a> Traverse<'a> for FoldConstants {
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.fold_expression(expr, ctx);
}
}

impl<'a> FoldConstants<'a> {
pub fn new(ast: AstBuilder<'a>) -> Self {
Self { ast, evaluate: false }
impl<'a> FoldConstants {
pub fn new() -> Self {
Self { evaluate: false }
}

pub fn with_evaluate(mut self, yes: bool) -> Self {
Expand Down Expand Up @@ -110,7 +109,7 @@ impl<'a> FoldConstants<'a> {
UnaryOperator::Void => Self::try_reduce_void(expr, ctx),
UnaryOperator::Typeof => self.try_fold_type_of(expr, ctx),
UnaryOperator::LogicalNot => {
expr.argument.to_boolean().map(|b| self.ast.expression_boolean_literal(SPAN, !b))
expr.argument.to_boolean().map(|b| ctx.ast.expression_boolean_literal(SPAN, !b))
}
// `-NaN` -> `NaN`
UnaryOperator::UnaryNegation if expr.argument.is_nan() => {
Expand Down Expand Up @@ -161,7 +160,7 @@ impl<'a> FoldConstants<'a> {
Expression::Identifier(ident) if ctx.is_identifier_undefined(ident) => "undefined",
_ => return None,
};
Some(self.ast.expression_string_literal(SPAN, s))
Some(ctx.ast.expression_string_literal(SPAN, s))
}

fn try_fold_addition<'b>(
Expand Down Expand Up @@ -189,7 +188,7 @@ impl<'a> FoldConstants<'a> {
let right_string = ctx.get_string_value(right)?;
// let value = left_string.to_owned().
let value = left_string + right_string;
Some(self.ast.expression_string_literal(span, value))
Some(ctx.ast.expression_string_literal(span, value))
},

// number addition
Expand All @@ -202,7 +201,7 @@ impl<'a> FoldConstants<'a> {
// Float if value has a fractional part, otherwise Decimal
let number_base = if is_exact_int64(value) { NumberBase::Decimal } else { NumberBase::Float };
// todo: add raw &str
Some(self.ast.expression_numeric_literal(span, value, "", number_base))
Some(ctx.ast.expression_numeric_literal(span, value, "", number_base))
},
_ => None
}
Expand All @@ -221,7 +220,7 @@ impl<'a> FoldConstants<'a> {
Tri::False => false,
Tri::Unknown => return None,
};
Some(self.ast.expression_boolean_literal(span, value))
Some(ctx.ast.expression_boolean_literal(span, value))
}

fn evaluate_comparison<'b>(
Expand All @@ -235,9 +234,9 @@ impl<'a> FoldConstants<'a> {
return Tri::Unknown;
}
match op {
BinaryOperator::Equality => self.try_abstract_equality_comparison(left, right, ctx),
BinaryOperator::Equality => Self::try_abstract_equality_comparison(left, right, ctx),
BinaryOperator::Inequality => {
self.try_abstract_equality_comparison(left, right, ctx).not()
Self::try_abstract_equality_comparison(left, right, ctx).not()
}
BinaryOperator::StrictEquality => {
Self::try_strict_equality_comparison(left, right, ctx)
Expand All @@ -263,7 +262,6 @@ impl<'a> FoldConstants<'a> {

/// <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
fn try_abstract_equality_comparison<'b>(
&self,
left_expr: &'b Expression<'a>,
right_expr: &'b Expression<'a>,
ctx: &mut TraverseCtx<'a>,
Expand All @@ -282,14 +280,14 @@ impl<'a> FoldConstants<'a> {
let right_number = ctx.get_side_free_number_value(right_expr);

if let Some(NumberValue::Number(num)) = right_number {
let number_literal_expr = self.ast.expression_numeric_literal(
let number_literal_expr = ctx.ast.expression_numeric_literal(
right_expr.span(),
num,
num.to_string(),
if num.fract() == 0.0 { NumberBase::Decimal } else { NumberBase::Float },
);

return self.try_abstract_equality_comparison(
return Self::try_abstract_equality_comparison(
left_expr,
&number_literal_expr,
ctx,
Expand All @@ -303,14 +301,14 @@ impl<'a> FoldConstants<'a> {
let left_number = ctx.get_side_free_number_value(left_expr);

if let Some(NumberValue::Number(num)) = left_number {
let number_literal_expr = self.ast.expression_numeric_literal(
let number_literal_expr = ctx.ast.expression_numeric_literal(
left_expr.span(),
num,
num.to_string(),
if num.fract() == 0.0 { NumberBase::Decimal } else { NumberBase::Float },
);

return self.try_abstract_equality_comparison(
return Self::try_abstract_equality_comparison(
&number_literal_expr,
right_expr,
ctx,
Expand Down Expand Up @@ -567,7 +565,7 @@ impl<'a> FoldConstants<'a> {
_ => unreachable!("Unknown binary operator {:?}", op),
};

return Some(self.ast.expression_numeric_literal(
return Some(ctx.ast.expression_numeric_literal(
span,
result_val,
result_val.to_string(),
Expand Down Expand Up @@ -607,7 +605,7 @@ impl<'a> FoldConstants<'a> {
// (TRUE || x) => TRUE (also, (3 || x) => 3)
// (FALSE && x) => FALSE
if if lval { op == LogicalOperator::Or } else { op == LogicalOperator::And } {
return Some(self.ast.move_expression(&mut logical_expr.left));
return Some(ctx.ast.move_expression(&mut logical_expr.left));
} else if !left.may_have_side_effects() {
let parent = ctx.ancestry.parent();
// Bail `let o = { f() { assert.ok(this !== o); } }; (true && o.f)(); (true && o.f)``;`
Expand All @@ -616,17 +614,17 @@ impl<'a> FoldConstants<'a> {
}
// (FALSE || x) => x
// (TRUE && x) => x
return Some(self.ast.move_expression(&mut logical_expr.right));
return Some(ctx.ast.move_expression(&mut logical_expr.right));
}
// Left side may have side effects, but we know its boolean value.
// e.g. true_with_sideeffects || foo() => true_with_sideeffects, foo()
// or: false_with_sideeffects && foo() => false_with_sideeffects, foo()
let left = self.ast.move_expression(&mut logical_expr.left);
let right = self.ast.move_expression(&mut logical_expr.right);
let mut vec = self.ast.vec_with_capacity(2);
let left = ctx.ast.move_expression(&mut logical_expr.left);
let right = ctx.ast.move_expression(&mut logical_expr.right);
let mut vec = ctx.ast.vec_with_capacity(2);
vec.push(left);
vec.push(right);
let sequence_expr = self.ast.expression_sequence(logical_expr.span, vec);
let sequence_expr = ctx.ast.expression_sequence(logical_expr.span, vec);
return Some(sequence_expr);
} else if let Expression::LogicalExpression(left_child) = &mut logical_expr.left {
if left_child.operator == logical_expr.operator {
Expand All @@ -639,9 +637,9 @@ impl<'a> FoldConstants<'a> {
if !right_boolean && left_child_op == LogicalOperator::Or
|| right_boolean && left_child_op == LogicalOperator::And
{
let left = self.ast.move_expression(&mut left_child.left);
let right = self.ast.move_expression(&mut logical_expr.right);
let logic_expr = self.ast.expression_logical(
let left = ctx.ast.move_expression(&mut left_child.left);
let right = ctx.ast.move_expression(&mut logical_expr.right);
let logic_expr = ctx.ast.expression_logical(
logical_expr.span,
left,
left_child_op,
Expand Down
30 changes: 16 additions & 14 deletions crates/oxc_minifier/src/ast_passes/minimize_conditions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use oxc_ast::{ast::*, AstBuilder};
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};

use crate::{node_util::NodeUtil, tri::Tri, CompressorPass};
Expand All @@ -10,27 +10,25 @@ use crate::{node_util::NodeUtil, tri::Tri, CompressorPass};
/// with `? :` and short-circuit binary operators.
///
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
pub struct MinimizeConditions<'a> {
ast: AstBuilder<'a>,
}
pub struct MinimizeConditions;

impl<'a> CompressorPass<'a> for MinimizeConditions<'a> {}
impl<'a> CompressorPass<'a> for MinimizeConditions {}

impl<'a> Traverse<'a> for MinimizeConditions<'a> {
impl<'a> Traverse<'a> for MinimizeConditions {
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.fold_expression(expr, ctx);
}
}

impl<'a> MinimizeConditions<'a> {
pub fn new(ast: AstBuilder<'a>) -> Self {
Self { ast }
impl<'a> MinimizeConditions {
pub fn new() -> Self {
Self
}

fn fold_expression(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(folded_expr) = match expr {
Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx),
Expression::UnaryExpression(e) if e.operator.is_not() => self.try_minimize_not(e),
Expression::UnaryExpression(e) if e.operator.is_not() => self.try_minimize_not(e, ctx),
_ => None,
} {
*expr = folded_expr;
Expand All @@ -49,20 +47,24 @@ impl<'a> MinimizeConditions<'a> {
if parent.is_tagged_template_expression() || parent.is_call_expression() {
return None;
}
Some(self.ast.move_expression(&mut expr.consequent))
Some(ctx.ast.move_expression(&mut expr.consequent))
}
Tri::False => Some(self.ast.move_expression(&mut expr.alternate)),
Tri::False => Some(ctx.ast.move_expression(&mut expr.alternate)),
Tri::Unknown => None,
}
}

/// Try to minimize NOT nodes such as `!(x==y)`.
fn try_minimize_not(&self, expr: &mut UnaryExpression<'a>) -> Option<Expression<'a>> {
fn try_minimize_not(
&self,
expr: &mut UnaryExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
debug_assert!(expr.operator.is_not());
if let Expression::BinaryExpression(binary_expr) = &mut expr.argument {
if let Some(new_op) = binary_expr.operator.equality_inverse_operator() {
binary_expr.operator = new_op;
return Some(self.ast.move_expression(&mut expr.argument));
return Some(ctx.ast.move_expression(&mut expr.argument));
}
}
None
Expand Down
Loading