Skip to content

Commit d5f40a0

Browse files
authoredJan 26, 2025··
feat(es/minifier): Compress Assign to number (#9943)
**Related issue:** - Closes #9935
1 parent f7faa7c commit d5f40a0

File tree

14 files changed

+271
-237
lines changed

14 files changed

+271
-237
lines changed
 

‎.changeset/happy-cameras-run.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_core: minor
3+
swc_ecma_minifier: minor
4+
---
5+
6+
feat(es/minifier): Compress Assign to number

‎crates/swc_ecma_minifier/src/compress/pure/numbers.rs

+42-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use swc_common::util::take::Take;
1+
use swc_common::{util::take::Take, EqIgnoreSpan, DUMMY_SP};
22
use swc_ecma_ast::*;
33
use swc_ecma_utils::num_from_str;
44

@@ -92,22 +92,50 @@ impl Pure<'_> {
9292
}
9393

9494
pub(super) fn optimize_to_number(&mut self, e: &mut Expr) {
95-
if let Expr::Bin(bin) = e {
96-
if bin.op == op!("*")
97-
&& matches!(&*bin.left, Expr::Lit(Lit::Num(Number { value: 1.0, .. })))
98-
{
99-
report_change!("numbers: Turn '1 *' into '+'");
100-
self.changed = true;
95+
match e {
96+
Expr::Bin(bin) => {
97+
if bin.op == op!("*")
98+
&& matches!(&*bin.left, Expr::Lit(Lit::Num(Number { value: 1.0, .. })))
99+
{
100+
report_change!("numbers: Turn '1 *' into '+'");
101+
self.changed = true;
102+
103+
let value = bin.right.take();
104+
let span = bin.span;
101105

102-
let value = bin.right.take();
103-
let span = bin.span;
106+
*e = Expr::Unary(UnaryExpr {
107+
span,
108+
op: op!(unary, "+"),
109+
arg: value,
110+
})
111+
}
112+
}
113+
Expr::Assign(a @ AssignExpr { op: op!("="), .. }) => {
114+
if let (
115+
AssignTarget::Simple(SimpleAssignTarget::Ident(l_id)),
116+
Expr::Unary(UnaryExpr {
117+
op: op!(unary, "+"),
118+
arg,
119+
..
120+
}),
121+
) = (&a.left, &*a.right)
122+
{
123+
if let Expr::Ident(r_id) = &**arg {
124+
if l_id.id.eq_ignore_span(r_id) {
125+
report_change!("numbers: Turn a = +a into a *= 1");
126+
self.changed = true;
104127

105-
*e = Expr::Unary(UnaryExpr {
106-
span,
107-
op: op!(unary, "+"),
108-
arg: value,
109-
})
128+
a.op = op!("*=");
129+
a.right = Box::new(Expr::Lit(Lit::Num(Number {
130+
span: DUMMY_SP,
131+
value: 1.0,
132+
raw: None,
133+
})))
134+
}
135+
}
136+
}
110137
}
138+
_ => (),
111139
}
112140
}
113141
}

‎crates/swc_ecma_minifier/tests/benches-full/d3.js

+84-84
Large diffs are not rendered by default.

‎crates/swc_ecma_minifier/tests/benches-full/echarts.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4039,7 +4039,7 @@
40394039
/**
40404040
* Get precision
40414041
*/ function getPrecision(val) {
4042-
if (isNaN(val = +val)) return 0;
4042+
if (isNaN(val *= 1)) return 0;
40434043
// It is much faster than methods converting number to string as follows
40444044
for(// let tmp = val.toString();
40454045
// return tmp.length - 1 - tmp.indexOf('.');

‎crates/swc_ecma_minifier/tests/benches-full/jquery.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@
456456
* @param {Function} fn
457457
*/ function createPositionalPseudo(fn) {
458458
return markFunction(function(argument) {
459-
return argument = +argument, markFunction(function(seed, matches) {
459+
return argument *= 1, markFunction(function(seed, matches) {
460460
// Match elements found at the specified indexes
461461
for(var j, matchIndexes = fn([], seed.length, argument), i = matchIndexes.length; i--;)seed[j = matchIndexes[i]] && (seed[j] = !(matches[j] = seed[j]));
462462
});

‎crates/swc_ecma_minifier/tests/benches-full/lodash.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9611,7 +9611,7 @@
96119611
* _.map(['6', '08', '10'], _.parseInt);
96129612
* // => [6, 8, 10]
96139613
*/ function(string, radix, guard) {
9614-
return guard || null == radix ? radix = 0 : radix && (radix = +radix), nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);
9614+
return guard || null == radix ? radix = 0 : radix && (radix *= 1), nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);
96159615
}, lodash.random = /**
96169616
* Produces a random number between the inclusive `lower` and `upper` bounds.
96179617
* If only one argument is provided a number between `0` and the given number

‎crates/swc_ecma_minifier/tests/benches-full/victory.js

+55-55
Large diffs are not rendered by default.

‎crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js

+10-10
Original file line numberDiff line numberDiff line change
@@ -4838,7 +4838,7 @@
48384838
module.exports = !$expm1 || // Old FF bug
48394839
$expm1(10) > 22025.465794806719 || 22025.4657948067165168 > $expm1(10) || // Tor Browser bug
48404840
-0.00000000000000002 != $expm1(-0.00000000000000002) ? function(x) {
4841-
return 0 == (x = +x) ? x : x > -0.000001 && x < 1e-6 ? x + x * x / 2 : exp(x) - 1;
4841+
return 0 == (x *= 1) ? x : x > -0.000001 && x < 1e-6 ? x + x * x / 2 : exp(x) - 1;
48424842
} : $expm1;
48434843
/***/ },
48444844
/***/ 45404: /***/ function(module, __unused_webpack_exports, __webpack_require__) {
@@ -4858,7 +4858,7 @@
48584858
// https://tc39.es/ecma262/#sec-math.log1p
48594859
// eslint-disable-next-line es/no-math-log1p -- safe
48604860
module.exports = Math.log1p || function(x) {
4861-
return (x = +x) > -0.00000001 && x < 1e-8 ? x - x * x / 2 : log(1 + x);
4861+
return (x *= 1) > -0.00000001 && x < 1e-8 ? x - x * x / 2 : log(1 + x);
48624862
};
48634863
/***/ },
48644864
/***/ 62381: /***/ function(module) {
@@ -4867,7 +4867,7 @@
48674867
// eslint-disable-next-line es/no-math-sign -- safe
48684868
module.exports = Math.sign || function(x) {
48694869
// eslint-disable-next-line no-self-compare -- NaN check
4870-
return 0 == (x = +x) || x != x ? x : x < 0 ? -1 : 1;
4870+
return 0 == (x *= 1) || x != x ? x : x < 0 ? -1 : 1;
48714871
};
48724872
/***/ },
48734873
/***/ 50277: /***/ function(module, __unused_webpack_exports, __webpack_require__) {
@@ -5675,7 +5675,7 @@
56755675
// `ToInteger` abstract operation
56765676
// https://tc39.es/ecma262/#sec-tointeger
56775677
module.exports = function(argument) {
5678-
return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor : ceil)(argument);
5678+
return isNaN(argument *= 1) ? 0 : (argument > 0 ? floor : ceil)(argument);
56795679
};
56805680
/***/ },
56815681
/***/ 31998: /***/ function(module, __unused_webpack_exports, __webpack_require__) {
@@ -6734,7 +6734,7 @@
67346734
$acosh(1 / 0) != 1 / 0
67356735
}, {
67366736
acosh: function(x) {
6737-
return (x = +x) < 1 ? NaN : x > 94906265.62425156 ? log(x) + LN2 : log1p(x - 1 + sqrt(x - 1) * sqrt(x + 1));
6737+
return (x *= 1) < 1 ? NaN : x > 94906265.62425156 ? log(x) + LN2 : log1p(x - 1 + sqrt(x - 1) * sqrt(x + 1));
67386738
}
67396739
});
67406740
/***/ },
@@ -6749,7 +6749,7 @@
67496749
forced: !($asinh && 1 / $asinh(0) > 0)
67506750
}, {
67516751
asinh: function asinh(x) {
6752-
return isFinite(x = +x) && 0 != x ? x < 0 ? -asinh(-x) : log(x + sqrt(x * x + 1)) : x;
6752+
return isFinite(x *= 1) && 0 != x ? x < 0 ? -asinh(-x) : log(x + sqrt(x * x + 1)) : x;
67536753
}
67546754
});
67556755
/***/ },
@@ -6764,7 +6764,7 @@
67646764
forced: !($atanh && 1 / $atanh(-0) < 0)
67656765
}, {
67666766
atanh: function(x) {
6767-
return 0 == (x = +x) ? x : log((1 + x) / (1 - x)) / 2;
6767+
return 0 == (x *= 1) ? x : log((1 + x) / (1 - x)) / 2;
67686768
}
67696769
});
67706770
/***/ },
@@ -6777,7 +6777,7 @@
67776777
stat: !0
67786778
}, {
67796779
cbrt: function(x) {
6780-
return sign(x = +x) * pow(abs(x), 1 / 3);
6780+
return sign(x *= 1) * pow(abs(x), 1 / 3);
67816781
}
67826782
});
67836783
/***/ },
@@ -6926,7 +6926,7 @@
69266926
})
69276927
}, {
69286928
sinh: function(x) {
6929-
return 1 > abs(x = +x) ? (expm1(x) - expm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
6929+
return 1 > abs(x *= 1) ? (expm1(x) - expm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
69306930
}
69316931
});
69326932
/***/ },
@@ -6939,7 +6939,7 @@
69396939
stat: !0
69406940
}, {
69416941
tanh: function(x) {
6942-
var a = expm1(x = +x), b = expm1(-x);
6942+
var a = expm1(x *= 1), b = expm1(-x);
69436943
return a == 1 / 0 ? 1 : b == 1 / 0 ? -1 : (a - b) / (exp(x) + exp(-x));
69446944
}
69456945
});

‎crates/swc_ecma_minifier/tests/fixture/next/33265/static/chunks/pages/index-cb36c1bf7f830e3c/output.js

+17-17
Original file line numberDiff line numberDiff line change
@@ -6528,7 +6528,7 @@
65286528
var obj;
65296529
// Empty buffer means no match
65306530
if (0 === buffer.length) return -1;
6531-
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
6531+
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
65326532
(byteOffset = dir ? 0 : buffer.length - 1), byteOffset < 0 && (byteOffset = buffer.length + byteOffset), byteOffset >= buffer.length) {
65336533
if (dir) return -1;
65346534
byteOffset = buffer.length - 1;
@@ -6612,10 +6612,10 @@
66126612
if (offset + ext > buf.length || offset < 0) throw RangeError("Index out of range");
66136613
}
66146614
function writeFloat(buf, value, offset, littleEndian, noAssert) {
6615-
return value = +value, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 4, 3.4028234663852886e38, -340282346638528860000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 23, 4), offset + 4;
6615+
return value *= 1, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 4, 3.4028234663852886e38, -340282346638528860000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 23, 4), offset + 4;
66166616
}
66176617
function writeDouble(buf, value, offset, littleEndian, noAssert) {
6618-
return value = +value, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 8, 1.7976931348623157e308, -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 52, 8), offset + 8;
6618+
return value *= 1, offset >>>= 0, noAssert || checkIEEE754(buf, value, offset, 8, 1.7976931348623157e308, -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000), ieee754.write(buf, value, offset, littleEndian, 52, 8), offset + 8;
66196619
}
66206620
exports.Buffer = Buffer, exports.SlowBuffer = function(length) {
66216621
return +length != length && // eslint-disable-line eqeqeq
@@ -6872,57 +6872,57 @@
68726872
}, Buffer.prototype.readDoubleBE = function(offset, noAssert) {
68736873
return offset >>>= 0, noAssert || checkOffset(offset, 8, this.length), ieee754.read(this, offset, !1, 52, 8);
68746874
}, Buffer.prototype.writeUIntLE = function(value, offset, byteLength, noAssert) {
6875-
if (value = +value, offset >>>= 0, byteLength >>>= 0, !noAssert) {
6875+
if (value *= 1, offset >>>= 0, byteLength >>>= 0, !noAssert) {
68766876
var maxBytes = Math.pow(2, 8 * byteLength) - 1;
68776877
checkInt(this, value, offset, byteLength, maxBytes, 0);
68786878
}
68796879
var mul = 1, i = 0;
68806880
for(this[offset] = 0xff & value; ++i < byteLength && (mul *= 0x100);)this[offset + i] = value / mul & 0xff;
68816881
return offset + byteLength;
68826882
}, Buffer.prototype.writeUIntBE = function(value, offset, byteLength, noAssert) {
6883-
if (value = +value, offset >>>= 0, byteLength >>>= 0, !noAssert) {
6883+
if (value *= 1, offset >>>= 0, byteLength >>>= 0, !noAssert) {
68846884
var maxBytes = Math.pow(2, 8 * byteLength) - 1;
68856885
checkInt(this, value, offset, byteLength, maxBytes, 0);
68866886
}
68876887
var i = byteLength - 1, mul = 1;
68886888
for(this[offset + i] = 0xff & value; --i >= 0 && (mul *= 0x100);)this[offset + i] = value / mul & 0xff;
68896889
return offset + byteLength;
68906890
}, Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
6891-
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0xff, 0), this[offset] = 0xff & value, offset + 1;
6891+
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0xff, 0), this[offset] = 0xff & value, offset + 1;
68926892
}, Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
6893-
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
6893+
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
68946894
}, Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
6895-
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
6895+
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0xffff, 0), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
68966896
}, Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
6897-
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;
6897+
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;
68986898
}, Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
6899-
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;
6899+
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;
69006900
}, Buffer.prototype.writeIntLE = function(value, offset, byteLength, noAssert) {
6901-
if (value = +value, offset >>>= 0, !noAssert) {
6901+
if (value *= 1, offset >>>= 0, !noAssert) {
69026902
var limit = Math.pow(2, 8 * byteLength - 1);
69036903
checkInt(this, value, offset, byteLength, limit - 1, -limit);
69046904
}
69056905
var i = 0, mul = 1, sub = 0;
69066906
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;
69076907
return offset + byteLength;
69086908
}, Buffer.prototype.writeIntBE = function(value, offset, byteLength, noAssert) {
6909-
if (value = +value, offset >>>= 0, !noAssert) {
6909+
if (value *= 1, offset >>>= 0, !noAssert) {
69106910
var limit = Math.pow(2, 8 * byteLength - 1);
69116911
checkInt(this, value, offset, byteLength, limit - 1, -limit);
69126912
}
69136913
var i = byteLength - 1, mul = 1, sub = 0;
69146914
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;
69156915
return offset + byteLength;
69166916
}, Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
6917-
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;
6917+
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;
69186918
}, Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
6919-
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
6919+
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = 0xff & value, this[offset + 1] = value >>> 8, offset + 2;
69206920
}, Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
6921-
return value = +value, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
6921+
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 2, 0x7fff, -32768), this[offset] = value >>> 8, this[offset + 1] = 0xff & value, offset + 2;
69226922
}, Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
6923-
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;
6923+
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;
69246924
}, Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
6925-
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;
6925+
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;
69266926
}, Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
69276927
return writeFloat(this, value, offset, !0, noAssert);
69286928
}, Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {

0 commit comments

Comments
 (0)
Please sign in to comment.