refactor(minifier): clean up Normalize (#8700)

This commit is contained in:
Boshen 2025-01-25 13:52:35 +08:00 committed by GitHub
parent 8587965e45
commit 32e0e4796c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 58 deletions

View file

@ -70,21 +70,6 @@ impl<'a> Ctx<'a, '_> {
false false
} }
#[inline]
pub fn is_identifier_infinity(self, ident: &IdentifierReference) -> bool {
if ident.name == "Infinity" && ident.is_global_reference(self.symbols()) {
return true;
}
false
}
#[inline]
pub fn is_identifier_nan(self, ident: &IdentifierReference) -> bool {
if ident.name == "NaN" && ident.is_global_reference(self.symbols()) {
return true;
}
false
}
/// If two expressions are equal. /// If two expressions are equal.
/// Special case `undefined` == `void 0` /// Special case `undefined` == `void 0`
pub fn expr_eq(self, a: &Expression<'a>, b: &Expression<'a>) -> bool { pub fn expr_eq(self, a: &Expression<'a>, b: &Expression<'a>) -> bool {

View file

@ -1,6 +1,7 @@
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::ast::*; use oxc_ast::ast::*;
use oxc_ecmascript::constant_evaluation::ConstantEvaluation; use oxc_ecmascript::constant_evaluation::ConstantEvaluation;
use oxc_semantic::IsGlobalReference;
use oxc_span::GetSpan; use oxc_span::GetSpan;
use oxc_syntax::scope::ScopeFlags; use oxc_syntax::scope::ScopeFlags;
use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx}; use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx};
@ -62,23 +63,22 @@ impl<'a> Traverse<'a> for Normalize {
if let Expression::ParenthesizedExpression(paren_expr) = expr { if let Expression::ParenthesizedExpression(paren_expr) = expr {
*expr = ctx.ast.move_expression(&mut paren_expr.expression); *expr = ctx.ast.move_expression(&mut paren_expr.expression);
} }
match expr { if let Some(e) = match expr {
Expression::Identifier(ident) => { Expression::Identifier(ident) => Self::try_compress_identifier(ident, ctx),
if let Some(e) = Self::try_compress_undefined(ident, ctx) {
*expr = e;
}
Self::convert_infinity_or_nan_into_number(expr, ctx);
}
Expression::UnaryExpression(e) if e.operator.is_void() => { Expression::UnaryExpression(e) if e.operator.is_void() => {
Self::convert_void_ident(e, ctx); Self::convert_void_ident(e, ctx);
None
} }
Expression::ArrowFunctionExpression(e) => { Expression::ArrowFunctionExpression(e) => {
self.recover_arrow_expression_after_drop_console(e); self.recover_arrow_expression_after_drop_console(e);
None
} }
Expression::CallExpression(_) if self.compress_options.drop_console => { Expression::CallExpression(_) if self.compress_options.drop_console => {
self.compress_console(expr, ctx); self.compress_console(expr, ctx)
} }
_ => {} _ => None,
} {
*expr = e;
} }
} }
} }
@ -95,11 +95,13 @@ impl<'a> Normalize {
matches!(stmt, Statement::DebuggerStatement(_)) && self.compress_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>,
) -> Option<Expression<'a>> {
debug_assert!(self.compress_options.drop_console); debug_assert!(self.compress_options.drop_console);
if Self::is_console(expr) { Self::is_console(expr).then(|| 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 {
@ -135,18 +137,34 @@ impl<'a> Normalize {
*stmt = Statement::ForStatement(for_stmt); *stmt = Statement::ForStatement(for_stmt);
} }
fn convert_infinity_or_nan_into_number(expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { /// Transforms `undefined` => `void 0`, `Infinity` => `f64::Infinity`, `NaN` -> `f64::NaN`.
if let Expression::Identifier(ident) = expr { /// So subsequent passes don't need to look up whether these variables are shadowed or not.
let ctx = Ctx(ctx); fn try_compress_identifier(
let value = if ctx.is_identifier_infinity(ident) { ident: &IdentifierReference<'a>,
f64::INFINITY ctx: &mut TraverseCtx<'a>,
} else if ctx.is_identifier_nan(ident) { ) -> Option<Expression<'a>> {
f64::NAN match ident.name.as_str() {
} else { "undefined" if ident.is_global_reference(ctx.symbols()) => {
return; // `delete undefined` returns `false`
}; // `delete void 0` returns `true`
*expr = if matches!(ctx.parent(), Ancestor::UnaryExpressionArgument(e) if e.operator().is_delete())
ctx.ast.expression_numeric_literal(ident.span, value, None, NumberBase::Decimal); {
return None;
}
Some(ctx.ast.void_0(ident.span))
}
"Infinity" if ident.is_global_reference(ctx.symbols()) => {
Some(ctx.ast.expression_numeric_literal(
ident.span,
f64::INFINITY,
None,
NumberBase::Decimal,
))
}
"NaN" if ident.is_global_reference(ctx.symbols()) => Some(
ctx.ast.expression_numeric_literal(ident.span, f64::NAN, None, NumberBase::Decimal),
),
_ => None,
} }
} }
@ -158,24 +176,6 @@ impl<'a> Normalize {
} }
e.argument = ctx.ast.expression_numeric_literal(ident.span, 0.0, None, NumberBase::Decimal); e.argument = ctx.ast.expression_numeric_literal(ident.span, 0.0, None, NumberBase::Decimal);
} }
/// Transforms `undefined` => `void 0`
/// So subsequent passes don't need to look up whether `undefined` is shadowed or not.
fn try_compress_undefined(
ident: &IdentifierReference<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if !Ctx(ctx).is_identifier_undefined(ident) {
return None;
}
// `delete undefined` returns `false`
// `delete void 0` returns `true`
if matches!(ctx.parent(), Ancestor::UnaryExpressionArgument(e) if e.operator().is_delete())
{
return None;
}
Some(ctx.ast.void_0(ident.span))
}
} }
#[cfg(test)] #[cfg(test)]