feat(minifier): compress a != null ? a.b : undefined to a?.b (#8802) (#8808)

feat(minifier): compress `a != null ? a.b : undefined` to `a?.b` (#8802)

refactor(minifier): some clean ups
This commit is contained in:
Boshen 2025-01-31 09:01:20 +00:00
parent d8fac6d76b
commit e525e60e5b
4 changed files with 10 additions and 58 deletions

View file

@ -1,30 +1,3 @@
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use crate::ctx::Ctx;
use super::PeepholeOptimizations;
impl<'a> PeepholeOptimizations {
/// Transform the structure of the AST so that the number of explicit exits
/// are minimized and instead flows to implicit exits conditions.
///
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/MinimizeExitPoints.java>
pub fn minimize_exit_points(&mut self, body: &mut FunctionBody<'_>, _ctx: Ctx<'a, '_>) {
self.remove_last_return(&mut body.statements);
}
// `function foo() { return }` -> `function foo() {}`
fn remove_last_return(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
if let Some(last) = stmts.last() {
if matches!(last, Statement::ReturnStatement(ret) if ret.argument.is_none()) {
stmts.pop();
self.mark_current_function_as_changed();
}
}
}
}
#[cfg(test)]
mod test {
use crate::tester::{test, test_same};

View file

@ -16,14 +16,15 @@ impl<'a> PeepholeOptimizations {
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/StatementFusion.java>
///
/// ## Collapse variable declarations
///
/// ## Join Vars
/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java>
///
/// ## Collapse into for statements:
/// `var a = 0; for(;a<0;a++) {}` => `for(var a = 0;a<0;a++) {}`
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/Denormalize.java>
///
/// ## MinimizeExitPoints:
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/MinimizeExitPoints.java>
pub fn minimize_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: Ctx<'a, '_>) {
let mut result: Vec<'a, Statement<'a>> = ctx.ast.vec_with_capacity(stmts.len());
let mut is_control_flow_dead = false;

View file

@ -159,14 +159,6 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
self.substitute_return_statement(stmt, ctx);
}
fn exit_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
if !self.is_prev_function_changed() {
return;
}
let ctx = Ctx(ctx);
self.minimize_exit_points(body, ctx);
}
fn exit_variable_declaration(
&mut self,
decl: &mut VariableDeclaration<'a>,

View file

@ -18,14 +18,6 @@ use super::{LatePeepholeOptimizations, PeepholeOptimizations};
/// See `KeepVar` at the end of this file for `var` hoisting logic.
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java>
impl<'a, 'b> PeepholeOptimizations {
pub fn remove_dead_code_exit_statements(
&mut self,
stmts: &mut Vec<'a, Statement<'a>>,
ctx: Ctx<'a, '_>,
) {
self.dead_code_elimination(stmts, ctx);
}
pub fn remove_dead_code_exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: Ctx<'a, '_>) {
if let Some(new_stmt) = match stmt {
Statement::BlockStatement(s) => Self::try_optimize_block(s, ctx),
@ -66,18 +58,22 @@ impl<'a, 'b> PeepholeOptimizations {
}
/// Removes dead code thats comes after `return`, `throw`, `continue` and `break` statements.
fn dead_code_elimination(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: Ctx<'a, 'b>) {
pub fn remove_dead_code_exit_statements(
&mut self,
stmts: &mut Vec<'a, Statement<'a>>,
ctx: Ctx<'a, '_>,
) {
// Remove code after `return` and `throw` statements
let mut index = None;
'outer: for (i, stmt) in stmts.iter().enumerate() {
if Self::is_unreachable_statement(stmt) {
if stmt.is_jump_statement() {
index.replace(i);
break;
}
// Double check block statements folded by if statements above
if let Statement::BlockStatement(block_stmt) = stmt {
for stmt in &block_stmt.body {
if Self::is_unreachable_statement(stmt) {
if stmt.is_jump_statement() {
index.replace(i);
break 'outer;
}
@ -125,16 +121,6 @@ impl<'a, 'b> PeepholeOptimizations {
}
}
fn is_unreachable_statement(stmt: &Statement<'a>) -> bool {
matches!(
stmt,
Statement::ReturnStatement(_)
| Statement::ThrowStatement(_)
| Statement::BreakStatement(_)
| Statement::ContinueStatement(_)
)
}
/// Remove block from single line blocks
/// `{ block } -> block`
fn try_optimize_block(