mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
fix(minifier): do not convert while to fors in DCE (#8484)
This commit is contained in:
parent
7eb6ccde66
commit
4c6675c46d
5 changed files with 36 additions and 18 deletions
|
|
@ -20,7 +20,7 @@ pub use collapse_variable_declarations::CollapseVariableDeclarations;
|
||||||
pub use convert_to_dotted_properties::ConvertToDottedProperties;
|
pub use convert_to_dotted_properties::ConvertToDottedProperties;
|
||||||
pub use exploit_assigns::ExploitAssigns;
|
pub use exploit_assigns::ExploitAssigns;
|
||||||
pub use minimize_exit_points::MinimizeExitPoints;
|
pub use minimize_exit_points::MinimizeExitPoints;
|
||||||
pub use normalize::Normalize;
|
pub use normalize::{Normalize, NormalizeOptions};
|
||||||
pub use peephole_fold_constants::PeepholeFoldConstants;
|
pub use peephole_fold_constants::PeepholeFoldConstants;
|
||||||
pub use peephole_minimize_conditions::PeepholeMinimizeConditions;
|
pub use peephole_minimize_conditions::PeepholeMinimizeConditions;
|
||||||
pub use peephole_remove_dead_code::PeepholeRemoveDeadCode;
|
pub use peephole_remove_dead_code::PeepholeRemoveDeadCode;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,11 @@ use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, Travers
|
||||||
|
|
||||||
use crate::{ctx::Ctx, CompressOptions, CompressorPass};
|
use crate::{ctx::Ctx, CompressOptions, CompressorPass};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NormalizeOptions {
|
||||||
|
pub convert_while_to_fors: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Normalize AST
|
/// Normalize AST
|
||||||
///
|
///
|
||||||
/// Make subsequent AST passes easier to analyze:
|
/// Make subsequent AST passes easier to analyze:
|
||||||
|
|
@ -24,7 +29,8 @@ use crate::{ctx::Ctx, CompressOptions, CompressorPass};
|
||||||
///
|
///
|
||||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/Normalize.java>
|
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/Normalize.java>
|
||||||
pub struct Normalize {
|
pub struct Normalize {
|
||||||
options: CompressOptions,
|
options: NormalizeOptions,
|
||||||
|
compress_options: CompressOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CompressorPass<'a> for Normalize {
|
impl<'a> CompressorPass<'a> for Normalize {
|
||||||
|
|
@ -44,10 +50,10 @@ impl<'a> Traverse<'a> for Normalize {
|
||||||
|
|
||||||
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::WhileStatement(_) => {
|
Statement::IfStatement(s) => Self::wrap_to_avoid_ambiguous_else(s, ctx),
|
||||||
|
Statement::WhileStatement(_) if self.options.convert_while_to_fors => {
|
||||||
Self::convert_while_to_for(stmt, ctx);
|
Self::convert_while_to_for(stmt, ctx);
|
||||||
}
|
}
|
||||||
Statement::IfStatement(s) => Self::wrap_to_avoid_ambiguous_else(s, ctx),
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +72,7 @@ impl<'a> Traverse<'a> for Normalize {
|
||||||
Expression::ArrowFunctionExpression(e) => {
|
Expression::ArrowFunctionExpression(e) => {
|
||||||
self.recover_arrow_expression_after_drop_console(e);
|
self.recover_arrow_expression_after_drop_console(e);
|
||||||
}
|
}
|
||||||
Expression::CallExpression(_) if self.options.drop_console => {
|
Expression::CallExpression(_) if self.compress_options.drop_console => {
|
||||||
self.compress_console(expr, ctx);
|
self.compress_console(expr, ctx);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -75,31 +81,31 @@ impl<'a> Traverse<'a> for Normalize {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Normalize {
|
impl<'a> Normalize {
|
||||||
pub fn new(options: CompressOptions) -> Self {
|
pub fn new(options: NormalizeOptions, compress_options: CompressOptions) -> Self {
|
||||||
Self { options }
|
Self { options, compress_options }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drop `drop_debugger` statement.
|
/// Drop `drop_debugger` statement.
|
||||||
///
|
///
|
||||||
/// Enabled by `compress.drop_debugger`
|
/// Enabled by `compress.drop_debugger`
|
||||||
fn drop_debugger(&mut self, stmt: &Statement<'a>) -> bool {
|
fn drop_debugger(&mut self, stmt: &Statement<'a>) -> bool {
|
||||||
matches!(stmt, Statement::DebuggerStatement(_)) && self.options.drop_debugger
|
matches!(stmt, Statement::DebuggerStatement(_)) && self.compress_options.drop_debugger
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compress_console(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn compress_console(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
debug_assert!(self.options.drop_console);
|
debug_assert!(self.compress_options.drop_console);
|
||||||
if Self::is_console(expr) {
|
if Self::is_console(expr) {
|
||||||
*expr = ctx.ast.void_0(expr.span());
|
*expr = ctx.ast.void_0(expr.span());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_console(&mut self, stmt: &Statement<'a>) -> bool {
|
fn drop_console(&mut self, stmt: &Statement<'a>) -> bool {
|
||||||
self.options.drop_console
|
self.compress_options.drop_console
|
||||||
&& matches!(stmt, Statement::ExpressionStatement(expr) if Self::is_console(&expr.expression))
|
&& matches!(stmt, Statement::ExpressionStatement(expr) if Self::is_console(&expr.expression))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recover_arrow_expression_after_drop_console(&self, expr: &mut ArrowFunctionExpression<'a>) {
|
fn recover_arrow_expression_after_drop_console(&self, expr: &mut ArrowFunctionExpression<'a>) {
|
||||||
if self.options.drop_console && expr.expression && expr.body.is_empty() {
|
if self.compress_options.drop_console && expr.expression && expr.body.is_empty() {
|
||||||
expr.expression = false;
|
expr.expression = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,16 +177,18 @@ impl<'a> Normalize {
|
||||||
mod test {
|
mod test {
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
|
|
||||||
|
use super::NormalizeOptions;
|
||||||
use crate::{tester, CompressOptions};
|
use crate::{tester, CompressOptions};
|
||||||
|
|
||||||
fn test(source_text: &str, expected: &str) {
|
fn test(source_text: &str, expected: &str) {
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
let options = CompressOptions {
|
let compress_options = CompressOptions {
|
||||||
drop_debugger: true,
|
drop_debugger: true,
|
||||||
drop_console: true,
|
drop_console: true,
|
||||||
..CompressOptions::default()
|
..CompressOptions::default()
|
||||||
};
|
};
|
||||||
let mut pass = super::Normalize::new(options);
|
let options = NormalizeOptions { convert_while_to_fors: true };
|
||||||
|
let mut pass = super::Normalize::new(options, compress_options);
|
||||||
tester::test(&allocator, source_text, expected, &mut pass);
|
tester::test(&allocator, source_text, expected, &mut pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use oxc_semantic::{ScopeTree, SemanticBuilder, SymbolTable};
|
||||||
use oxc_traverse::ReusableTraverseCtx;
|
use oxc_traverse::ReusableTraverseCtx;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast_passes::{DeadCodeElimination, Normalize, PeepholeOptimizations},
|
ast_passes::{DeadCodeElimination, Normalize, NormalizeOptions, PeepholeOptimizations},
|
||||||
CompressOptions, CompressorPass,
|
CompressOptions, CompressorPass,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -32,7 +32,8 @@ impl<'a> Compressor<'a> {
|
||||||
) {
|
) {
|
||||||
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator);
|
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator);
|
||||||
// RemoveUnusedCode::new(self.options).build(program, &mut ctx);
|
// RemoveUnusedCode::new(self.options).build(program, &mut ctx);
|
||||||
Normalize::new(self.options).build(program, &mut ctx);
|
let normalize_options = NormalizeOptions { convert_while_to_fors: true };
|
||||||
|
Normalize::new(normalize_options, self.options).build(program, &mut ctx);
|
||||||
PeepholeOptimizations::new(self.options.target, true, self.options)
|
PeepholeOptimizations::new(self.options.target, true, self.options)
|
||||||
.run_in_loop(program, &mut ctx);
|
.run_in_loop(program, &mut ctx);
|
||||||
PeepholeOptimizations::new(self.options.target, false, self.options)
|
PeepholeOptimizations::new(self.options.target, false, self.options)
|
||||||
|
|
@ -52,7 +53,8 @@ impl<'a> Compressor<'a> {
|
||||||
program: &mut Program<'a>,
|
program: &mut Program<'a>,
|
||||||
) {
|
) {
|
||||||
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator);
|
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator);
|
||||||
Normalize::new(self.options).build(program, &mut ctx);
|
let normalize_options = NormalizeOptions { convert_while_to_fors: false };
|
||||||
|
Normalize::new(normalize_options, self.options).build(program, &mut ctx);
|
||||||
DeadCodeElimination::new().build(program, &mut ctx);
|
DeadCodeElimination::new().build(program, &mut ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use oxc_span::SourceType;
|
||||||
use oxc_traverse::ReusableTraverseCtx;
|
use oxc_traverse::ReusableTraverseCtx;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast_passes::{CompressorPass, Normalize},
|
ast_passes::{CompressorPass, Normalize, NormalizeOptions},
|
||||||
CompressOptions,
|
CompressOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -44,7 +44,9 @@ fn run<'a, P: CompressorPass<'a>>(
|
||||||
let (symbols, scopes) =
|
let (symbols, scopes) =
|
||||||
SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree();
|
SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree();
|
||||||
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, allocator);
|
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, allocator);
|
||||||
Normalize::new(CompressOptions::all_false()).build(&mut program, &mut ctx);
|
let normalize_options = NormalizeOptions { convert_while_to_fors: true };
|
||||||
|
Normalize::new(normalize_options, CompressOptions::all_false())
|
||||||
|
.build(&mut program, &mut ctx);
|
||||||
pass.build(&mut program, &mut ctx);
|
pass.build(&mut program, &mut ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,12 @@ fn dce_if_statement() {
|
||||||
test("if (typeof 1 === 'string') { REMOVE; }", "");
|
test("if (typeof 1 === 'string') { REMOVE; }", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dce_while_statement() {
|
||||||
|
test_same("while (true);");
|
||||||
|
test_same("while (false);");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dce_conditional_expression() {
|
fn dce_conditional_expression() {
|
||||||
test("false ? foo : bar;", "bar");
|
test("false ? foo : bar;", "bar");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue