mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +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::{
|
use crate::{
|
||||||
node_util::{
|
node_util::{
|
||||||
is_exact_int64, IsLiteralValue, MayHaveSideEffects, NodeUtil, NumberValue, ValueType,
|
is_exact_int64, IsLiteralValue, MayHaveSideEffects, NodeUtil, NumberValue, ValueType,
|
||||||
|
|
@ -17,6 +6,16 @@ use crate::{
|
||||||
ty::Ty,
|
ty::Ty,
|
||||||
CompressorPass,
|
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
|
/// Constant Folding
|
||||||
///
|
///
|
||||||
|
|
@ -139,6 +138,12 @@ impl<'a> PeepholeFoldConstants {
|
||||||
expr: &mut UnaryExpression<'a>,
|
expr: &mut UnaryExpression<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) -> Option<Expression<'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 {
|
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),
|
||||||
|
|
@ -162,9 +167,51 @@ impl<'a> PeepholeFoldConstants {
|
||||||
matches!(unary.operator, UnaryOperator::UnaryNegation)
|
matches!(unary.operator, UnaryOperator::UnaryNegation)
|
||||||
.then(|| ctx.ast.move_expression(&mut expr.argument))
|
.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)),
|
_ if expr.argument.is_number() => Some(ctx.ast.move_expression(&mut expr.argument)),
|
||||||
_ => None,
|
_ => 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1268,9 +1315,9 @@ mod test {
|
||||||
test_same("a=-Infinity");
|
test_same("a=-Infinity");
|
||||||
test("a=-NaN", "a=NaN");
|
test("a=-NaN", "a=NaN");
|
||||||
test_same("a=-foo()");
|
test_same("a=-foo()");
|
||||||
// test("a=~~0", "a=0");
|
test("a=~~0", "a=0");
|
||||||
// test("a=~~10", "a=10");
|
test("a=~~10", "a=10");
|
||||||
// test("a=~-7", "a=6");
|
test("a=~-7", "a=6");
|
||||||
|
|
||||||
// test("a=+true", "a=1");
|
// test("a=+true", "a=1");
|
||||||
test("a=+10", "a=10");
|
test("a=+10", "a=10");
|
||||||
|
|
@ -1279,8 +1326,8 @@ mod test {
|
||||||
test_same("a=+f");
|
test_same("a=+f");
|
||||||
// test("a=+(f?true:false)", "a=+(f?1:0)");
|
// test("a=+(f?true:false)", "a=+(f?1:0)");
|
||||||
test("a=+0", "a=0");
|
test("a=+0", "a=0");
|
||||||
// test("a=+Infinity", "a=Infinity");
|
test("a=+Infinity", "a=Infinity");
|
||||||
// test("a=+NaN", "a=NaN");
|
test("a=+NaN", "a=NaN");
|
||||||
test("a=+-7", "a=-7");
|
test("a=+-7", "a=-7");
|
||||||
// test("a=+.5", "a=.5");
|
// test("a=+.5", "a=.5");
|
||||||
|
|
||||||
|
|
@ -1299,12 +1346,23 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn test_unary_ops_string_compare() {
|
fn test_unary_ops_string_compare() {
|
||||||
test_same("a = -1");
|
test_same("a = -1");
|
||||||
test("a = ~0", "a = -1");
|
test("a = ~0", "a = -1");
|
||||||
test("a = ~1", "a = -2");
|
test("a = ~1", "a = -2");
|
||||||
test("a = ~101", "a = -102");
|
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]
|
#[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
|
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
|
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
|
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.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
|
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
|
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