mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(minifier): replace Math.pow with ** (#8730)
Replaces `Math.pow(a, b)` with `(+a) ** (+b)`. `+` does `ToNumber` and `**` does `Number::exponentiate` when both operands are number. `+` is not added when `a` or `b` is already a number. **Reference** - [Spec of `Math.pow`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.pow) - [Spec of `**`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-exp-operator-runtime-semantics-evaluation) - [Spec of unary `+`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-unary-plus-operator-runtime-semantics-evaluation)
This commit is contained in:
parent
6bc906c694
commit
360d49e962
2 changed files with 71 additions and 8 deletions
|
|
@ -4,8 +4,8 @@ use cow_utils::CowUtils;
|
|||
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_ecmascript::{
|
||||
constant_evaluation::ConstantEvaluation, StringCharAt, StringCharCodeAt, StringIndexOf,
|
||||
StringLastIndexOf, StringSubstring, ToInt32,
|
||||
constant_evaluation::{ConstantEvaluation, ValueType},
|
||||
StringCharAt, StringCharCodeAt, StringIndexOf, StringLastIndexOf, StringSubstring, ToInt32,
|
||||
};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::es_target::ESTarget;
|
||||
|
|
@ -63,6 +63,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),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(replacement) = replacement {
|
||||
|
|
@ -342,6 +343,51 @@ impl<'a> PeepholeOptimizations {
|
|||
result.into_iter().rev().collect()
|
||||
}
|
||||
|
||||
/// `Math.pow(a, b)` -> `+(a) ** +b`
|
||||
fn try_fold_pow(
|
||||
&self,
|
||||
span: Span,
|
||||
arguments: &mut Arguments<'a>,
|
||||
object: &Expression<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if self.target < ESTarget::ES2016 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Expression::Identifier(ident) = object else { return None };
|
||||
if ident.name != "Math" || !ctx.is_global_reference(ident) {
|
||||
return None;
|
||||
}
|
||||
if arguments.len() != 2 || arguments.iter().any(|arg| !arg.is_expression()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut second_arg = arguments.pop().expect("checked len above");
|
||||
let second_arg = second_arg.to_expression_mut(); // checked above
|
||||
let mut first_arg = arguments.pop().expect("checked len above");
|
||||
let first_arg = first_arg.to_expression_mut(); // checked above
|
||||
|
||||
let wrap_with_unary_plus_if_needed = |expr: &mut Expression<'a>| {
|
||||
if ValueType::from(&*expr).is_number() {
|
||||
ctx.ast.move_expression(expr)
|
||||
} else {
|
||||
ctx.ast.expression_unary(
|
||||
SPAN,
|
||||
UnaryOperator::UnaryPlus,
|
||||
ctx.ast.move_expression(expr),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Some(ctx.ast.expression_binary(
|
||||
span,
|
||||
wrap_with_unary_plus_if_needed(first_arg),
|
||||
BinaryOperator::Exponential,
|
||||
wrap_with_unary_plus_if_needed(second_arg),
|
||||
))
|
||||
}
|
||||
|
||||
/// `[].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, '_>) {
|
||||
|
|
@ -1470,6 +1516,23 @@ mod test {
|
|||
test("/./.toString(b)", "/./.toString(b)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_pow() {
|
||||
test("Math.pow(2, 3)", "2 ** 3");
|
||||
test("Math.pow(a, 3)", "+(a) ** 3");
|
||||
test("Math.pow(2, b)", "2 ** +b");
|
||||
test("Math.pow(a, b)", "+(a) ** +b");
|
||||
test("Math.pow(2n, 3n)", "+(2n) ** +3n"); // errors both before and after
|
||||
test("Math.pow(a + b, c)", "+(a + b) ** +c");
|
||||
test_same("Math.pow()");
|
||||
test_same("Math.pow(1)");
|
||||
test_same("Math.pow(...a, 1)");
|
||||
test_same("Math.pow(1, ...a)");
|
||||
test_same("Math.pow(1, 2, 3)");
|
||||
test_es2015("Math.pow(2, 3)", "Math.pow(2, 3)");
|
||||
test_same("Unknown.pow(1, 2)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_constants() {
|
||||
test("v = Number.POSITIVE_INFINITY", "v = Infinity");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
-------------------------------------------------------------------------------------
|
||||
72.14 kB | 23.61 kB | 23.70 kB | 8.55 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 59.71 kB | 59.82 kB | 19.26 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 59.70 kB | 59.82 kB | 19.26 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 89.58 kB | 90.07 kB | 31.08 kB | 31.95 kB | jquery.js
|
||||
|
||||
|
|
@ -11,17 +11,17 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
|
||||
544.10 kB | 71.50 kB | 72.48 kB | 25.92 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 272.35 kB | 270.13 kB | 88.60 kB | 90.80 kB | d3.js
|
||||
555.77 kB | 272.12 kB | 270.13 kB | 88.59 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 458.28 kB | 458.89 kB | 123.94 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 650.82 kB | 646.76 kB | 161.51 kB | 163.73 kB | three.js
|
||||
1.25 MB | 650.70 kB | 646.76 kB | 161.49 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 719.54 kB | 724.14 kB | 162.47 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 719.06 kB | 724.14 kB | 162.43 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.01 MB | 1.01 MB | 325.41 kB | 331.56 kB | echarts.js
|
||||
3.20 MB | 1.01 MB | 1.01 MB | 325.38 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.30 MB | 2.31 MB | 470.00 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.37 MB | 3.49 MB | 866.65 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.37 MB | 3.49 MB | 866.66 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue