diff --git a/crates/swc/tests/fixture/issues-5xxx/5112/output/index.js b/crates/swc/tests/fixture/issues-5xxx/5112/output/index.js index 2d909c49710f..97079e76e138 100644 --- a/crates/swc/tests/fixture/issues-5xxx/5112/output/index.js +++ b/crates/swc/tests/fixture/issues-5xxx/5112/output/index.js @@ -2,7 +2,7 @@ function c(r, n) { let e; return { e: new Uint8Array(4 * r * r), - s: Math.sqrt(1.25), + s: 1.118033988749895, c: (r - n) / 2 }; } diff --git a/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs b/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs index 98145a084e21..956a95b51964 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs @@ -7,7 +7,10 @@ use swc_ecma_utils::{ExprExt, Value::Known}; use super::{BitCtx, Optimizer}; use crate::{ - compress::util::eval_as_number, program_data::VarUsageInfoFlags, DISABLE_BUGGY_PASSES, + compress::util::eval_as_number, + program_data::VarUsageInfoFlags, + util::size::{Size, SizeWithCtxt}, + DISABLE_BUGGY_PASSES, }; /// Methods related to the option `evaluate`. @@ -428,6 +431,24 @@ impl Optimizer<'_> { if let Expr::Call(..) = e { if let Some(value) = eval_as_number(self.ctx.expr_ctx, e) { + // Calculate the size of the original expression and the resulting literal + let original_size = e.size(self.ctx.expr_ctx.unresolved_ctxt); + let new_size = if value.is_nan() { + // "NaN" identifier is 3 characters + 3 + } else { + // Size of the numeric literal + value.size() + }; + + // Only perform the optimization if the result is strictly smaller than the + // original We use >= instead of > to avoid optimizing + // equal-size expressions, which can lead to worse overall + // minification when values are reused multiple times + if new_size >= original_size { + return; + } + self.changed = true; report_change!("evaluate: Evaluated an expression as `{}`", value); diff --git a/crates/swc_ecma_minifier/src/compress/util/mod.rs b/crates/swc_ecma_minifier/src/compress/util/mod.rs index 21ae0c15efa4..eaf311e5d629 100644 --- a/crates/swc_ecma_minifier/src/compress/util/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/util/mod.rs @@ -518,6 +518,42 @@ pub(crate) fn eval_as_number(expr_ctx: ExprCtx, e: &Expr) -> Option { return Some(base.pow(exponent).into()); } + "ceil" => { + if args.len() != 1 { + return None; + } + let v = eval_as_number(expr_ctx, &args.first()?.expr)?; + + return Some(v.ceil()); + } + + "floor" => { + if args.len() != 1 { + return None; + } + let v = eval_as_number(expr_ctx, &args.first()?.expr)?; + + return Some(v.floor()); + } + + "round" => { + if args.len() != 1 { + return None; + } + let v = eval_as_number(expr_ctx, &args.first()?.expr)?; + + return Some(v.round()); + } + + "sqrt" => { + if args.len() != 1 { + return None; + } + let v = eval_as_number(expr_ctx, &args.first()?.expr)?; + + return Some(v.sqrt()); + } + _ => {} }, _ => {} diff --git a/crates/swc_ecma_minifier/tests/benches-full/d3.js b/crates/swc_ecma_minifier/tests/benches-full/d3.js index 0fbc49a6a3b0..0b9f192491b8 100644 --- a/crates/swc_ecma_minifier/tests/benches-full/d3.js +++ b/crates/swc_ecma_minifier/tests/benches-full/d3.js @@ -3307,7 +3307,7 @@ function(global, factory) { function defaultWeight() { return 1; } - let EDGE_STACK = new Uint32Array(512); + let EPSILON = Math.pow(2, -52), EDGE_STACK = new Uint32Array(512); class Delaunator { static from(points, getX = defaultGetX, getY = defaultGetY) { let n = points.length, coords = new Float64Array(2 * n); @@ -3391,7 +3391,7 @@ function(global, factory) { for(let k = 0, xp, yp; k < this._ids.length; k++){ let i = this._ids[k], x = coords[2 * i], y = coords[2 * i + 1]; // skip near-duplicate points - if (k > 0 && 2.220446049250313e-16 >= Math.abs(x - xp) && 2.220446049250313e-16 >= Math.abs(y - yp) || (xp = x, yp = y, i === i0 || i === i1 || i === i2)) continue; + if (k > 0 && Math.abs(x - xp) <= EPSILON && Math.abs(y - yp) <= EPSILON || (xp = x, yp = y, i === i0 || i === i1 || i === i2)) continue; // find a visible edge on the convex hull using edge hash let start = 0; for(let j = 0, key = this._hashKey(x, y); j < this._hashSize && (-1 === (start = hullHash[(key + j) % this._hashSize]) || start === hullNext[start]); j++); diff --git a/crates/swc_ecma_minifier/tests/benches-full/three.js b/crates/swc_ecma_minifier/tests/benches-full/three.js index d4566afd30f6..6e4acf75ef80 100644 --- a/crates/swc_ecma_minifier/tests/benches-full/three.js +++ b/crates/swc_ecma_minifier/tests/benches-full/three.js @@ -5,7 +5,7 @@ function(global, factory) { ], factory) : factory((global = 'undefined' != typeof globalThis ? globalThis : global || self).THREE = {}); }(this, function(exports1) { 'use strict'; - void 0 === Number.EPSILON && (Number.EPSILON = 2.220446049250313e-16), void 0 === Number.isInteger && // Missing in IE + void 0 === Number.EPSILON && (Number.EPSILON = Math.pow(2, -52)), void 0 === Number.isInteger && // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger (Number.isInteger = function(value) { return 'number' == typeof value && isFinite(value) && Math.floor(value) === value; diff --git a/crates/swc_ecma_minifier/tests/benches-full/victory.js b/crates/swc_ecma_minifier/tests/benches-full/victory.js index 3031ad7c1b70..91c50126dcf7 100644 --- a/crates/swc_ecma_minifier/tests/benches-full/victory.js +++ b/crates/swc_ecma_minifier/tests/benches-full/victory.js @@ -7108,7 +7108,7 @@ \*********************************************************************************/ /*! no static exports found */ /***/ function(module1, exports1, __webpack_require__) { module1.exports = function() { 'use strict'; - var EDGE_STACK = new Uint32Array(512), Delaunator = function(coords) { + var EPSILON = Math.pow(2, -52), EDGE_STACK = new Uint32Array(512), Delaunator = function(coords) { var n = coords.length >> 1; if (n > 0 && 'number' != typeof coords[0]) throw Error('Expected coords to contain numbers.'); this.coords = coords; @@ -7224,7 +7224,7 @@ for(var k = 0, xp = void 0, yp = void 0; k < this._ids.length; k++){ var i$8 = this._ids[k], x$2 = coords[2 * i$8], y$2 = coords[2 * i$8 + 1]; // skip near-duplicate points - if (!(k > 0 && 2.220446049250313e-16 >= Math.abs(x$2 - xp) && 2.220446049250313e-16 >= Math.abs(y$2 - yp)) && (xp = x$2, yp = y$2, i$8 !== i0 && i$8 !== i1 && i$8 !== i2)) { + if (!(k > 0 && Math.abs(x$2 - xp) <= EPSILON && Math.abs(y$2 - yp) <= EPSILON) && (xp = x$2, yp = y$2, i$8 !== i0 && i$8 !== i1 && i$8 !== i2)) { for(var start = 0, j$1 = 0, key = this._hashKey(x$2, y$2); j$1 < this._hashSize && (-1 === (start = hullHash[(key + j$1) % this._hashSize]) || start === hullNext[start]); j$1++); for(// find a visible edge on the convex hull using edge hash var e = start = hullPrev[start], q = void 0; q = hullNext[e], !orient(x$2, y$2, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1]);)if ((e = q) === start) { diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/11078/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/11078/input.js new file mode 100644 index 000000000000..da182abe2fe9 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/11078/input.js @@ -0,0 +1,44 @@ +// Basic constant folding for Math.ceil() +var a = Math.ceil(3.2); +var b = Math.ceil(-3.2); +var c = Math.ceil(0); +var d = Math.ceil(5); + +// Basic constant folding for Math.floor() +var e = Math.floor(3.8); +var f = Math.floor(-3.2); +var g = Math.floor(0); +var h = Math.floor(5); + +// Basic constant folding for Math.round() +var i = Math.round(3.5); +var j = Math.round(-3.5); +var k = Math.round(3.4); +var l = Math.round(-3.6); +var m = Math.round(0); + +// Basic constant folding for Math.sqrt() +var n = Math.sqrt(16); +var o = Math.sqrt(0); +var p = Math.sqrt(2); +var q = Math.sqrt(9); + +// Non-constant expressions should not be optimized +var x = 5; +var notOptimized1 = Math.ceil(x); +var notOptimized2 = Math.floor(x + 1); +var notOptimized3 = Math.round(x * 2); +var notOptimized4 = Math.sqrt(x); + +// Chained optimization example +var result = Math.ceil(3.2); +if (result === 4) { + console.log("Optimized correctly"); +} + +// Nested Math calls should be optimized +var nested = Math.ceil(Math.sqrt(16)); + +// Math operations with constants +var expr1 = Math.ceil(1.5) + Math.floor(2.9); +var expr2 = Math.round(3.7) - Math.sqrt(4); diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/11078/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/11078/output.js new file mode 100644 index 000000000000..61ab88022494 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/11078/output.js @@ -0,0 +1,2 @@ +// Basic constant folding for Math.ceil() +console.log("Optimized correctly"); diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js index f133d0a2311d..b4c4f66316e0 100644 --- a/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js @@ -6972,7 +6972,7 @@ target: "Number", stat: !0 }, { - EPSILON: 2.220446049250313e-16 + EPSILON: Math.pow(2, -52) }); /***/ }, /***/ 36017: /***/ function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) { diff --git a/crates/swc_ecma_minifier/tests/fixture/next/33265/static/chunks/pages/index-cb36c1bf7f830e3c/output.js b/crates/swc_ecma_minifier/tests/fixture/next/33265/static/chunks/pages/index-cb36c1bf7f830e3c/output.js index d6dc0a8d4302..49cd02af9c4c 100644 --- a/crates/swc_ecma_minifier/tests/fixture/next/33265/static/chunks/pages/index-cb36c1bf7f830e3c/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/next/33265/static/chunks/pages/index-cb36c1bf7f830e3c/output.js @@ -6968,7 +6968,7 @@ } return (s ? -1 : 1) * m * Math.pow(2, e - mLen); }, exports.write = function(buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c, eLen = 8 * nBytes - mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1, rt = 5.960464477539062e-8 * (23 === mLen), i = isLE ? 0 : nBytes - 1, d = isLE ? 1 : -1, s = +(value < 0 || 0 === value && 1 / value < 0); + var e, m, c, eLen = 8 * nBytes - mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1, rt = 23 === mLen ? Math.pow(2, -24) - Math.pow(2, -77) : 0, i = isLE ? 0 : nBytes - 1, d = isLE ? 1 : -1, s = +(value < 0 || 0 === value && 1 / value < 0); for(isNaN(value = Math.abs(value)) || value === 1 / 0 ? (m = +!!isNaN(value), e = eMax) : (e = Math.floor(Math.log(value) / Math.LN2), value * (c = Math.pow(2, -e)) < 1 && (e--, c *= 2), e + eBias >= 1 ? value += rt / c : value += rt * Math.pow(2, 1 - eBias), value * c >= 2 && (e++, c /= 2), e + eBias >= eMax ? (m = 0, e = eMax) : e + eBias >= 1 ? (m = (value * c - 1) * Math.pow(2, mLen), e += eBias) : (m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen), e = 0)); mLen >= 8; buffer[offset + i] = 0xff & m, i += d, m /= 256, mLen -= 8); for(e = e << mLen | m, eLen += mLen; eLen > 0; buffer[offset + i] = 0xff & e, i += d, e /= 256, eLen -= 8); buffer[offset + i - d] |= 128 * s; diff --git a/crates/swc_ecma_minifier/tests/fixture/next/feedback-util-promisify/chunks/pages/_app-72ad41192608e93a/output.js b/crates/swc_ecma_minifier/tests/fixture/next/feedback-util-promisify/chunks/pages/_app-72ad41192608e93a/output.js index 5383d1452eef..8db414adf34c 100644 --- a/crates/swc_ecma_minifier/tests/fixture/next/feedback-util-promisify/chunks/pages/_app-72ad41192608e93a/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/next/feedback-util-promisify/chunks/pages/_app-72ad41192608e93a/output.js @@ -729,7 +729,7 @@ } return (p ? -1 : 1) * o * Math.pow(2, i - f); }, r.write = function(e, r, t, f, n, i) { - var o, u, a, s = 8 * i - n - 1, h = (1 << s) - 1, c = h >> 1, l = 5.960464477539062e-8 * (23 === n), p = f ? 0 : i - 1, y = f ? 1 : -1, g = +(r < 0 || 0 === r && 1 / r < 0); + var o, u, a, s = 8 * i - n - 1, h = (1 << s) - 1, c = h >> 1, l = 23 === n ? Math.pow(2, -24) - Math.pow(2, -77) : 0, p = f ? 0 : i - 1, y = f ? 1 : -1, g = +(r < 0 || 0 === r && 1 / r < 0); for(isNaN(r = Math.abs(r)) || r === 1 / 0 ? (u = +!!isNaN(r), o = h) : (o = Math.floor(Math.log(r) / Math.LN2), r * (a = Math.pow(2, -o)) < 1 && (o--, a *= 2), o + c >= 1 ? r += l / a : r += l * Math.pow(2, 1 - c), r * a >= 2 && (o++, a /= 2), o + c >= h ? (u = 0, o = h) : o + c >= 1 ? (u = (r * a - 1) * Math.pow(2, n), o += c) : (u = r * Math.pow(2, c - 1) * Math.pow(2, n), o = 0)); n >= 8; e[t + p] = 255 & u, p += y, u /= 256, n -= 8); for(o = o << n | u, s += n; s > 0; e[t + p] = 255 & o, p += y, o /= 256, s -= 8); e[t + p - y] |= 128 * g; diff --git a/crates/swc_ecma_minifier/tests/fixture/next/react-pdf-renderer/output.js b/crates/swc_ecma_minifier/tests/fixture/next/react-pdf-renderer/output.js index 30e171b619b2..9efe9333deb7 100644 --- a/crates/swc_ecma_minifier/tests/fixture/next/react-pdf-renderer/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/next/react-pdf-renderer/output.js @@ -20962,7 +20962,7 @@ } return (d ? -1 : 1) * a * Math.pow(2, o - n); }, t.write = function(e, t, r, n, i, o) { - var a, u, l, s = 8 * o - i - 1, c = (1 << s) - 1, f = c >> 1, p = 5.960464477539062e-8 * (23 === i), d = n ? 0 : o - 1, h = n ? 1 : -1, y = +(t < 0 || 0 === t && 1 / t < 0); + var a, u, l, s = 8 * o - i - 1, c = (1 << s) - 1, f = c >> 1, p = 23 === i ? Math.pow(2, -24) - Math.pow(2, -77) : 0, d = n ? 0 : o - 1, h = n ? 1 : -1, y = +(t < 0 || 0 === t && 1 / t < 0); for(isNaN(t = Math.abs(t)) || t === 1 / 0 ? (u = +!!isNaN(t), a = c) : (a = Math.floor(Math.log(t) / Math.LN2), t * (l = Math.pow(2, -a)) < 1 && (a--, l *= 2), a + f >= 1 ? t += p / l : t += p * Math.pow(2, 1 - f), t * l >= 2 && (a++, l /= 2), a + f >= c ? (u = 0, a = c) : a + f >= 1 ? (u = (t * l - 1) * Math.pow(2, i), a += f) : (u = t * Math.pow(2, f - 1) * Math.pow(2, i), a = 0)); i >= 8; e[r + d] = 255 & u, d += h, u /= 256, i -= 8); for(a = a << i | u, s += i; s > 0; e[r + d] = 255 & a, d += h, a /= 256, s -= 8); e[r + d - h] |= 128 * y; diff --git a/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js b/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js index 74c804daa064..0f175bff6479 100644 --- a/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js @@ -16024,7 +16024,7 @@ } return (s ? -1 : 1) * m * Math.pow(2, e - mLen); }, exports.write = function(buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c, eLen = 8 * nBytes - mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1, rt = 5.960464477539062e-8 * (23 === mLen), i = isLE ? 0 : nBytes - 1, d = isLE ? 1 : -1, s = +(value < 0 || 0 === value && 1 / value < 0); + var e, m, c, eLen = 8 * nBytes - mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1, rt = 23 === mLen ? Math.pow(2, -24) - Math.pow(2, -77) : 0, i = isLE ? 0 : nBytes - 1, d = isLE ? 1 : -1, s = +(value < 0 || 0 === value && 1 / value < 0); for(isNaN(value = Math.abs(value)) || value === 1 / 0 ? (m = +!!isNaN(value), e = eMax) : (e = Math.floor(Math.log(value) / Math.LN2), value * (c = Math.pow(2, -e)) < 1 && (e--, c *= 2), e + eBias >= 1 ? value += rt / c : value += rt * Math.pow(2, 1 - eBias), value * c >= 2 && (e++, c /= 2), e + eBias >= eMax ? (m = 0, e = eMax) : e + eBias >= 1 ? (m = (value * c - 1) * Math.pow(2, mLen), e += eBias) : (m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen), e = 0)); mLen >= 8; buffer[offset + i] = 0xff & m, i += d, m /= 256, mLen -= 8); for(e = e << mLen | m, eLen += mLen; eLen > 0; buffer[offset + i] = 0xff & e, i += d, e /= 256, eLen -= 8); buffer[offset + i - d] |= 128 * s; diff --git a/crates/swc_ecma_minifier/tests/libs-size.snapshot.md b/crates/swc_ecma_minifier/tests/libs-size.snapshot.md index 561c402979ab..42b0b9a67a82 100644 --- a/crates/swc_ecma_minifier/tests/libs-size.snapshot.md +++ b/crates/swc_ecma_minifier/tests/libs-size.snapshot.md @@ -1,14 +1,14 @@ | File | Original Size | Compressed Size | Gzipped Size | | --- | --- | --- | --- | | antd.js | 6.38 MiB | 2.06 MiB | 445.40 KiB | -| d3.js | 542.74 KiB | 261.45 KiB | 85.34 KiB | +| d3.js | 542.74 KiB | 261.43 KiB | 85.33 KiB | | echarts.js | 3.41 MiB | 977.31 KiB | 314.18 KiB | | jquery.js | 280.89 KiB | 87.80 KiB | 30.21 KiB | | lodash.js | 531.35 KiB | 68.92 KiB | 24.60 KiB | | moment.js | 169.83 KiB | 57.39 KiB | 18.26 KiB | | react.js | 70.45 KiB | 22.44 KiB | 8.04 KiB | | terser.js | 1.08 MiB | 446.65 KiB | 120.49 KiB | -| three.js | 1.19 MiB | 630.73 KiB | 154.79 KiB | -| typescript.js | 10.45 MiB | 3.17 MiB | 840.60 KiB | -| victory.js | 2.30 MiB | 694.10 KiB | 154.19 KiB | +| three.js | 1.19 MiB | 630.73 KiB | 154.78 KiB | +| typescript.js | 10.45 MiB | 3.17 MiB | 840.61 KiB | +| victory.js | 2.30 MiB | 694.08 KiB | 154.18 KiB | | vue.js | 334.13 KiB | 113.70 KiB | 41.81 KiB | diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/issue_2207_1/output.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/issue_2207_1/output.js index 5ad7a3669c19..8f99ff4a083f 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/issue_2207_1/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/issue_2207_1/output.js @@ -1,5 +1,5 @@ console.log("A"); console.log(7); -console.log(0.32999315767856785); -console.log(1.2543732512566947); +console.log(Math.cos(1.2345)); +console.log(Math.cos(1.2345) - Math.sin(4.321)); console.log("1.609398451447204"); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js index d33b69748151..5377324be96c 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js @@ -21,7 +21,7 @@ console.log(Math.pow(2, 1 / 0)); console.log(4); console.log(2); console.log(1); -console.log(1.4142135623730951); +console.log(Math.pow(2, 0.5)); console.log(Math.pow(2, -1 / 0)); console.log(0.5); console.log(1); @@ -39,7 +39,7 @@ console.log(Math.pow(0.5, 1 / 0)); console.log(0.25); console.log(0.5); console.log(1); -console.log(0.7071067811865476); +console.log(Math.pow(0.5, 0.5)); console.log(Math.pow(0.5, -1 / 0)); console.log(2); console.log(1);