Boshen
d55331815d
feat(minifier): complete MangleIf ( #8810 )
...
Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2025-02-02 17:21:17 +08:00
Daniel Bulant
6aa2ddeb85
refactor(codegen): accept SymbolTable instead of Mangler ( #8829 )
...
It seems to be that the easiest way to rename variables is to use
rename_symbol from SymbolTable and to let it be applied during codegen -
this is what Mangler does.
This PR changes Codegen to accept (any) SymbolTable instead of just
Mangler itself, and for Mangler build output to be a new SymbolTable,
after consuming itself (which makes it clear at the type level if a
Mangler has been built or not).
This also moves the few symbol table helper methods from Mangler to
SymbolTable itself, and adds an example of Mangler use to it's
documentation.
Codegen dependencies now include semantic (which was transient by
mangler before). It's dependency on Mangler could be removed, though
I've left it to allow easy linking of docs (with_symbol_table points
users to Mangler, hopefully helps with migration?)
2025-02-02 17:13:09 +08:00
sapphi-red
5cfea76bc3
feat(minifier): compress (a = _a) != null ? a : b and (a = _a) != null ? a.b() : undefined ( #8823 )
...
Compresses `(a = _a) != null ? a : b` to `(a = _a) ?? b` and `(a = _a) != null ? a.b() : undefined` to `(a = _a).b()`. This commonly happens when lowered.
2025-02-02 01:09:20 +00:00
sapphi-red
d9f1d0daa1
feat(minifier): merge expressions in for-in statement head ( #8811 )
...
Compress `a; for (var b in c) d` into `for (var b in a, c) d`. This is possible when the left hand does not have a sideeffectful initializer. (Initializers on the left hand of for-in is Annex B thing.)
**References**
- [Spec of `ForIn/OfHeadEvaluation`](https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-runtime-semantics-forinofheadevaluation ): `c` in the example above is passed as `expr` to this abstract operation. No side effect exists before Step 3.
- [Spec of the initializer in ForIn](https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-initializers-in-forin-statement-heads ): See "The runtime semantics of ForInOfLoopEvaluation in 14.7.5.5 are augmented with the following:" part. Evaluation of Initializer is executed before `ForIn/OfHeadEvalution`. This is the reason why it cannot be compressed when a sideeffectful initializer exists.
2025-01-31 15:31:24 +00:00
sapphi-red
e353a018f0
feat(minifier): compress a != null ? a.b : undefined to a?.b ( #8802 )
2025-01-31 07:27:49 +00:00
sapphi-red
72d74a2fdc
feat(minifier): compress a != null ? a : b into a ?? b ( #8801 )
...
It seems this was implemented in past by #8352 and was reverted in #8411 . I'm not sure what was buggy, but one difference with this PR is that the previous PR doesn't check whether the identifier is a global reference or not.
2025-01-31 06:47:47 +00:00
Boshen
a861d93722
refactor(minifier): port esbuild's mangleStmts ( #8770 )
2025-01-31 13:43:10 +08:00
sapphi-red
7ea99f40ac
feat(minifier): compress array of string literals to 'str1,str2'.split(',') ( #8786 )
...
Ported `["str1", "str2", ...]` => `"str1 str2".split(" ")` compression from closure compiler with some tweaks.
2025-01-31 04:13:41 +00:00
sapphi-red
86b6219d26
feat(mangler): use characters in the order of their likely frequency ( #8771 )
...
Just noticed that we can use a static list here. I think this has no downsides. To have better compression, we can actually count the characters, but I guess there won't be much difference normally.
2025-01-29 02:20:44 +00:00
翠 / green
66c33ed0af
fix(minifier): remove incorrect not + conditional expression compression ( #8759 )
...
```js
var foo = (a) => {
if (!(a == null ? void 0 : a.b))
return
a.b(to);
};
foo(null)
```
was minified into
```js
var foo = (a) => {
if (a == null && a.b) return;
a.b(to);
};
foo(null);
```
which is incorrect (no error happens before and error happens after).
I found this while trying rolldown-vite with ecosystem-ci.
refs #8651
2025-01-28 13:30:00 +08:00
sapphi-red
ad14403f9e
feat(minifier): compress typeof a.b === 'undefined' to a.b === void 0 ( #8751 )
...
The special behavior of `typeof` is only for the identifier, so it should be safe to compress `typeof a.b === 'undefined'` (and other expressions) to `a.b === void 0`.
2025-01-27 14:31:13 +00:00
sapphi-red
f7f2d2f93a
feat(minifier): compress a == null && b to a ?? b when return value is ignored ( #8749 )
...
Compress `a == null && b` to `a ?? b` when return value is ignored
When `a` is `null` or `undefined`, `a == null && b` returns `b`.
When `a` is not `null` or `undefined`, `a == null && b` returns `false`.
When `a` is `null` or `undefined`, `a ?? b` returns `b`.
When `a` is not `null` or `undefined`, `a ?? b` returns `a`.
**References**
- [Spec of `??`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators-runtime-semantics-evaluation )
2025-01-27 14:31:12 +00:00
sapphi-red
3c1c92cac0
feat(minifier): support a[0] and this.a in has_no_side_effect_for_evaluation_same_target ( #8748 )
...
Add support for `a[0]` and `this.a` in `has_no_side_effect_for_evaluation_same_target` function.
2025-01-27 14:31:11 +00:00
Boshen
29417ddc03
feat(minifier): minimize !(a, b) -> a, !b ( #8746 )
2025-01-27 08:24:07 +00:00
sapphi-red
e0117db531
feat(minifier): replace const with let for non-exported read-only variables ( #8733 )
...
Replace `const` with `let` when that value does not have any assignments and not exposed.
2025-01-26 12:44:01 +00:00
sapphi-red
360d49e962
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 )
2025-01-26 11:24:19 +00:00
Boshen
e9fb5febdf
feat(minifier): dce pure expressions such as new Map() ( #8725 )
2025-01-26 18:08:34 +08:00
Boshen
0af0267077
refactor(minifier): side effect detection needs symbols resolution ( #8715 )
2025-01-25 15:01:00 +00:00
翠 / green
6589c3bbb3
feat(mangler): reuse variable names ( #8562 )
...
Changed the mangler to reuse variable names where possible.
This will reduce the code size as shorter variable names can be used in
more places. But requires global information and limits parallelism in a
single file and requires more memory.
---------
Co-authored-by: Boshen <boshenc@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-01-25 14:00:24 +08:00
Boshen
8587965e45
perf(minifier): normalize undefined to void 0 before everything else ( #8699 )
...
so subsequent code don't need to lookup `undefined`.
2025-01-25 11:50:08 +08:00
sapphi-red
343690e178
feat(minifier): replace Number.*_SAFE_INTEGER/Number.EPSILON ( #8682 )
...
The value of `Number.*_SAFE_INTEGER`, `Number.EPSILON` are constants as they cannot be changed. This PR replaces them with `2**53-1` / `-(2**53-1)` / `2**-52` for ES2016+. For ES2015, `Number.EPSILON` is not changed but `Number.*_SAFE_INTEGER`s are replaced with `9007199254740991` / `-9007199254740991`.
**Reference**
- Spec of [`Number.MAX_SAFE_INTEGER`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.max_safe_integer )
- Spec of [`Number.MIN_SAFE_INTEGER`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.min_safe_integer )
- Spec of [`Number.EPSILON`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.epsilon )
### Additional Information
- [`Number.MIN_VALUE`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.min_value ) cannot be replaced as the value depends on the runtime
- [`Number.MAX_VALUE`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.max_value ) can be replaced but I didn't come up with a shorter representation that does not lack precision
2025-01-24 03:47:52 +00:00
sapphi-red
0c5bb30859
feat(minifier): replace Number.POSITIVE_INFINITY/Number.NEGATIVE_INFINITY/Number.NaN ( #8681 )
...
The value of `Number.POSITIVE_INFINITY`, `Number.NEGATIVE_INFINITY`, `Number.NaN` are constants as they cannot be changed. This PR replaces them with `Infinity`/`-Infinity`/`NaN`.
**Reference**
- Spec of [`Number.POSITIVE_INFINITY`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.positive_infinity )
- Spec of [`Number.NEGATIVE_INFINITY`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.positive_infinity )
- Spec of [`Number.NaN`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.positive_infinity )
2025-01-23 15:26:22 +00:00
Boshen
ba201a6511
fix(minifier): remove "non esbuild optimizations" which is incorrect ( #8668 )
2025-01-23 03:45:42 +00:00
Boshen
3802d28233
refactor(minifier): clean up try_minimize_conditional ( #8660 )
2025-01-22 15:17:47 +00:00
Boshen
9953ac7cad
perf(minifier): add LatePeepholeOptimizations ( #8651 )
...
This PR adds a `LatePeepholeOptimizations` pass for minifications that
don't interact with the fixed point loop.
While working on this I found a couple of cases where the previous fixed
point loop is not idempotent.
2025-01-22 16:19:06 +08:00
Boshen
5b3c412e26
perf(minifier): only run optimizations on local changes ( #8644 )
...
Previously all code are ran in a fixed point loop when ast changes.
This PR changes running code when a function changes its enclosing ast only.
2025-01-21 23:55:54 +08:00
sapphi-red
835b25889b
feat(minifier): compress typeof foo === 'object' && foo !== null to typeof foo == 'object' && !!foo ( #8638 )
...
If `typeof foo == 'object'`, then `foo` is guaranteed to be an object or null. In that case, `foo !== null` can be replaced with `!!foo` because objects return `true` for `!!foo` and null returns `false` for it.
**References**
- [Spec of `typeof`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-typeof-operator )
- [Spec of `!`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-logical-not-operator )
- [Spec of `ToBoolean`](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toboolean )
2025-01-21 14:59:25 +00:00
sapphi-red
2bcbed2d50
feat(minifier): compress (a = b) === null || a === undefined to (a = b) == null ( #8637 )
2025-01-21 14:59:23 +00:00
Boshen
6f95cd599a
refactor(minifier): remove all the unnecessary fake ast passes ( #8618 )
...
This also removes handling of making cjs-module-lexer to work.
2025-01-20 22:05:51 +08:00
翠 / green
8c8b5fa9be
fix(minifier): avoid minifing String(a) into "" + a for symbols ( #8612 )
...
We shouldn't change `String(a)` into `"" + a` if `a` can be a Symbol.
`String(Symbol())` does not throw an error, but `"" + Symbol()` does.
**References**
- [Spec of `ToString` (called for `"" +
variable`)](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tostring )
- [Spec of
`String(a)`](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value )
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-01-20 13:34:46 +08:00
Boshen
2a2a2eb5ea
chore(tasks/minsize): enable test coverage
2025-01-19 12:00:40 +08:00
Boshen
7b219a930f
fix(minifier): fix dce shadowed undefined ( #8582 )
2025-01-18 09:04:28 +00:00
camc314
4d4e805691
feat(minifier): collapse if stmt with empty consequent ( #8577 )
2025-01-18 03:51:40 +00:00
sapphi-red
991a22f907
feat(minifier): fold Array::concat into literal ( #8442 )
...
Compress `[].concat(a, [b])` into `[a, b]`.
**References**
- [Spec of `Array::concat`](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.concat )
2025-01-17 06:00:07 +00:00
sapphi-red
3dc2d8b8e9
feat(minifier): fold string concat chaining ( #8441 )
...
Compress `"".concat(a).concat(b)` into `"".concat(a, b)`.
**References**
- [Spec of `String::concat`](https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.concat )
2025-01-17 06:00:06 +00:00
sapphi-red
a4ae4505f1
feat(minifier): fold array concat chaining ( #8440 )
...
Compress `[].concat(a).concat(b)` into `[].concat(a, b)`.
**References**
- [Spec of `Array::concat`](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.concat )
---
### The new assumption
I added a new assumption description in `crates/oxc_minifier/README.md`: "Errors thrown when creating a String or an Array that exceeds the maximum length can disappear or moved".
This is an assumption held by other minifiers. Without this assumption, we have to treat `+` and array creation/update to have a sideeffect and that limits the minification.
For input:
```js
[...Array(Number(2n ** 32n - 1n)),""]
" ".repeat(Number(2n ** 29n - 24n - 1n)) + ' '
export {}
```
(Note that `2 ** 32 - 1` is the max array length and `2 ** 29 - 24` is the max string length on V8.)
This code errors with too long array and too long string on V8 based runtimes.
terser outputs:
```js
Array(Number(2n**32n-1n))," ".repeat(Number(536870887n));export{};
```
No error will happen with this code.
SWC outputs:
```js
[...Array(Number(2n**32n-1n))]," ".repeat(Number(2n**29n-24n-1n));export{};
```
No error will happen with this code.
[playground](https://play.swc.rs/?version=1.10.7&code=H4sIAAAAAAAAA4vW09NzLCpKrNTwK81NSi3SMMpT0NJSMAZSugqGeZqaOkpKsbxcSgpKekWpBamJJajqjCxB6oxMYKoVtBXUFdR5uVIrCvKLShSqa3m5AGRqy%2FBiAAAA&config=H4sIAAAAAAAAA32UO3LjMAyG%2B5zCozrFjostcoDtcgYOTYIyvXxoCNCxJuO7L0RJtjeG3En48AMkAOL7bbfrTmi6j903f%2FLPoAtCuf2zBcdE%2BsKWDkzUaIofqHtf6Qkn5HRAaKbrTDrSpQdqKtz%2F2u8XRRdyRlgViy365N34mNPkOBRAfLCxlUPWCInwf%2F3CSv6aAJX6aD%2FkHECnF0RpVD4R9FCkwCaHoAcEddZFiDKdVBePWUoxwUpg1VDyIPJkPfmcOOcztaCtMtmCgHwBQ%2F4MkoxzsSwhX0%2B4T8MWDrXvW59%2FqOGsQ9Uk5IRLawmfVoh6zB5JuZqkEs5wowYzXIr7U%2BmdKkC1pGfdKfu00ZO%2FAFyBoBGTjiDFbR6O52lL7V4qfXI8sjQKnOdbumWCnouqvHdCZafKQCEvdbOArQamyhrpOAveKB96Cwqc41kRQuOXJ3OUktI4QHYC4P5qJ03VDNTtFW7w6UG8wH%2F4liQP2OIRNR23KY7xkMOLBBHomO0LB24F5W1ceEtchm1ekwUeDbCiS8UGnpcAPwDKKrR9%2BTQb%2FDw4oupDPtzXxOJwve3hqFN%2Ff%2B%2FzKn5bHLqYbW1wWfJTf%2BfV%2FLu7O61beD1B5%2FFzFbac13%2FKOhgeLwYAAA%3D%3D )
esbuild outputs:
```js
[...Array(Number(2n**32n-1n))]," ".repeat(Number(2n**29n-24n-1n))+"";export{};
```
No error will happen with this code.
[esbuild try](https://esbuild.github.io/try/#dAAwLjI0LjIALS1taW5pZnkAWy4uLkFycmF5KE51bWJlcigybiAqKiAzMm4gLSAxbikpLCIiXQoiICIucmVwZWF0KE51bWJlcigybiAqKiAyOW4gLSAyNG4gLSAxbikpICsgJyAnCmV4cG9ydCB7fQ )
OXC outputs:
```js
[...Array(Number(2n**32n-1n))]," ".repeat(Number(2n**29n-24n-1n))+" ";export{};
```
The array error won't happen and the String error will happen with this code.
[playground](https://playground.oxc.rs/#eNpVT71OwzAQfhXrlv4QIhpgIBtLR8SOGZxwCUH22To7baMo746dNEVMd5/u+7sRaijhI8/zV2Y1bN96UyFvCxL7vXiM414caLfLJEj4lCRBSMgZHarwn1u8JG7xtCrEndiIjSS8OMtBjJMkyMBCOQL3lIYfKKgLlIF7zEB3FKBslPYR+No6XC9+MJXVKwqsyDeWzZU8ZeAUe+TZ0vZc47HTSMpEAwjoQ/7jY7JjjKQTvitG8n/ilDuL437zXtyC4hZjKUBfPByeITJq+4UtpvoRmI66plu4tTUpIRovNc/fXcx2qr69YRS1+opmJwps9VHbc9KfkCvr43npNU2/v/WIsw== )
2025-01-17 05:53:54 +00:00
Boshen
b1d018622b
fix(minifier): do not fold !!void b ( #8533 )
2025-01-16 05:52:03 +00:00
Boshen
53adde5003
fix(minifier): x['-2147483648'] -> x[-2147483648] ( #8528 )
2025-01-16 13:43:23 +08:00
Boshen
1860411656
feat(minifier): remove last redundant return statement ( #8523 )
2025-01-16 02:07:28 +00:00
Boshen
8accfefa74
feat(minifier): minify var x; void x -> void 0 ( #8466 )
2025-01-13 16:07:59 +00:00
Boshen
3c9354983d
fix(minifier): dce if statement should keep side effects and vars ( #8433 )
...
closes #7209
Note: Current output is sub-optimal.
2025-01-11 14:02:12 +00:00
sapphi-red
d56020b84c
feat(minifier): drop 0 from new Int8Array(0) and other TypedArrays ( #8431 )
...
Compresses `new Int8Array(0)` into `new Int8Array()`. (then will be compress into `new Int8Array`).
Partial quote from the [spec](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-typedarray ):
> 5. If numberOfArgs = 0, then
> a. Return ? AllocateTypedArray(constructorName, NewTarget, proto, 0).
> 6. Else,
> c. Else,
> ii. Let elementLength be ? ToIndex(firstArgument).
> iii. Return ? AllocateTypedArray(constructorName, NewTarget, proto, elementLength).
2025-01-11 07:45:36 +00:00
sapphi-red
f935d9434f
feat(minifier): remove new from NativeErrors / AggregateError ( #8430 )
...
Remove `new` in the some cases:
- NativeErrors (e.g. `new EvalError(...)` -> `EvalError(...)`): [spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-nativeerror-constructors:~:text=the%20function%20call%20NativeError(%E2%80%A6)%20is%20equivalent%20to%20the%20object%20creation%20expression%20new%20NativeError(%E2%80%A6)%20with%20the%20same%20arguments .), [the list of NativeErrors in spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-native-error-types-used-in-this-standard )
- `new AggregateError(...)` -> `AggregateError(...)`: [spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-aggregate-error-constructor:~:text=the%20function%20call%20AggregateError(%E2%80%A6)%20is%20equivalent%20to%20the%20object%20creation%20expression%20new%20AggregateError(%E2%80%A6)%20with%20the%20same%20arguments .)
2025-01-11 07:23:07 +00:00
Boshen
dab7a51e78
feat(minifier): minimize not !(x === undefined) -> x !== undefined ( #8429 )
2025-01-11 06:30:36 +00:00
Boshen
357b61d179
fix(minifier): do not minify Object.defineProperty in sequence expressions ( #8416 )
2025-01-10 12:04:39 +00:00
Boshen
5b5b8443f4
feat(minifier): fold ambiguous if else ( #8415 )
2025-01-10 19:51:31 +08:00
Boshen
fb2acd87b3
refactor(minifier): change minimize conditionals into a loop ( #8413 )
2025-01-10 09:49:09 +00:00
Boshen
baaec6020c
refactor(minifier): remove the buggy ?? transform ( #8411 )
...
e.g. `(a != null ? a : b);` is not `a ?? b`
There are also no unit tests.
2025-01-10 09:33:09 +00:00
Boshen
438a6e7abc
feat(minifier): minimize conditions in boolean context ( #8381 )
2025-01-10 03:47:10 +00:00
camc314
793cb43138
feat(minifier): a != null ? a : b -> a ?? b ( #8352 )
2025-01-09 10:12:52 +00:00