mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): evaluate Math.sqrt and Math.cbrt (#8731)
Replaces `Math.sqrt(a)` and `Math.cbrt(a)` when a is a constant and the result is an integer. The motivation of this PR is to add a comment that `Math.sqrt` / `Math.cbrt` cannot be replaced to `**`, rather than the actual minification improvement. **Reference** - [Spec of `Math.sqrt`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.sqrt) - [Spec of `Math.cbrt`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.cbrt) - [Spec of `Number::exponentiate`](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate)
This commit is contained in:
parent
360d49e962
commit
9e32f55a8c
1 changed files with 75 additions and 0 deletions
|
|
@ -64,6 +64,7 @@ impl<'a> PeepholeOptimizations {
|
|||
"fromCharCode" => Self::try_fold_string_from_char_code(*span, arguments, object, ctx),
|
||||
"toString" => Self::try_fold_to_string(*span, arguments, object, ctx),
|
||||
"pow" => self.try_fold_pow(*span, arguments, object, ctx),
|
||||
"sqrt" | "cbrt" => Self::try_fold_roots(*span, arguments, name, object, ctx),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(replacement) = replacement {
|
||||
|
|
@ -388,6 +389,57 @@ impl<'a> PeepholeOptimizations {
|
|||
))
|
||||
}
|
||||
|
||||
/// `Math.sqrt(a)`, `Math.cbrt(a)`
|
||||
///
|
||||
/// These cannot be replaced with `a ** .5`, `a ** (1/3)` because `Math.sqrt(-0)` returns `-0` where `(-0) ** .5` returns `0`.
|
||||
/// It can be replaced when the value is known to be not `-0`, but that makes the gzip output worse.
|
||||
fn try_fold_roots(
|
||||
span: Span,
|
||||
arguments: &Arguments,
|
||||
name: &str,
|
||||
object: &Expression<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let Expression::Identifier(ident) = object else { return None };
|
||||
if ident.name != "Math" || !ctx.is_global_reference(ident) {
|
||||
return None;
|
||||
}
|
||||
if arguments.len() != 1 || !arguments[0].is_expression() {
|
||||
return None;
|
||||
}
|
||||
let arg_val = ctx.get_side_free_number_value(arguments[0].to_expression())?;
|
||||
if arg_val == f64::INFINITY || arg_val.is_nan() || arg_val == 0.0 {
|
||||
return Some(ctx.ast.expression_numeric_literal(
|
||||
span,
|
||||
arg_val,
|
||||
None,
|
||||
NumberBase::Decimal,
|
||||
));
|
||||
}
|
||||
if arg_val < 0.0 {
|
||||
return Some(ctx.ast.expression_numeric_literal(
|
||||
span,
|
||||
f64::NAN,
|
||||
None,
|
||||
NumberBase::Decimal,
|
||||
));
|
||||
}
|
||||
let calculated_val = match name {
|
||||
"sqrt" => arg_val.sqrt(),
|
||||
"cbrt" => arg_val.cbrt(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if calculated_val.fract() == 0.0 {
|
||||
return Some(ctx.ast.expression_numeric_literal(
|
||||
span,
|
||||
calculated_val,
|
||||
None,
|
||||
NumberBase::Decimal,
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// `[].concat(a).concat(b)` -> `[].concat(a, b)`
|
||||
/// `"".concat(a).concat(b)` -> `"".concat(a, b)`
|
||||
fn try_fold_concat_chain(&mut self, node: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
|
|
@ -1533,6 +1585,29 @@ mod test {
|
|||
test_same("Unknown.pow(1, 2)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_roots() {
|
||||
test_same("v = Math.sqrt()");
|
||||
test_same("v = Math.sqrt(1, 2)");
|
||||
test_same("v = Math.sqrt(...a)");
|
||||
test_same("v = Math.sqrt(a)"); // a maybe -0
|
||||
test_same("v = Math.sqrt(2n)");
|
||||
test("v = Math.sqrt(Infinity)", "v = Infinity");
|
||||
test("v = Math.sqrt(NaN)", "v = NaN");
|
||||
test("v = Math.sqrt(0)", "v = 0");
|
||||
test("v = Math.sqrt(-0)", "v = -0");
|
||||
test("v = Math.sqrt(-1)", "v = NaN");
|
||||
test("v = Math.sqrt(-Infinity)", "v = NaN");
|
||||
test("v = Math.sqrt(1)", "v = 1");
|
||||
test("v = Math.sqrt(4)", "v = 2");
|
||||
test_same("v = Math.sqrt(2)");
|
||||
test("v = Math.cbrt(1)", "v = 1");
|
||||
test("v = Math.cbrt(8)", "v = 2");
|
||||
test_same("v = Math.cbrt(2)");
|
||||
test_same("Unknown.sqrt(1)");
|
||||
test_same("Unknown.cbrt(1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_constants() {
|
||||
test("v = Number.POSITIVE_INFINITY", "v = Infinity");
|
||||
|
|
|
|||
Loading…
Reference in a new issue