refactor(minifier): replace self.ast with ctx.ast (#5748)

This commit is contained in:
Boshen 2024-09-13 08:49:45 +00:00
parent 6bc13f6cd4
commit 9a9d8f61d4
8 changed files with 127 additions and 124 deletions

View file

@ -1,5 +1,5 @@
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx}; use oxc_traverse::{Traverse, TraverseCtx};
use crate::{CompressOptions, CompressorPass}; use crate::{CompressOptions, CompressorPass};
@ -8,28 +8,27 @@ use crate::{CompressOptions, CompressorPass};
/// ///
/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2` /// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
/// TODO: `a = null; b = null;` => `a = b = null` /// TODO: `a = null; b = null;` => `a = b = null`
pub struct Collapse<'a> { pub struct Collapse {
ast: AstBuilder<'a>,
options: CompressOptions, options: CompressOptions,
} }
impl<'a> CompressorPass<'a> for Collapse<'a> {} impl<'a> CompressorPass<'a> for Collapse {}
impl<'a> Traverse<'a> for Collapse<'a> { impl<'a> Traverse<'a> for Collapse {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
if self.options.join_vars { if self.options.join_vars {
self.join_vars(stmts); self.join_vars(stmts, ctx);
} }
} }
} }
impl<'a> Collapse<'a> { impl<'a> Collapse {
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self { pub fn new(options: CompressOptions) -> Self {
Self { ast, options } Self { options }
} }
/// Join consecutive var statements /// 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. // Collect all the consecutive ranges that contain joinable vars.
// This is required because Rust prevents in-place vec mutation. // This is required because Rust prevents in-place vec mutation.
let mut ranges = vec![]; let mut ranges = vec![];
@ -72,7 +71,7 @@ impl<'a> Collapse<'a> {
} }
// Reconstruct the stmts array by joining consecutive ranges // 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() { for (i, stmt) in stmts.drain(..).enumerate() {
if i > 0 && ranges.iter().any(|range| range.contains(&(i - 1)) && range.contains(&i)) { 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() { if let Statement::VariableDeclaration(prev_decl) = new_stmts.last_mut().unwrap() {

View file

@ -1,7 +1,7 @@
use std::{cmp::Ordering, mem}; use std::{cmp::Ordering, mem};
use num_bigint::BigInt; use num_bigint::BigInt;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::ast::*;
use oxc_span::{GetSpan, Span, SPAN}; use oxc_span::{GetSpan, Span, SPAN};
use oxc_syntax::{ use oxc_syntax::{
number::NumberBase, number::NumberBase,
@ -19,22 +19,21 @@ use crate::{
/// Constant Folding /// Constant Folding
/// ///
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java> /// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
pub struct FoldConstants<'a> { pub struct FoldConstants {
ast: AstBuilder<'a>,
evaluate: bool, 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>) { fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.fold_expression(expr, ctx); self.fold_expression(expr, ctx);
} }
} }
impl<'a> FoldConstants<'a> { impl<'a> FoldConstants {
pub fn new(ast: AstBuilder<'a>) -> Self { pub fn new() -> Self {
Self { ast, evaluate: false } Self { evaluate: false }
} }
pub fn with_evaluate(mut self, yes: bool) -> Self { pub fn with_evaluate(mut self, yes: bool) -> Self {
@ -110,7 +109,7 @@ impl<'a> FoldConstants<'a> {
UnaryOperator::Void => Self::try_reduce_void(expr, ctx), UnaryOperator::Void => Self::try_reduce_void(expr, ctx),
UnaryOperator::Typeof => self.try_fold_type_of(expr, ctx), UnaryOperator::Typeof => self.try_fold_type_of(expr, ctx),
UnaryOperator::LogicalNot => { 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` // `-NaN` -> `NaN`
UnaryOperator::UnaryNegation if expr.argument.is_nan() => { UnaryOperator::UnaryNegation if expr.argument.is_nan() => {
@ -161,7 +160,7 @@ impl<'a> FoldConstants<'a> {
Expression::Identifier(ident) if ctx.is_identifier_undefined(ident) => "undefined", Expression::Identifier(ident) if ctx.is_identifier_undefined(ident) => "undefined",
_ => return None, _ => return None,
}; };
Some(self.ast.expression_string_literal(SPAN, s)) Some(ctx.ast.expression_string_literal(SPAN, s))
} }
fn try_fold_addition<'b>( fn try_fold_addition<'b>(
@ -189,7 +188,7 @@ impl<'a> FoldConstants<'a> {
let right_string = ctx.get_string_value(right)?; let right_string = ctx.get_string_value(right)?;
// let value = left_string.to_owned(). // let value = left_string.to_owned().
let value = left_string + right_string; let value = left_string + right_string;
Some(self.ast.expression_string_literal(span, value)) Some(ctx.ast.expression_string_literal(span, value))
}, },
// number addition // number addition
@ -202,7 +201,7 @@ impl<'a> FoldConstants<'a> {
// Float if value has a fractional part, otherwise Decimal // Float if value has a fractional part, otherwise Decimal
let number_base = if is_exact_int64(value) { NumberBase::Decimal } else { NumberBase::Float }; let number_base = if is_exact_int64(value) { NumberBase::Decimal } else { NumberBase::Float };
// todo: add raw &str // 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 _ => None
} }
@ -221,7 +220,7 @@ impl<'a> FoldConstants<'a> {
Tri::False => false, Tri::False => false,
Tri::Unknown => return None, Tri::Unknown => return None,
}; };
Some(self.ast.expression_boolean_literal(span, value)) Some(ctx.ast.expression_boolean_literal(span, value))
} }
fn evaluate_comparison<'b>( fn evaluate_comparison<'b>(
@ -235,9 +234,9 @@ impl<'a> FoldConstants<'a> {
return Tri::Unknown; return Tri::Unknown;
} }
match op { match op {
BinaryOperator::Equality => self.try_abstract_equality_comparison(left, right, ctx), BinaryOperator::Equality => Self::try_abstract_equality_comparison(left, right, ctx),
BinaryOperator::Inequality => { BinaryOperator::Inequality => {
self.try_abstract_equality_comparison(left, right, ctx).not() Self::try_abstract_equality_comparison(left, right, ctx).not()
} }
BinaryOperator::StrictEquality => { BinaryOperator::StrictEquality => {
Self::try_strict_equality_comparison(left, right, ctx) Self::try_strict_equality_comparison(left, right, ctx)
@ -263,7 +262,6 @@ impl<'a> FoldConstants<'a> {
/// <https://tc39.es/ecma262/#sec-abstract-equality-comparison> /// <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
fn try_abstract_equality_comparison<'b>( fn try_abstract_equality_comparison<'b>(
&self,
left_expr: &'b Expression<'a>, left_expr: &'b Expression<'a>,
right_expr: &'b Expression<'a>, right_expr: &'b Expression<'a>,
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
@ -282,14 +280,14 @@ impl<'a> FoldConstants<'a> {
let right_number = ctx.get_side_free_number_value(right_expr); let right_number = ctx.get_side_free_number_value(right_expr);
if let Some(NumberValue::Number(num)) = right_number { 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(), right_expr.span(),
num, num,
num.to_string(), num.to_string(),
if num.fract() == 0.0 { NumberBase::Decimal } else { NumberBase::Float }, if num.fract() == 0.0 { NumberBase::Decimal } else { NumberBase::Float },
); );
return self.try_abstract_equality_comparison( return Self::try_abstract_equality_comparison(
left_expr, left_expr,
&number_literal_expr, &number_literal_expr,
ctx, ctx,
@ -303,14 +301,14 @@ impl<'a> FoldConstants<'a> {
let left_number = ctx.get_side_free_number_value(left_expr); let left_number = ctx.get_side_free_number_value(left_expr);
if let Some(NumberValue::Number(num)) = left_number { 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(), left_expr.span(),
num, num,
num.to_string(), num.to_string(),
if num.fract() == 0.0 { NumberBase::Decimal } else { NumberBase::Float }, 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, &number_literal_expr,
right_expr, right_expr,
ctx, ctx,
@ -567,7 +565,7 @@ impl<'a> FoldConstants<'a> {
_ => unreachable!("Unknown binary operator {:?}", op), _ => unreachable!("Unknown binary operator {:?}", op),
}; };
return Some(self.ast.expression_numeric_literal( return Some(ctx.ast.expression_numeric_literal(
span, span,
result_val, result_val,
result_val.to_string(), result_val.to_string(),
@ -607,7 +605,7 @@ impl<'a> FoldConstants<'a> {
// (TRUE || x) => TRUE (also, (3 || x) => 3) // (TRUE || x) => TRUE (also, (3 || x) => 3)
// (FALSE && x) => FALSE // (FALSE && x) => FALSE
if if lval { op == LogicalOperator::Or } else { op == LogicalOperator::And } { 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() { } else if !left.may_have_side_effects() {
let parent = ctx.ancestry.parent(); let parent = ctx.ancestry.parent();
// Bail `let o = { f() { assert.ok(this !== o); } }; (true && o.f)(); (true && o.f)``;` // Bail `let o = { f() { assert.ok(this !== o); } }; (true && o.f)(); (true && o.f)``;`
@ -616,17 +614,17 @@ impl<'a> FoldConstants<'a> {
} }
// (FALSE || x) => x // (FALSE || x) => x
// (TRUE && 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. // Left side may have side effects, but we know its boolean value.
// e.g. true_with_sideeffects || foo() => true_with_sideeffects, foo() // e.g. true_with_sideeffects || foo() => true_with_sideeffects, foo()
// or: false_with_sideeffects && foo() => false_with_sideeffects, foo() // or: false_with_sideeffects && foo() => false_with_sideeffects, foo()
let left = self.ast.move_expression(&mut logical_expr.left); let left = ctx.ast.move_expression(&mut logical_expr.left);
let right = self.ast.move_expression(&mut logical_expr.right); let right = ctx.ast.move_expression(&mut logical_expr.right);
let mut vec = self.ast.vec_with_capacity(2); let mut vec = ctx.ast.vec_with_capacity(2);
vec.push(left); vec.push(left);
vec.push(right); 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); return Some(sequence_expr);
} else if let Expression::LogicalExpression(left_child) = &mut logical_expr.left { } else if let Expression::LogicalExpression(left_child) = &mut logical_expr.left {
if left_child.operator == logical_expr.operator { if left_child.operator == logical_expr.operator {
@ -639,9 +637,9 @@ impl<'a> FoldConstants<'a> {
if !right_boolean && left_child_op == LogicalOperator::Or if !right_boolean && left_child_op == LogicalOperator::Or
|| right_boolean && left_child_op == LogicalOperator::And || right_boolean && left_child_op == LogicalOperator::And
{ {
let left = self.ast.move_expression(&mut left_child.left); let left = ctx.ast.move_expression(&mut left_child.left);
let right = self.ast.move_expression(&mut logical_expr.right); let right = ctx.ast.move_expression(&mut logical_expr.right);
let logic_expr = self.ast.expression_logical( let logic_expr = ctx.ast.expression_logical(
logical_expr.span, logical_expr.span,
left, left,
left_child_op, left_child_op,

View file

@ -1,4 +1,4 @@
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx}; use oxc_traverse::{Traverse, TraverseCtx};
use crate::{node_util::NodeUtil, tri::Tri, CompressorPass}; use crate::{node_util::NodeUtil, tri::Tri, CompressorPass};
@ -10,27 +10,25 @@ use crate::{node_util::NodeUtil, tri::Tri, CompressorPass};
/// with `? :` and short-circuit binary operators. /// with `? :` and short-circuit binary operators.
/// ///
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java> /// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
pub struct MinimizeConditions<'a> { pub struct MinimizeConditions;
ast: AstBuilder<'a>,
}
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>) { fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.fold_expression(expr, ctx); self.fold_expression(expr, ctx);
} }
} }
impl<'a> MinimizeConditions<'a> { impl<'a> MinimizeConditions {
pub fn new(ast: AstBuilder<'a>) -> Self { pub fn new() -> Self {
Self { ast } Self
} }
fn fold_expression(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { fn fold_expression(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(folded_expr) = match expr { if let Some(folded_expr) = match expr {
Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx), 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, _ => None,
} { } {
*expr = folded_expr; *expr = folded_expr;
@ -49,20 +47,24 @@ impl<'a> MinimizeConditions<'a> {
if parent.is_tagged_template_expression() || parent.is_call_expression() { if parent.is_tagged_template_expression() || parent.is_call_expression() {
return None; 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, Tri::Unknown => None,
} }
} }
/// Try to minimize NOT nodes such as `!(x==y)`. /// 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()); debug_assert!(expr.operator.is_not());
if let Expression::BinaryExpression(binary_expr) = &mut expr.argument { if let Expression::BinaryExpression(binary_expr) = &mut expr.argument {
if let Some(new_op) = binary_expr.operator.equality_inverse_operator() { if let Some(new_op) = binary_expr.operator.equality_inverse_operator() {
binary_expr.operator = new_op; binary_expr.operator = new_op;
return Some(self.ast.move_expression(&mut expr.argument)); return Some(ctx.ast.move_expression(&mut expr.argument));
} }
} }
None None

View file

@ -1,5 +1,5 @@
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder, Visit}; use oxc_ast::{ast::*, Visit};
use oxc_span::SPAN; use oxc_span::SPAN;
use oxc_traverse::{Traverse, TraverseCtx}; use oxc_traverse::{Traverse, TraverseCtx};
@ -10,30 +10,32 @@ use crate::{keep_var::KeepVar, node_util::NodeUtil, tri::Tri, CompressorPass};
/// Terser option: `dead_code: true`. /// Terser option: `dead_code: true`.
/// ///
/// See `KeepVar` at the end of this file for `var` hoisting logic. /// See `KeepVar` at the end of this file for `var` hoisting logic.
pub struct RemoveDeadCode<'a> { pub struct RemoveDeadCode;
ast: AstBuilder<'a>,
}
impl<'a> CompressorPass<'a> for RemoveDeadCode<'a> {} impl<'a> CompressorPass<'a> for RemoveDeadCode {}
impl<'a> Traverse<'a> for RemoveDeadCode<'a> { impl<'a> Traverse<'a> for RemoveDeadCode {
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.fold_if_statement(stmt, ctx); Self::fold_if_statement(stmt, ctx);
} }
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
self.dead_code_elimination(stmts); self.dead_code_elimination(stmts, ctx);
} }
} }
impl<'a> RemoveDeadCode<'a> { impl<'a> RemoveDeadCode {
pub fn new(ast: AstBuilder<'a>) -> Self { pub fn new() -> Self {
Self { ast } Self {}
} }
/// Removes dead code thats comes after `return` statements after inlining `if` statements /// Removes dead code thats comes after `return` statements after inlining `if` statements
fn dead_code_elimination(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { fn dead_code_elimination(
&mut self,
stmts: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
// Remove code after `return` and `throw` statements // Remove code after `return` and `throw` statements
let mut index = None; let mut index = None;
'outer: for (i, stmt) in stmts.iter().enumerate() { 'outer: for (i, stmt) in stmts.iter().enumerate() {
@ -58,7 +60,7 @@ impl<'a> RemoveDeadCode<'a> {
return; return;
} }
let mut keep_var = KeepVar::new(self.ast); let mut keep_var = KeepVar::new(ctx.ast);
for stmt in stmts.iter().skip(index + 1) { for stmt in stmts.iter().skip(index + 1) {
keep_var.visit_statement(stmt); keep_var.visit_statement(stmt);
@ -82,12 +84,12 @@ impl<'a> RemoveDeadCode<'a> {
} }
} }
fn fold_if_statement(&self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { fn fold_if_statement(stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
let Statement::IfStatement(if_stmt) = stmt else { return }; let Statement::IfStatement(if_stmt) = stmt else { return };
// Descend and remove `else` blocks first. // Descend and remove `else` blocks first.
if let Some(alternate) = &mut if_stmt.alternate { if let Some(alternate) = &mut if_stmt.alternate {
self.fold_if_statement(alternate, ctx); Self::fold_if_statement(alternate, ctx);
if matches!(alternate, Statement::EmptyStatement(_)) { if matches!(alternate, Statement::EmptyStatement(_)) {
if_stmt.alternate = None; if_stmt.alternate = None;
} }
@ -95,18 +97,18 @@ impl<'a> RemoveDeadCode<'a> {
match ctx.get_boolean_value(&if_stmt.test) { match ctx.get_boolean_value(&if_stmt.test) {
Tri::True => { Tri::True => {
*stmt = self.ast.move_statement(&mut if_stmt.consequent); *stmt = ctx.ast.move_statement(&mut if_stmt.consequent);
} }
Tri::False => { Tri::False => {
*stmt = if let Some(alternate) = &mut if_stmt.alternate { *stmt = if let Some(alternate) = &mut if_stmt.alternate {
self.ast.move_statement(alternate) ctx.ast.move_statement(alternate)
} else { } else {
// Keep hoisted `vars` from the consequent block. // Keep hoisted `vars` from the consequent block.
let mut keep_var = KeepVar::new(self.ast); let mut keep_var = KeepVar::new(ctx.ast);
keep_var.visit_statement(&if_stmt.consequent); keep_var.visit_statement(&if_stmt.consequent);
keep_var keep_var
.get_variable_declaration_statement() .get_variable_declaration_statement()
.unwrap_or_else(|| self.ast.statement_empty(SPAN)) .unwrap_or_else(|| ctx.ast.statement_empty(SPAN))
}; };
} }
Tri::Unknown => {} Tri::Unknown => {}

View file

@ -1,5 +1,5 @@
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx}; use oxc_traverse::{Traverse, TraverseCtx};
use crate::{CompressOptions, CompressorPass}; use crate::{CompressOptions, CompressorPass};
@ -9,14 +9,13 @@ use crate::{CompressOptions, CompressorPass};
/// * Parenthesized Expression /// * Parenthesized Expression
/// * `debugger` /// * `debugger`
/// * `console.log` /// * `console.log`
pub struct RemoveSyntax<'a> { pub struct RemoveSyntax {
ast: AstBuilder<'a>,
options: CompressOptions, options: CompressOptions,
} }
impl<'a> CompressorPass<'a> for RemoveSyntax<'a> {} impl<'a> CompressorPass<'a> for RemoveSyntax {}
impl<'a> Traverse<'a> for RemoveSyntax<'a> { impl<'a> Traverse<'a> for RemoveSyntax {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) {
stmts.retain(|stmt| { stmts.retain(|stmt| {
!(matches!(stmt, Statement::EmptyStatement(_)) !(matches!(stmt, Statement::EmptyStatement(_))
@ -25,9 +24,9 @@ impl<'a> Traverse<'a> for RemoveSyntax<'a> {
}); });
} }
fn enter_expression(&mut self, expr: &mut Expression<'a>, _ctx: &mut TraverseCtx<'a>) { fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.strip_parenthesized_expression(expr); Self::strip_parenthesized_expression(expr, ctx);
self.compress_console(expr); self.compress_console(expr, ctx);
} }
fn exit_arrow_function_expression( fn exit_arrow_function_expression(
@ -39,15 +38,15 @@ impl<'a> Traverse<'a> for RemoveSyntax<'a> {
} }
} }
impl<'a> RemoveSyntax<'a> { impl<'a> RemoveSyntax {
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self { pub fn new(options: CompressOptions) -> Self {
Self { ast, options } Self { options }
} }
fn strip_parenthesized_expression(&self, expr: &mut Expression<'a>) { fn strip_parenthesized_expression(expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if let Expression::ParenthesizedExpression(paren_expr) = expr { if let Expression::ParenthesizedExpression(paren_expr) = expr {
*expr = self.ast.move_expression(&mut paren_expr.expression); *expr = ctx.ast.move_expression(&mut paren_expr.expression);
self.strip_parenthesized_expression(expr); Self::strip_parenthesized_expression(expr, ctx);
} }
} }
@ -66,9 +65,9 @@ impl<'a> RemoveSyntax<'a> {
&& matches!(stmt, Statement::ExpressionStatement(expr) if Self::is_console(&expr.expression)) && matches!(stmt, Statement::ExpressionStatement(expr) if Self::is_console(&expr.expression))
} }
fn compress_console(&mut self, expr: &mut Expression<'a>) { fn compress_console(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.drop_console && Self::is_console(expr) { if self.options.drop_console && Self::is_console(expr) {
*expr = self.ast.void_0(); *expr = ctx.ast.void_0();
} }
} }

View file

@ -1,4 +1,4 @@
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::ast::*;
use oxc_span::SPAN; use oxc_span::SPAN;
use oxc_syntax::{ use oxc_syntax::{
number::NumberBase, number::NumberBase,
@ -11,15 +11,14 @@ use crate::{node_util::NodeUtil, CompressOptions, CompressorPass};
/// A peephole optimization that minimizes code by simplifying conditional /// A peephole optimization that minimizes code by simplifying conditional
/// expressions, replacing IFs with HOOKs, replacing object constructors /// expressions, replacing IFs with HOOKs, replacing object constructors
/// with literals, and simplifying returns. /// with literals, and simplifying returns.
pub struct SubstituteAlternateSyntax<'a> { pub struct SubstituteAlternateSyntax {
ast: AstBuilder<'a>,
options: CompressOptions, options: CompressOptions,
in_define_export: bool, in_define_export: bool,
} }
impl<'a> CompressorPass<'a> for SubstituteAlternateSyntax<'a> {} impl<'a> CompressorPass<'a> for SubstituteAlternateSyntax {}
impl<'a> Traverse<'a> for SubstituteAlternateSyntax<'a> { impl<'a> Traverse<'a> for SubstituteAlternateSyntax {
fn enter_statement(&mut self, stmt: &mut Statement<'a>, _ctx: &mut TraverseCtx<'a>) { fn enter_statement(&mut self, stmt: &mut Statement<'a>, _ctx: &mut TraverseCtx<'a>) {
self.compress_block(stmt); self.compress_block(stmt);
// self.compress_while(stmt); // self.compress_while(stmt);
@ -69,22 +68,22 @@ impl<'a> Traverse<'a> for SubstituteAlternateSyntax<'a> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if !self.compress_undefined(expr, ctx) { if !self.compress_undefined(expr, ctx) {
self.compress_boolean(expr); self.compress_boolean(expr, ctx);
} }
} }
fn exit_binary_expression( fn exit_binary_expression(
&mut self, &mut self,
expr: &mut BinaryExpression<'a>, expr: &mut BinaryExpression<'a>,
_ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) { ) {
self.compress_typeof_undefined(expr); self.compress_typeof_undefined(expr, ctx);
} }
} }
impl<'a> SubstituteAlternateSyntax<'a> { impl<'a> SubstituteAlternateSyntax {
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self { pub fn new(options: CompressOptions) -> Self {
Self { ast, options, in_define_export: false } Self { options, in_define_export: false }
} }
/* Utilities */ /* Utilities */
@ -92,7 +91,7 @@ impl<'a> SubstituteAlternateSyntax<'a> {
/// Transforms `undefined` => `void 0` /// Transforms `undefined` => `void 0`
fn compress_undefined(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) -> bool { fn compress_undefined(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) -> bool {
if ctx.is_expression_undefined(expr) { if ctx.is_expression_undefined(expr) {
*expr = self.ast.void_0(); *expr = ctx.ast.void_0();
return true; return true;
}; };
false false
@ -137,10 +136,10 @@ impl<'a> SubstituteAlternateSyntax<'a> {
// fn compress_while(&mut self, stmt: &mut Statement<'a>) { // fn compress_while(&mut self, stmt: &mut Statement<'a>) {
// let Statement::WhileStatement(while_stmt) = stmt else { return }; // let Statement::WhileStatement(while_stmt) = stmt else { return };
// if self.options.loops { // if self.options.loops {
// let dummy_test = self.ast.expression_this(SPAN); // let dummy_test = ctx.ast.expression_this(SPAN);
// let test = std::mem::replace(&mut while_stmt.test, dummy_test); // let test = std::mem::replace(&mut while_stmt.test, dummy_test);
// let body = self.ast.move_statement(&mut while_stmt.body); // let body = ctx.ast.move_statement(&mut while_stmt.body);
// *stmt = self.ast.statement_for(SPAN, None, Some(test), None, body); // *stmt = ctx.ast.statement_for(SPAN, None, Some(test), None, body);
// } // }
// } // }
@ -149,16 +148,16 @@ impl<'a> SubstituteAlternateSyntax<'a> {
/// Transforms boolean expression `true` => `!0` `false` => `!1`. /// Transforms boolean expression `true` => `!0` `false` => `!1`.
/// Enabled by `compress.booleans`. /// Enabled by `compress.booleans`.
/// Do not compress `true` in `Object.defineProperty(exports, 'Foo', {enumerable: true, ...})`. /// Do not compress `true` in `Object.defineProperty(exports, 'Foo', {enumerable: true, ...})`.
fn compress_boolean(&mut self, expr: &mut Expression<'a>) -> bool { fn compress_boolean(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) -> bool {
let Expression::BooleanLiteral(lit) = expr else { return false }; let Expression::BooleanLiteral(lit) = expr else { return false };
if self.options.booleans && !self.in_define_export { if self.options.booleans && !self.in_define_export {
let num = self.ast.expression_numeric_literal( let num = ctx.ast.expression_numeric_literal(
SPAN, SPAN,
if lit.value { 0.0 } else { 1.0 }, if lit.value { 0.0 } else { 1.0 },
if lit.value { "0" } else { "1" }, if lit.value { "0" } else { "1" },
NumberBase::Decimal, NumberBase::Decimal,
); );
*expr = self.ast.expression_unary(SPAN, UnaryOperator::LogicalNot, num); *expr = ctx.ast.expression_unary(SPAN, UnaryOperator::LogicalNot, num);
return true; return true;
} }
false false
@ -166,7 +165,11 @@ impl<'a> SubstituteAlternateSyntax<'a> {
/// Compress `typeof foo == "undefined"` into `typeof foo > "u"` /// Compress `typeof foo == "undefined"` into `typeof foo > "u"`
/// Enabled by `compress.typeofs` /// Enabled by `compress.typeofs`
fn compress_typeof_undefined(&self, expr: &mut BinaryExpression<'a>) { fn compress_typeof_undefined(
&self,
expr: &mut BinaryExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if !self.options.typeofs { if !self.options.typeofs {
return; return;
} }
@ -190,14 +193,14 @@ impl<'a> SubstituteAlternateSyntax<'a> {
let Some((_void_exp, id_ref)) = pair else { let Some((_void_exp, id_ref)) = pair else {
return; return;
}; };
let argument = self.ast.expression_from_identifier_reference(id_ref); let argument = ctx.ast.expression_from_identifier_reference(id_ref);
let left = self.ast.unary_expression(SPAN, UnaryOperator::Typeof, argument); let left = ctx.ast.unary_expression(SPAN, UnaryOperator::Typeof, argument);
let right = self.ast.string_literal(SPAN, "u"); let right = ctx.ast.string_literal(SPAN, "u");
let binary_expr = self.ast.binary_expression( let binary_expr = ctx.ast.binary_expression(
expr.span, expr.span,
self.ast.expression_from_unary(left), ctx.ast.expression_from_unary(left),
BinaryOperator::GreaterThan, BinaryOperator::GreaterThan,
self.ast.expression_from_string_literal(right), ctx.ast.expression_from_string_literal(right),
); );
*expr = binary_expr; *expr = binary_expr;
} }

View file

@ -47,37 +47,37 @@ impl<'a> Compressor<'a> {
fn remove_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn remove_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.remove_syntax { if self.options.remove_syntax {
RemoveSyntax::new(ctx.ast, self.options).build(program, ctx); RemoveSyntax::new(self.options).build(program, ctx);
} }
} }
fn minimize_conditions(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn minimize_conditions(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.minimize_conditions { if self.options.minimize_conditions {
MinimizeConditions::new(ctx.ast).build(program, ctx); MinimizeConditions::new().build(program, ctx);
} }
} }
fn fold_constants(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn fold_constants(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.fold_constants { if self.options.fold_constants {
FoldConstants::new(ctx.ast).with_evaluate(self.options.evaluate).build(program, ctx); FoldConstants::new().with_evaluate(self.options.evaluate).build(program, ctx);
} }
} }
fn substitute_alternate_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn substitute_alternate_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.substitute_alternate_syntax { if self.options.substitute_alternate_syntax {
SubstituteAlternateSyntax::new(ctx.ast, self.options).build(program, ctx); SubstituteAlternateSyntax::new(self.options).build(program, ctx);
} }
} }
fn remove_dead_code(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn remove_dead_code(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.remove_dead_code { if self.options.remove_dead_code {
RemoveDeadCode::new(ctx.ast).build(program, ctx); RemoveDeadCode::new().build(program, ctx);
} }
} }
fn collapse(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn collapse(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.collapse { if self.options.collapse {
Collapse::new(ctx.ast, self.options).build(program, ctx); Collapse::new(self.options).build(program, ctx);
} }
} }
} }

View file

@ -1,4 +1,4 @@
#![allow(clippy::wildcard_imports)] #![allow(clippy::wildcard_imports, clippy::new_without_default, clippy::unused_self)]
//! ECMAScript Minifier //! ECMAScript Minifier