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
}
#[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.
/// Special case `undefined` == `void 0`
pub fn expr_eq(self, a: &Expression<'a>, b: &Expression<'a>) -> bool {

View file

@ -1,6 +1,7 @@
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_ecmascript::constant_evaluation::ConstantEvaluation;
use oxc_semantic::IsGlobalReference;
use oxc_span::GetSpan;
use oxc_syntax::scope::ScopeFlags;
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 {
*expr = ctx.ast.move_expression(&mut paren_expr.expression);
}
match expr {
Expression::Identifier(ident) => {
if let Some(e) = Self::try_compress_undefined(ident, ctx) {
*expr = e;
}
Self::convert_infinity_or_nan_into_number(expr, ctx);
}
if let Some(e) = match expr {
Expression::Identifier(ident) => Self::try_compress_identifier(ident, ctx),
Expression::UnaryExpression(e) if e.operator.is_void() => {
Self::convert_void_ident(e, ctx);
None
}
Expression::ArrowFunctionExpression(e) => {
self.recover_arrow_expression_after_drop_console(e);
None
}
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
}
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);
if Self::is_console(expr) {
*expr = ctx.ast.void_0(expr.span());
}
Self::is_console(expr).then(|| ctx.ast.void_0(expr.span()))
}
fn drop_console(&mut self, stmt: &Statement<'a>) -> bool {
@ -135,18 +137,34 @@ impl<'a> Normalize {
*stmt = Statement::ForStatement(for_stmt);
}
fn convert_infinity_or_nan_into_number(expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if let Expression::Identifier(ident) = expr {
let ctx = Ctx(ctx);
let value = if ctx.is_identifier_infinity(ident) {
f64::INFINITY
} else if ctx.is_identifier_nan(ident) {
f64::NAN
} else {
return;
};
*expr =
ctx.ast.expression_numeric_literal(ident.span, value, None, NumberBase::Decimal);
/// Transforms `undefined` => `void 0`, `Infinity` => `f64::Infinity`, `NaN` -> `f64::NaN`.
/// So subsequent passes don't need to look up whether these variables are shadowed or not.
fn try_compress_identifier(
ident: &IdentifierReference<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
match ident.name.as_str() {
"undefined" if ident.is_global_reference(ctx.symbols()) => {
// `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))
}
"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);
}
/// 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)]