mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(minifier): replace self.ast with ctx.ast (#5748)
This commit is contained in:
parent
6bc13f6cd4
commit
9a9d8f61d4
8 changed files with 127 additions and 124 deletions
|
|
@ -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};
|
||||
|
|
@ -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![];
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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() => {
|
||||
|
|
@ -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>(
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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>(
|
||||
|
|
@ -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)
|
||||
|
|
@ -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>,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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)``;`
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder, Visit};
|
||||
use oxc_ast::{ast::*, Visit};
|
||||
use oxc_span::SPAN;
|
||||
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`.
|
||||
///
|
||||
/// See `KeepVar` at the end of this file for `var` hoisting logic.
|
||||
pub struct RemoveDeadCode<'a> {
|
||||
ast: AstBuilder<'a>,
|
||||
}
|
||||
pub struct RemoveDeadCode;
|
||||
|
||||
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>) {
|
||||
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(_)));
|
||||
self.dead_code_elimination(stmts);
|
||||
self.dead_code_elimination(stmts, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveDeadCode<'a> {
|
||||
pub fn new(ast: AstBuilder<'a>) -> Self {
|
||||
Self { ast }
|
||||
impl<'a> RemoveDeadCode {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// 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
|
||||
let mut index = None;
|
||||
'outer: for (i, stmt) in stmts.iter().enumerate() {
|
||||
|
|
@ -58,7 +60,7 @@ impl<'a> RemoveDeadCode<'a> {
|
|||
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) {
|
||||
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 };
|
||||
|
||||
// Descend and remove `else` blocks first.
|
||||
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_stmt.alternate = None;
|
||||
}
|
||||
|
|
@ -95,18 +97,18 @@ impl<'a> RemoveDeadCode<'a> {
|
|||
|
||||
match ctx.get_boolean_value(&if_stmt.test) {
|
||||
Tri::True => {
|
||||
*stmt = self.ast.move_statement(&mut if_stmt.consequent);
|
||||
*stmt = ctx.ast.move_statement(&mut if_stmt.consequent);
|
||||
}
|
||||
Tri::False => {
|
||||
*stmt = if let Some(alternate) = &mut if_stmt.alternate {
|
||||
self.ast.move_statement(alternate)
|
||||
ctx.ast.move_statement(alternate)
|
||||
} else {
|
||||
// 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
|
||||
.get_variable_declaration_statement()
|
||||
.unwrap_or_else(|| self.ast.statement_empty(SPAN))
|
||||
.unwrap_or_else(|| ctx.ast.statement_empty(SPAN))
|
||||
};
|
||||
}
|
||||
Tri::Unknown => {}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -9,14 +9,13 @@ use crate::{CompressOptions, CompressorPass};
|
|||
/// * Parenthesized Expression
|
||||
/// * `debugger`
|
||||
/// * `console.log`
|
||||
pub struct RemoveSyntax<'a> {
|
||||
ast: AstBuilder<'a>,
|
||||
pub struct RemoveSyntax {
|
||||
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>) {
|
||||
stmts.retain(|stmt| {
|
||||
!(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>) {
|
||||
self.strip_parenthesized_expression(expr);
|
||||
self.compress_console(expr);
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::strip_parenthesized_expression(expr, ctx);
|
||||
self.compress_console(expr, ctx);
|
||||
}
|
||||
|
||||
fn exit_arrow_function_expression(
|
||||
|
|
@ -39,15 +38,15 @@ impl<'a> Traverse<'a> for RemoveSyntax<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSyntax<'a> {
|
||||
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self {
|
||||
Self { ast, options }
|
||||
impl<'a> RemoveSyntax {
|
||||
pub fn new(options: CompressOptions) -> Self {
|
||||
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 {
|
||||
*expr = self.ast.move_expression(&mut paren_expr.expression);
|
||||
self.strip_parenthesized_expression(expr);
|
||||
*expr = ctx.ast.move_expression(&mut paren_expr.expression);
|
||||
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))
|
||||
}
|
||||
|
||||
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) {
|
||||
*expr = self.ast.void_0();
|
||||
*expr = ctx.ast.void_0();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::{
|
||||
number::NumberBase,
|
||||
|
|
@ -11,15 +11,14 @@ use crate::{node_util::NodeUtil, CompressOptions, CompressorPass};
|
|||
/// A peephole optimization that minimizes code by simplifying conditional
|
||||
/// expressions, replacing IFs with HOOKs, replacing object constructors
|
||||
/// with literals, and simplifying returns.
|
||||
pub struct SubstituteAlternateSyntax<'a> {
|
||||
ast: AstBuilder<'a>,
|
||||
pub struct SubstituteAlternateSyntax {
|
||||
options: CompressOptions,
|
||||
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>) {
|
||||
self.compress_block(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>) {
|
||||
if !self.compress_undefined(expr, ctx) {
|
||||
self.compress_boolean(expr);
|
||||
self.compress_boolean(expr, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_binary_expression(
|
||||
&mut self,
|
||||
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> {
|
||||
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self {
|
||||
Self { ast, options, in_define_export: false }
|
||||
impl<'a> SubstituteAlternateSyntax {
|
||||
pub fn new(options: CompressOptions) -> Self {
|
||||
Self { options, in_define_export: false }
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
|
@ -92,7 +91,7 @@ impl<'a> SubstituteAlternateSyntax<'a> {
|
|||
/// Transforms `undefined` => `void 0`
|
||||
fn compress_undefined(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) -> bool {
|
||||
if ctx.is_expression_undefined(expr) {
|
||||
*expr = self.ast.void_0();
|
||||
*expr = ctx.ast.void_0();
|
||||
return true;
|
||||
};
|
||||
false
|
||||
|
|
@ -137,10 +136,10 @@ impl<'a> SubstituteAlternateSyntax<'a> {
|
|||
// fn compress_while(&mut self, stmt: &mut Statement<'a>) {
|
||||
// let Statement::WhileStatement(while_stmt) = stmt else { return };
|
||||
// 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 body = self.ast.move_statement(&mut while_stmt.body);
|
||||
// *stmt = self.ast.statement_for(SPAN, None, Some(test), None, body);
|
||||
// let body = ctx.ast.move_statement(&mut while_stmt.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`.
|
||||
/// Enabled by `compress.booleans`.
|
||||
/// 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 };
|
||||
if self.options.booleans && !self.in_define_export {
|
||||
let num = self.ast.expression_numeric_literal(
|
||||
let num = ctx.ast.expression_numeric_literal(
|
||||
SPAN,
|
||||
if lit.value { 0.0 } else { 1.0 },
|
||||
if lit.value { "0" } else { "1" },
|
||||
NumberBase::Decimal,
|
||||
);
|
||||
*expr = self.ast.expression_unary(SPAN, UnaryOperator::LogicalNot, num);
|
||||
*expr = ctx.ast.expression_unary(SPAN, UnaryOperator::LogicalNot, num);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
|
|
@ -166,7 +165,11 @@ impl<'a> SubstituteAlternateSyntax<'a> {
|
|||
|
||||
/// Compress `typeof foo == "undefined"` into `typeof foo > "u"`
|
||||
/// 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 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -190,14 +193,14 @@ impl<'a> SubstituteAlternateSyntax<'a> {
|
|||
let Some((_void_exp, id_ref)) = pair else {
|
||||
return;
|
||||
};
|
||||
let argument = self.ast.expression_from_identifier_reference(id_ref);
|
||||
let left = self.ast.unary_expression(SPAN, UnaryOperator::Typeof, argument);
|
||||
let right = self.ast.string_literal(SPAN, "u");
|
||||
let binary_expr = self.ast.binary_expression(
|
||||
let argument = ctx.ast.expression_from_identifier_reference(id_ref);
|
||||
let left = ctx.ast.unary_expression(SPAN, UnaryOperator::Typeof, argument);
|
||||
let right = ctx.ast.string_literal(SPAN, "u");
|
||||
let binary_expr = ctx.ast.binary_expression(
|
||||
expr.span,
|
||||
self.ast.expression_from_unary(left),
|
||||
ctx.ast.expression_from_unary(left),
|
||||
BinaryOperator::GreaterThan,
|
||||
self.ast.expression_from_string_literal(right),
|
||||
ctx.ast.expression_from_string_literal(right),
|
||||
);
|
||||
*expr = binary_expr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,37 +47,37 @@ impl<'a> Compressor<'a> {
|
|||
|
||||
fn remove_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
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>) {
|
||||
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>) {
|
||||
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>) {
|
||||
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>) {
|
||||
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>) {
|
||||
if self.options.collapse {
|
||||
Collapse::new(ctx.ast, self.options).build(program, ctx);
|
||||
Collapse::new(self.options).build(program, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#![allow(clippy::wildcard_imports)]
|
||||
#![allow(clippy::wildcard_imports, clippy::new_without_default, clippy::unused_self)]
|
||||
|
||||
//! ECMAScript Minifier
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue