mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): handle positive NaN and Infinity. (#6207)
`+NaN` -> `NaN`, `+Infinity` -> `Infinity`.
This commit is contained in:
parent
c3c3447ac3
commit
cca0034e8b
2 changed files with 80 additions and 22 deletions
|
|
@ -1,14 +1,3 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, Span, SPAN};
|
||||
use oxc_syntax::{
|
||||
number::NumberBase,
|
||||
operator::{BinaryOperator, LogicalOperator, UnaryOperator},
|
||||
};
|
||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
|
||||
use crate::{
|
||||
node_util::{
|
||||
is_exact_int64, IsLiteralValue, MayHaveSideEffects, NodeUtil, NumberValue, ValueType,
|
||||
|
|
@ -17,6 +6,16 @@ use crate::{
|
|||
ty::Ty,
|
||||
CompressorPass,
|
||||
};
|
||||
use num_bigint::BigInt;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, Span, SPAN};
|
||||
use oxc_syntax::number::ToJsInt32;
|
||||
use oxc_syntax::{
|
||||
number::NumberBase,
|
||||
operator::{BinaryOperator, LogicalOperator, UnaryOperator},
|
||||
};
|
||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Constant Folding
|
||||
///
|
||||
|
|
@ -139,6 +138,12 @@ impl<'a> PeepholeFoldConstants {
|
|||
expr: &mut UnaryExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
fn is_within_i32_range(x: f64) -> bool {
|
||||
x.is_finite()
|
||||
&& x.fract() == 0.0
|
||||
&& x >= f64::from(i32::MIN)
|
||||
&& x <= f64::from(i32::MAX)
|
||||
}
|
||||
match expr.operator {
|
||||
UnaryOperator::Void => self.try_reduce_void(expr, ctx),
|
||||
UnaryOperator::Typeof => self.try_fold_type_of(expr, ctx),
|
||||
|
|
@ -162,9 +167,51 @@ impl<'a> PeepholeFoldConstants {
|
|||
matches!(unary.operator, UnaryOperator::UnaryNegation)
|
||||
.then(|| ctx.ast.move_expression(&mut expr.argument))
|
||||
}
|
||||
Expression::Identifier(id) if id.name == "Infinity" => {
|
||||
Some(ctx.ast.move_expression(&mut expr.argument))
|
||||
}
|
||||
// `+NaN` -> `NaN`
|
||||
_ if expr.argument.is_nan() => Some(ctx.ast.move_expression(&mut expr.argument)),
|
||||
_ if expr.argument.is_number() => Some(ctx.ast.move_expression(&mut expr.argument)),
|
||||
_ => None,
|
||||
},
|
||||
UnaryOperator::BitwiseNot => match &mut expr.argument {
|
||||
Expression::NumericLiteral(n) => is_within_i32_range(n.value).then(|| {
|
||||
let value = !n.value.to_js_int_32();
|
||||
ctx.ast.expression_numeric_literal(
|
||||
SPAN,
|
||||
value.into(),
|
||||
value.to_string(),
|
||||
NumberBase::Decimal,
|
||||
)
|
||||
}),
|
||||
Expression::UnaryExpression(un) => {
|
||||
match un.operator {
|
||||
UnaryOperator::BitwiseNot => {
|
||||
// Return the un-bitten value
|
||||
Some(ctx.ast.move_expression(&mut un.argument))
|
||||
}
|
||||
UnaryOperator::UnaryNegation if un.argument.is_number() => {
|
||||
// `-~1` -> `2`
|
||||
if let Expression::NumericLiteral(n) = &mut un.argument {
|
||||
is_within_i32_range(n.value).then(|| {
|
||||
let value = !(-n.value.to_js_int_32());
|
||||
ctx.ast.expression_numeric_literal(
|
||||
SPAN,
|
||||
value.into(),
|
||||
value.to_string(),
|
||||
NumberBase::Decimal,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1268,9 +1315,9 @@ mod test {
|
|||
test_same("a=-Infinity");
|
||||
test("a=-NaN", "a=NaN");
|
||||
test_same("a=-foo()");
|
||||
// test("a=~~0", "a=0");
|
||||
// test("a=~~10", "a=10");
|
||||
// test("a=~-7", "a=6");
|
||||
test("a=~~0", "a=0");
|
||||
test("a=~~10", "a=10");
|
||||
test("a=~-7", "a=6");
|
||||
|
||||
// test("a=+true", "a=1");
|
||||
test("a=+10", "a=10");
|
||||
|
|
@ -1279,8 +1326,8 @@ mod test {
|
|||
test_same("a=+f");
|
||||
// test("a=+(f?true:false)", "a=+(f?1:0)");
|
||||
test("a=+0", "a=0");
|
||||
// test("a=+Infinity", "a=Infinity");
|
||||
// test("a=+NaN", "a=NaN");
|
||||
test("a=+Infinity", "a=Infinity");
|
||||
test("a=+NaN", "a=NaN");
|
||||
test("a=+-7", "a=-7");
|
||||
// test("a=+.5", "a=.5");
|
||||
|
||||
|
|
@ -1299,12 +1346,23 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_unary_ops_string_compare() {
|
||||
test_same("a = -1");
|
||||
test("a = ~0", "a = -1");
|
||||
test("a = ~1", "a = -2");
|
||||
test("a = ~101", "a = -102");
|
||||
|
||||
// More tests added by Ethan, which aligns with Google Closure Compiler's behavior
|
||||
test_same("a = ~1.1"); // By default, we don't fold floating-point numbers.
|
||||
test("a = ~0x3", "a = -4"); // Hexadecimal number
|
||||
test("a = ~9", "a = -10"); // Despite `-10` is longer than `~9`, the compiler still folds it.
|
||||
test_same("a = ~b");
|
||||
test_same("a = ~NaN");
|
||||
test_same("a = ~-Infinity");
|
||||
// TODO(7086cmd) We preserve it right now, since exceeded data's ~ calculation
|
||||
// is hard to implement within one PR.
|
||||
// test("x = ~2147483658.0", "x = 2147483647");
|
||||
// test("x = ~-2147483658", "x = -2147483649");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Original | Minified | esbuild | Gzip | esbuild
|
|||
|
||||
72.14 kB | 24.47 kB | 23.70 kB | 8.65 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 61.71 kB | 59.82 kB | 19.56 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 61.70 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 92.83 kB | 90.07 kB | 32.29 kB | 31.95 kB | jquery.js
|
||||
|
||||
|
|
@ -10,17 +10,17 @@ Original | Minified | esbuild | Gzip | esbuild
|
|||
|
||||
544.10 kB | 74.13 kB | 72.48 kB | 26.23 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 278.71 kB | 270.13 kB | 91.40 kB | 90.80 kB | d3.js
|
||||
555.77 kB | 278.70 kB | 270.13 kB | 91.39 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 470.11 kB | 458.89 kB | 126.97 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 671.02 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js
|
||||
1.25 MB | 671 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 756.70 kB | 724.14 kB | 182.87 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 756.69 kB | 724.14 kB | 182.87 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.05 MB | 1.01 MB | 334.11 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.44 MB | 2.31 MB | 498.90 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.59 MB | 3.49 MB | 913.91 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.59 MB | 3.49 MB | 913.94 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue