Skip to content

Commit

Permalink
feat(es/minifier): Compress Assign to number (#9943)
Browse files Browse the repository at this point in the history
**Related issue:**
 - Closes #9935
  • Loading branch information
Austaras authored Jan 26, 2025
1 parent f7faa7c commit d5f40a0
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 237 deletions.
6 changes: 6 additions & 0 deletions .changeset/happy-cameras-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: minor
swc_ecma_minifier: minor
---

feat(es/minifier): Compress Assign to number
56 changes: 42 additions & 14 deletions crates/swc_ecma_minifier/src/compress/pure/numbers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use swc_common::util::take::Take;
use swc_common::{util::take::Take, EqIgnoreSpan, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::num_from_str;

Expand Down Expand Up @@ -92,22 +92,50 @@ impl Pure<'_> {
}

pub(super) fn optimize_to_number(&mut self, e: &mut Expr) {
if let Expr::Bin(bin) = e {
if bin.op == op!("*")
&& matches!(&*bin.left, Expr::Lit(Lit::Num(Number { value: 1.0, .. })))
{
report_change!("numbers: Turn '1 *' into '+'");
self.changed = true;
match e {
Expr::Bin(bin) => {
if bin.op == op!("*")
&& matches!(&*bin.left, Expr::Lit(Lit::Num(Number { value: 1.0, .. })))
{
report_change!("numbers: Turn '1 *' into '+'");
self.changed = true;

let value = bin.right.take();
let span = bin.span;

let value = bin.right.take();
let span = bin.span;
*e = Expr::Unary(UnaryExpr {
span,
op: op!(unary, "+"),
arg: value,
})
}
}
Expr::Assign(a @ AssignExpr { op: op!("="), .. }) => {
if let (
AssignTarget::Simple(SimpleAssignTarget::Ident(l_id)),
Expr::Unary(UnaryExpr {
op: op!(unary, "+"),
arg,
..
}),
) = (&a.left, &*a.right)
{
if let Expr::Ident(r_id) = &**arg {
if l_id.id.eq_ignore_span(r_id) {
report_change!("numbers: Turn a = +a into a *= 1");
self.changed = true;

*e = Expr::Unary(UnaryExpr {
span,
op: op!(unary, "+"),
arg: value,
})
a.op = op!("*=");
a.right = Box::new(Expr::Lit(Lit::Num(Number {
span: DUMMY_SP,
value: 1.0,
raw: None,
})))
}
}
}
}
_ => (),
}
}
}
168 changes: 84 additions & 84 deletions crates/swc_ecma_minifier/tests/benches-full/d3.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/echarts.js
Original file line number Diff line number Diff line change
Expand Up @@ -4039,7 +4039,7 @@
/**
* Get precision
*/ function getPrecision(val) {
if (isNaN(val = +val)) return 0;
if (isNaN(val *= 1)) return 0;
// It is much faster than methods converting number to string as follows
for(// let tmp = val.toString();
// return tmp.length - 1 - tmp.indexOf('.');
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/jquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@
* @param {Function} fn
*/ function createPositionalPseudo(fn) {
return markFunction(function(argument) {
return argument = +argument, markFunction(function(seed, matches) {
return argument *= 1, markFunction(function(seed, matches) {
// Match elements found at the specified indexes
for(var j, matchIndexes = fn([], seed.length, argument), i = matchIndexes.length; i--;)seed[j = matchIndexes[i]] && (seed[j] = !(matches[j] = seed[j]));
});
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/lodash.js
Original file line number Diff line number Diff line change
Expand Up @@ -9611,7 +9611,7 @@
* _.map(['6', '08', '10'], _.parseInt);
* // => [6, 8, 10]
*/ function(string, radix, guard) {
return guard || null == radix ? radix = 0 : radix && (radix = +radix), nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);
return guard || null == radix ? radix = 0 : radix && (radix *= 1), nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);
}, lodash.random = /**
* Produces a random number between the inclusive `lower` and `upper` bounds.
* If only one argument is provided a number between `0` and the given number
Expand Down
110 changes: 55 additions & 55 deletions crates/swc_ecma_minifier/tests/benches-full/victory.js

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -4838,7 +4838,7 @@
module.exports = !$expm1 || // Old FF bug
$expm1(10) > 22025.465794806719 || 22025.4657948067165168 > $expm1(10) || // Tor Browser bug
-0.00000000000000002 != $expm1(-0.00000000000000002) ? function(x) {
return 0 == (x = +x) ? x : x > -0.000001 && x < 1e-6 ? x + x * x / 2 : exp(x) - 1;
return 0 == (x *= 1) ? x : x > -0.000001 && x < 1e-6 ? x + x * x / 2 : exp(x) - 1;
} : $expm1;
/***/ },
/***/ 45404: /***/ function(module, __unused_webpack_exports, __webpack_require__) {
Expand All @@ -4858,7 +4858,7 @@
// https://tc39.es/ecma262/#sec-math.log1p
// eslint-disable-next-line es/no-math-log1p -- safe
module.exports = Math.log1p || function(x) {
return (x = +x) > -0.00000001 && x < 1e-8 ? x - x * x / 2 : log(1 + x);
return (x *= 1) > -0.00000001 && x < 1e-8 ? x - x * x / 2 : log(1 + x);
};
/***/ },
/***/ 62381: /***/ function(module) {
Expand All @@ -4867,7 +4867,7 @@
// eslint-disable-next-line es/no-math-sign -- safe
module.exports = Math.sign || function(x) {
// eslint-disable-next-line no-self-compare -- NaN check
return 0 == (x = +x) || x != x ? x : x < 0 ? -1 : 1;
return 0 == (x *= 1) || x != x ? x : x < 0 ? -1 : 1;
};
/***/ },
/***/ 50277: /***/ function(module, __unused_webpack_exports, __webpack_require__) {
Expand Down Expand Up @@ -5675,7 +5675,7 @@
// `ToInteger` abstract operation
// https://tc39.es/ecma262/#sec-tointeger
module.exports = function(argument) {
return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor : ceil)(argument);
return isNaN(argument *= 1) ? 0 : (argument > 0 ? floor : ceil)(argument);
};
/***/ },
/***/ 31998: /***/ function(module, __unused_webpack_exports, __webpack_require__) {
Expand Down Expand Up @@ -6734,7 +6734,7 @@
$acosh(1 / 0) != 1 / 0
}, {
acosh: function(x) {
return (x = +x) < 1 ? NaN : x > 94906265.62425156 ? log(x) + LN2 : log1p(x - 1 + sqrt(x - 1) * sqrt(x + 1));
return (x *= 1) < 1 ? NaN : x > 94906265.62425156 ? log(x) + LN2 : log1p(x - 1 + sqrt(x - 1) * sqrt(x + 1));
}
});
/***/ },
Expand All @@ -6749,7 +6749,7 @@
forced: !($asinh && 1 / $asinh(0) > 0)
}, {
asinh: function asinh(x) {
return isFinite(x = +x) && 0 != x ? x < 0 ? -asinh(-x) : log(x + sqrt(x * x + 1)) : x;
return isFinite(x *= 1) && 0 != x ? x < 0 ? -asinh(-x) : log(x + sqrt(x * x + 1)) : x;
}
});
/***/ },
Expand All @@ -6764,7 +6764,7 @@
forced: !($atanh && 1 / $atanh(-0) < 0)
}, {
atanh: function(x) {
return 0 == (x = +x) ? x : log((1 + x) / (1 - x)) / 2;
return 0 == (x *= 1) ? x : log((1 + x) / (1 - x)) / 2;
}
});
/***/ },
Expand All @@ -6777,7 +6777,7 @@
stat: !0
}, {
cbrt: function(x) {
return sign(x = +x) * pow(abs(x), 1 / 3);
return sign(x *= 1) * pow(abs(x), 1 / 3);
}
});
/***/ },
Expand Down Expand Up @@ -6926,7 +6926,7 @@
})
}, {
sinh: function(x) {
return 1 > abs(x = +x) ? (expm1(x) - expm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
return 1 > abs(x *= 1) ? (expm1(x) - expm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
}
});
/***/ },
Expand All @@ -6939,7 +6939,7 @@
stat: !0
}, {
tanh: function(x) {
var a = expm1(x = +x), b = expm1(-x);
var a = expm1(x *= 1), b = expm1(-x);
return a == 1 / 0 ? 1 : b == 1 / 0 ? -1 : (a - b) / (exp(x) + exp(-x));
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6528,7 +6528,7 @@
var obj;
// Empty buffer means no match
if (0 === buffer.length) return -1;
if ("string" == typeof byteOffset ? (encoding = byteOffset, byteOffset = 0) : byteOffset > 0x7fffffff ? byteOffset = 0x7fffffff : byteOffset < -2147483648 && (byteOffset = -2147483648), (obj = byteOffset = +byteOffset) != obj && // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
if ("string" == typeof byteOffset ? (encoding = byteOffset, byteOffset = 0) : byteOffset > 0x7fffffff ? byteOffset = 0x7fffffff : byteOffset < -2147483648 && (byteOffset = -2147483648), (obj = byteOffset *= 1) != obj && // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
(byteOffset = dir ? 0 : buffer.length - 1), byteOffset < 0 && (byteOffset = buffer.length + byteOffset), byteOffset >= buffer.length) {
if (dir) return -1;
byteOffset = buffer.length - 1;
Expand Down Expand Up @@ -6612,10 +6612,10 @@
if (offset + ext > buf.length || offset < 0) throw RangeError("Index out of range");
}
function writeFloat(buf, value, offset, littleEndian, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 4, 3.4028234663852886e38, -340282346638528860000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 23, 4), offset + 4;
return value *= 1, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 4, 3.4028234663852886e38, -340282346638528860000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 23, 4), offset + 4;
}
function writeDouble(buf, value, offset, littleEndian, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 8, 1.7976931348623157e308, -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 52, 8), offset + 8;
return value *= 1, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 8, 1.7976931348623157e308, -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 52, 8), offset + 8;
}
exports.Buffer = Buffer, exports.SlowBuffer = function(length) {
return +length != length && // eslint-disable-line eqeqeq
Expand Down Expand Up @@ -6872,57 +6872,57 @@
}, Buffer.prototype.readDoubleBE = function(offset, noAssert) {
return offset >>>= 0, noAssert || checkOffset(offset, 8, this.length), ieee754.read(this, offset, !1, 52, 8);
}, Buffer.prototype.writeUIntLE = function(value, offset, byteLength, noAssert) {
if (value = +value, offset >>>= 0, byteLength >>>= 0, !noAssert) {
if (value *= 1, offset >>>= 0, byteLength >>>= 0, !noAssert) {
var maxBytes = Math.pow(2, 8 * byteLength) - 1;
checkInt(this, value, offset, byteLength, maxBytes, 0);
}
var mul = 1, i = 0;
for(this[offset] = 0xff & value; ++i < byteLength && (mul *= 0x100);)this[offset + i] = value / mul & 0xff;
return offset + byteLength;
}, Buffer.prototype.writeUIntBE = function(value, offset, byteLength, noAssert) {
if (value = +value, offset >>>= 0, byteLength >>>= 0, !noAssert) {
if (value *= 1, offset >>>= 0, byteLength >>>= 0, !noAssert) {
var maxBytes = Math.pow(2, 8 * byteLength) - 1;
checkInt(this, value, offset, byteLength, maxBytes, 0);
}
var i = byteLength - 1, mul = 1;
for(this[offset + i] = 0xff & value; --i >= 0 && (mul *= 0x100);)this[offset + i] = value / mul & 0xff;
return offset + byteLength;
}, Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0xff, 0), this[offset] = 0xff & value, offset + 1;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0xff, 0), this[offset] = 0xff & value, offset + 1;
}, Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
}, Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
}, Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0xffffffff, 0), this[offset + 3] = value >>> 24, this[offset + 2] = value >>> 16, this[offset + 1] = value >>> 8, this[offset] = 0xff & value, offset + 4;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0xffffffff, 0), this[offset + 3] = value >>> 24, this[offset + 2] = value >>> 16, this[offset + 1] = value >>> 8, this[offset] = 0xff & value, offset + 4;
}, Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0xffffffff, 0), this[offset] = value >>> 24, this[offset + 1] = value >>> 16, this[offset + 2] = value >>> 8, this[offset + 3] = 0xff & value, offset + 4;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0xffffffff, 0), this[offset] = value >>> 24, this[offset + 1] = value >>> 16, this[offset + 2] = value >>> 8, this[offset + 3] = 0xff & value, offset + 4;
}, Buffer.prototype.writeIntLE = function(value, offset, byteLength, noAssert) {
if (value = +value, offset >>>= 0, !noAssert) {
if (value *= 1, offset >>>= 0, !noAssert) {
var limit = Math.pow(2, 8 * byteLength - 1);
checkInt(this, value, offset, byteLength, limit - 1, -limit);
}
var i = 0, mul = 1, sub = 0;
for(this[offset] = 0xff & value; ++i < byteLength && (mul *= 0x100);)value < 0 && 0 === sub && 0 !== this[offset + i - 1] && (sub = 1), this[offset + i] = (value / mul >> 0) - sub & 0xff;
return offset + byteLength;
}, Buffer.prototype.writeIntBE = function(value, offset, byteLength, noAssert) {
if (value = +value, offset >>>= 0, !noAssert) {
if (value *= 1, offset >>>= 0, !noAssert) {
var limit = Math.pow(2, 8 * byteLength - 1);
checkInt(this, value, offset, byteLength, limit - 1, -limit);
}
var i = byteLength - 1, mul = 1, sub = 0;
for(this[offset + i] = 0xff & value; --i >= 0 && (mul *= 0x100);)value < 0 && 0 === sub && 0 !== this[offset + i + 1] && (sub = 1), this[offset + i] = (value / mul >> 0) - sub & 0xff;
return offset + byteLength;
}, Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0x7f, -128), value < 0 && (value = 0xff + value + 1), this[offset] = 0xff & value, offset + 1;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0x7f, -128), value < 0 && (value = 0xff + value + 1), this[offset] = 0xff & value, offset + 1;
}, Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
}, Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
}, Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0x7fffffff, -2147483648), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, this[offset + 2] = value >>> 16, this[offset + 3] = value >>> 24, offset + 4;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0x7fffffff, -2147483648), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, this[offset + 2] = value >>> 16, this[offset + 3] = value >>> 24, offset + 4;
}, Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0x7fffffff, -2147483648), value < 0 && (value = 0xffffffff + value + 1), this[offset] = value >>> 24, this[offset + 1] = value >>> 16, this[offset + 2] = value >>> 8, this[offset + 3] = 0xff & value, offset + 4;
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 4, 0x7fffffff, -2147483648), value < 0 && (value = 0xffffffff + value + 1), this[offset] = value >>> 24, this[offset + 1] = value >>> 16, this[offset + 2] = value >>> 8, this[offset + 3] = 0xff & value, offset + 4;
}, Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
return writeFloat(this, value, offset, !0, noAssert);
}, Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
Expand Down
Loading

0 comments on commit d5f40a0

Please sign in to comment.