fix(minifier): compute void number as undefined (#6028)

This commit is contained in:
Boshen 2024-09-24 14:39:44 +00:00
parent 539751cd13
commit e0a895962d
5 changed files with 41 additions and 22 deletions

View file

@ -23,11 +23,13 @@ fn main() -> std::io::Result<()> {
let source_text = std::fs::read_to_string(path)?; let source_text = std::fs::read_to_string(path)?;
let source_type = SourceType::from_path(path).unwrap(); let source_type = SourceType::from_path(path).unwrap();
let printed = minify(&source_text, source_type, mangle); let mut allocator = Allocator::default();
let printed = minify(&allocator, &source_text, source_type, mangle);
println!("{printed}"); println!("{printed}");
if twice { if twice {
let printed2 = minify(&printed, source_type, mangle); allocator.reset();
let printed2 = minify(&allocator, &printed, source_type, mangle);
println!("{printed2}"); println!("{printed2}");
println!("same = {}", printed == printed2); println!("same = {}", printed == printed2);
} }
@ -35,11 +37,15 @@ fn main() -> std::io::Result<()> {
Ok(()) Ok(())
} }
fn minify(source_text: &str, source_type: SourceType, mangle: bool) -> String { fn minify(
let allocator = Allocator::default(); allocator: &Allocator,
let ret = Parser::new(&allocator, source_text, source_type).parse(); source_text: &str,
let program = allocator.alloc(ret.program); source_type: SourceType,
let options = MinifierOptions { mangle, compress: CompressOptions::all_true() }; mangle: bool,
let ret = Minifier::new(options).build(&allocator, program); ) -> String {
CodeGenerator::new().with_mangler(ret.mangler).build(program).source_text let ret = Parser::new(allocator, source_text, source_type).parse();
let mut program = ret.program;
let options = MinifierOptions { mangle, compress: CompressOptions::default() };
let ret = Minifier::new(options).build(allocator, &mut program);
CodeGenerator::new().with_mangler(ret.mangler).build(&program).source_text
} }

View file

@ -1,4 +1,4 @@
use std::{cmp::Ordering, mem}; use std::cmp::Ordering;
use num_bigint::BigInt; use num_bigint::BigInt;
use oxc_ast::ast::*; use oxc_ast::ast::*;
@ -105,12 +105,12 @@ impl<'a> PeepholeFoldConstants {
} }
fn try_fold_unary_expression( fn try_fold_unary_expression(
&self, &mut self,
expr: &mut UnaryExpression<'a>, expr: &mut UnaryExpression<'a>,
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> { ) -> Option<Expression<'a>> {
match expr.operator { match expr.operator {
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),
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
UnaryOperator::LogicalNot => { UnaryOperator::LogicalNot => {
@ -135,13 +135,15 @@ impl<'a> PeepholeFoldConstants {
/// `void 1` -> `void 0` /// `void 1` -> `void 0`
fn try_reduce_void( fn try_reduce_void(
&mut self,
expr: &mut UnaryExpression<'a>, expr: &mut UnaryExpression<'a>,
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> { ) -> Option<Expression<'a>> {
if (!expr.argument.is_number() || !expr.argument.is_number_0()) if (!expr.argument.is_number() || !expr.argument.is_number_0())
&& !expr.may_have_side_effects() && !expr.may_have_side_effects()
{ {
let _ = mem::replace(&mut expr.argument, ctx.ast.number_0()); expr.argument = ctx.ast.number_0();
self.changed = true;
} }
None None
} }

View file

@ -46,10 +46,10 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
fn enter_variable_declaration( fn enter_variable_declaration(
&mut self, &mut self,
decl: &mut VariableDeclaration<'a>, decl: &mut VariableDeclaration<'a>,
_ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) { ) {
for declarator in decl.declarations.iter_mut() { for declarator in decl.declarations.iter_mut() {
self.compress_variable_declarator(declarator); self.compress_variable_declarator(declarator, ctx);
} }
} }
@ -247,11 +247,15 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
} }
} }
fn compress_variable_declarator(&mut self, decl: &mut VariableDeclarator<'a>) { fn compress_variable_declarator(
&mut self,
decl: &mut VariableDeclarator<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if decl.kind.is_const() { if decl.kind.is_const() {
return; return;
} }
if decl.init.as_ref().is_some_and(|init| init.is_undefined() || init.is_void_0()) { if decl.init.as_ref().is_some_and(|init| ctx.is_expression_undefined(init)) {
decl.init = None; decl.init = None;
self.changed = true; self.changed = true;
} }

View file

@ -41,10 +41,13 @@ pub trait NodeUtil {
fn scopes(&self) -> &ScopeTree; fn scopes(&self) -> &ScopeTree;
fn is_expression_undefined(&self, expr: &Expression) -> bool { fn is_expression_undefined(&self, expr: &Expression) -> bool {
if let Expression::Identifier(ident) = expr { match expr {
return self.is_identifier_undefined(ident); Expression::Identifier(ident) if self.is_identifier_undefined(ident) => true,
}; Expression::UnaryExpression(e) if e.operator.is_void() && e.argument.is_number() => {
false true
}
_ => false,
}
} }
fn is_identifier_undefined(&self, ident: &IdentifierReference) -> bool { fn is_identifier_undefined(&self, ident: &IdentifierReference) -> bool {

View file

@ -5,7 +5,7 @@ use oxc_allocator::CloneIn;
use oxc_ast_macros::ast; use oxc_ast_macros::ast;
use oxc_span::{cmp::ContentEq, hash::ContentHash}; use oxc_span::{cmp::ContentEq, hash::ContentHash};
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use ::{serde::Serialize, tsify::Tsify}; use {serde::Serialize, tsify::Tsify};
use crate::precedence::{GetPrecedence, Precedence}; use crate::precedence::{GetPrecedence, Precedence};
@ -356,6 +356,10 @@ impl UnaryOperator {
matches!(self, Self::BitwiseNot) matches!(self, Self::BitwiseNot)
} }
pub fn is_void(self) -> bool {
matches!(self, Self::Void)
}
pub fn is_keyword(self) -> bool { pub fn is_keyword(self) -> bool {
matches!(self, Self::Typeof | Self::Void | Self::Delete) matches!(self, Self::Typeof | Self::Void | Self::Delete)
} }