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
sapphi-red
814da55f81
feat(minifier): compress x = x || 1 to x ||= 1 ( #8368 )
...
The simplified version of the evaluation of `a = a || b` is:
> AssignmentExpression : LeftHandSideExpression = LogicalORExpression || LogicalANDExpression
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let llRef be ? Evaluation of LogicalORExpression.
> 3. Let llVal be ? GetValue(llRef).
> 4. If ToBoolean(llVal) is true
> a. Perform ? PutValue(lRef, llVal).
> b. return llVal.
> 5. Let lrRef be ? Evaluation of LogicalANDExpression.
> 6. Let rRef be ? GetValue(lrRef).
> 7. Let rVal be ? GetValue(rRef). [Note GetValue(rRef) returns rRef itself]
> 8. Perform ? PutValue(lRef, rVal).
> 9. Return rVal.
The simplified version of the evaluation of `a ||= b` is:
> AssignmentExpression : LeftHandSideExpression ||= AssignmentExpression
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let lVal be ? GetValue(lRef).
> 3. If ToBoolean(lVal) is true, return lVal.
> 4. Let rRef be ? Evaluation of AssignmentExpression.
> 5. Let rVal be ? GetValue(rRef).
> 6. Perform ? PutValue(lRef, rVal).
> 7. Return rVal.
The difference of these is that
- the evaluation of `a` is done twice for `a = a || b`, one with `1. Let lRef be ? Evaluation of LeftHandSideExpression` and one with `2. Let llRef be ? Evaluation of LogicalORExpression.`. This is same with #8366 , #8367 .
- `PutValue(lRef, llVal)` is performed when `ToBoolean(lVal)` is `true`.
So `x = x || 1` can be compressed to `x ||= 1` when the conditions written in #8366 are met and `PutValue(lRef, llVal)` does not have a side effect. When `a` is a non-global identifier (and not a reference created by a `with` statement), these conditions are met.
**References**
- [Spec of `||`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators-runtime-semantics-evaluation )
- [Spec of `=` / `||=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators-runtime-semantics-evaluation )
2025-01-09 03:47:12 +00:00
sapphi-red
a596821d85
feat(minifier): compress a.b = a.b + c to a.b += c ( #8367 )
...
The simplified version of the evaluation of `a += b` is:
> AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let lVal be ? GetValue(lRef).
> 3. Let rRef be ? Evaluation of AssignmentExpression.
> 4. Let rVal be ? GetValue(rRef).
> 5. Let r be ? ApplyStringOrNumericBinaryOperator(lVal, opText, rVal).
> 6. Perform ? PutValue(lRef, r).
> 7. Return r.
The simplified version of the evaluation of `a = a + b` is:
> AssignmentExpression : LeftHandSideExpression = AssignmentExpressionLeft + AssignmentExpressionRight
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let alRef be ? Evaluation of AssignmentExpressionLeft.
> 3. Let alVal be ? GetValue(alRef).
> 4. Let arRef be ? Evaluation of AssignmentExpressionRight.
> 5. Let arVal be ? GetValue(arRef).
> 6. Let rRef be ? ApplyStringOrNumericBinaryOperator(alVal, opText, arVal).
> 7. Let rVal be ? GetValue(rRef). [Note GetValue(rRef) returns rRef itself]
> 8. Perform ? PutValue(lRef, rVal).
> 9. Return rVal.
The difference of these is that the evaluation of `a` is done twice for `a = a + b`, one with `1. Let lRef be ? Evaluation of LeftHandSideExpression` and one with `2. Let alRef be ? Evaluation of AssignmentExpressionLeft.`
So this is same with #8366 and can be compressed similarly when the conditions are met (`a.b = a.b + c` -> `a.b += c`).
**References**
- [Spec of `=`, `+=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-assignment-operators-runtime-semantics-evaluation )
- [Spec of `+`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-addition-operator-plus-runtime-semantics-evaluation )
2025-01-09 03:47:11 +00:00
sapphi-red
579eb603d5
feat(minifier): compress a.b || (a.b = c) to a.b ||= c ( #8366 )
...
As noted in https://github.com/oxc-project/oxc/pull/8315 , the only difference of `x ||= y` and `x || (x = y)` is that `Let lRef be ? Evaluation of LeftHandSideExpression` is done twice.
If the LeftHandSideExpression is a MemberExpression, `GetValue(baseReference)` is the only place that might have a side effect. Further more, if the `baseReference` is an IdentifierReference that is not a global reference (and not a reference created by a `with` statement), `GetValue(baseReference)` does not have a side effect.
When those conditions are met, `a.b || (a.b = c)` can be compress to `a.b ||= c`.
**References**
- [Spec of `a.b`](<https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors-runtime-semantics-evaluation:~:text=%2C%20strict ).-,MemberExpression,4.%20Return%20MakePrivateReference(baseValue%2C%20fieldNameString).,-CallExpression>)
- [Spec of `GetValue`](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-getvalue )
- [Spec of `IdentifierReference`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-identifiers-runtime-semantics-evaluation )
- [Spec of `DeclarativeEnvironmentRecord.GetBindingValue`](https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-declarative-environment-records-getbindingvalue-n-s )
2025-01-09 03:47:10 +00:00
camc314
f367a16364
feat(minifier): port esbuild conditional expr minification ( #8351 )
...
still some TODOs, but the main framework is there
2025-01-09 02:49:33 +00:00
Boshen
09f0f483f6
refactor(minifier): remove the buggy minimize_exit_points implementation ( #8349 )
2025-01-09 02:49:32 +00:00
sapphi-red
8d52cd0a5e
feat(minifier): merge assign expression in conditional expression ( #8345 )
...
compresses `a ? b = 0 : b = 1` into `b = a ? 0 : 1`
This can be done when `b` is an IdentifierReference and the assignment operator is `=`.
In this circumstance, the evaluation of `b = a ? 0 : 1` is:
1. Let lref be ? Evaluation of LeftHandSideExpression. (this does not have a side effect when LeftHandSideExpression is an IdentifierReference)
2. Let rref be ? Evaluation of AssignmentExpression. (ConditionalExpression is evaluated here)
3. Let rval be ? GetValue(rref).
4. Perform ? PutValue(lref, rval).
5. Return rval.
**References**
- [spec of `=`](https://262.ecma-international.org/15.0/index.html#sec-assignment-operators-runtime-semantics-evaluation )
- [spec of `? :`](https://262.ecma-international.org/15.0/index.html#sec-conditional-operator-runtime-semantics-evaluation )
2025-01-08 13:34:12 +00:00
sapphi-red
a69d15f299
feat(minifier): compress new Array(2) -> [,,] ( #8344 )
...
For an integer value `n` smaller than 6, `Array(n)` can be compressed to `[,,]` (the number of `,` is `n`).
2025-01-08 12:07:46 +00:00
Boshen
6220e05f38
feat(minifier): remove empty if statment if (test) {} -> test ( #8336 )
2025-01-08 08:50:10 +00:00
sapphi-red
ec88c68c28
feat(minifier): compress a || (a = b) to a ||= b ( #8315 )
...
Compress `a || (a = b)` to `a ||= b` and for other logical operators that are possible to.
I didn't find other minifiers doing this, but this is safe for identifiers. [Quoting MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment#description ):
> Logical OR assignment short-circuits, meaning that `x ||= y` is equivalent to `x || (x = y)`, except that the expression x is only evaluated once.
I actually checked the spec and the only difference was that `Let lRef be ? Evaluation of LeftHandSideExpression` was done twice. Evaluating an IdentifierReference is idempotent so it should be safe to do this compression.
References:
- [Spec of `&&=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-assignment-operators:~:text=Return%20r.-,AssignmentExpression,7.%20Return%20rVal.,-AssignmentExpression )
- [Spec of `=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-AssignmentExpression:~:text=Runtime%20Semantics%3A%20Evaluation-,AssignmentExpression,6.%20Return%20rVal.,-AssignmentExpression )
- [Spec of `&&`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators:~:text=Runtime%20Semantics%3A%20Evaluation-,LogicalANDExpression,5.%20Return%20%3F%C2%A0GetValue(rRef).,-LogicalORExpression )
I think this is safe for `a.b || (a.b = foo)` as well because the number of `GetValue` and `SetValue` does not change, but I didn't include that in this PR.
2025-01-07 23:19:52 +00:00
sapphi-red
e6fe84d674
feat(minifier): compress a = a + b to a += b ( #8314 )
...
Compress `a = a + b` to `a += b` and for other binary operators that are possible to.
2025-01-07 23:19:50 +00:00
sapphi-red
9ea4e31ba3
feat(minifier): remove new from new Error/new Function/new RegExp ( #8313 )
...
Remove `new` from some cases:
- `new Error(...)` -> `Error(...)`: `new` can be removed unconditionally, [spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-error-constructor:~:text=Thus%20the%20function%20call%20Error(%E2%80%A6)%20is%20equivalent%20to%20the%20object%20creation%20expression%20new%20Error(%E2%80%A6)%20with%20the%20same%20arguments .)
- `new Function(...)` -> `Function(...)`: `new` can be removed unconditionally, [spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-function-constructor:~:text=Thus%20the%20function%20call%20Function(%E2%80%A6)%20is%20equivalent%20to%20the%20object%20creation%20expression%20new%20Function(%E2%80%A6)%20with%20the%20same%20arguments .)
- `new RegExp(nonRegexp)` -> `RegExp(nonRegexp)`: `new` can be removed if there's no argument or the first argument is not regex object, [mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp#return_value ), [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-regexp-constructor:~:text=when%20called%20as%20a%20function%20rather%20than%20as%20a%20constructor%2C%20returns%20either%20a%20new%20RegExp%20object%2C%20or%20the%20argument%20itself%20if%20the%20only%20argument%20is%20a%20RegExp%20object .). I made this to happen if the first argument is not an object.
2025-01-07 14:48:56 +00:00
Boshen
051fbb6909
feat(minifier): minimize x["0"] -> x[0] ( #8316 )
2025-01-07 13:12:35 +00:00
Boshen
a542013773
feat(minifier): minimize do{}while(true) -> do;while(true) ( #8311 )
2025-01-07 12:55:03 +00:00
Boshen
e3ff81ef82
feat(minifier): minimize (x = 1) === 1 -> (x = 1) == 1 ( #8310 )
2025-01-07 07:48:35 +00:00
camc314
5ed439bcaf
feat(minifier): minify typeof in binary expressions ( #8302 )
2025-01-06 23:26:19 +00:00
Boshen
b8d26eab17
refactor(minifier): move optional catch param to peephole_substitute_alternate_syntax ( #8282 )
2025-01-06 10:47:56 +00:00
Boshen
f87da160df
fix(minifier): do not fold literals in -0 != +0 ( #8278 )
2025-01-06 10:08:57 +00:00
camc314
e446c15619
feat(minifier): improve minimizing unary not expressions ( #8261 )
2025-01-06 02:21:26 +00:00
camc314
7f19211736
feat(minifier): minimize unary expression statements ( #8256 )
...
`delete` cannot be minified as it may have side effect.
2025-01-06 02:21:24 +00:00
Boshen
4d8a08d2ac
feat(minifier): improve constant evaluation ( #8252 )
2025-01-05 12:41:57 +00:00
Boshen
2f52f333fa
feat(minifier): minsize !!!foo ? bar : baz -> foo ? baz : bar ( #8244 )
2025-01-04 09:10:22 +00:00
Boshen
ce2b5a994b
chore(minifier): disable RemoveUnusedCode ( #8243 )
...
A lot of edge cases fail in test262 and monitor-oxc.
2025-01-04 08:49:20 +00:00
Boshen
ad9a0a9c4a
feat(mininifier): minimize variants of a instanceof b == true ( #8241 )
2025-01-04 06:04:52 +00:00
Boshen
ccdc039f54
feat(minifier): always put literals on the rhs of equal op 1==x => x==1 ( #8240 )
2025-01-04 04:07:54 +00:00
Boshen
bf0fbcea6e
refactor(minifier): improve constant fold numbers ( #8239 )
...
Ported esbuild's implementation
2025-01-04 03:30:20 +00:00
Cameron
39353b22e9
feat(minifier): improve minimizing conditionals ( #8238 )
2025-01-04 08:36:54 +08:00
camc314
c90fc16bba
feat(minifier): restore conditional minification and fix edge case ( #8235 )
...
This restore's the changes made in #8233 , but fixing the edge cases.
If the conditional expression is not a child of an `IfStatementTest`, `WhileStatementTest`, `DoWhileStatementText` or `ExpressionStatementExpression`, we must coerce the test to a boolean.
2025-01-03 14:20:42 +00:00
Boshen
6c8ee9fdef
feat(minifier): remove last redundant return statement ( #8234 )
2025-01-03 12:24:57 +00:00
Boshen
a698deff51
fix(minifier): fix incorrect return value for (x ? true : y) ( #8233 )
...
Need to check if the return value is used or not:
```
function foo() {
return foo ? true : bar; // no transformed
}
foo ? true : bar; // transformed to `foo || bar;`
```
2025-01-03 11:58:28 +00:00
Boshen
51f47926ef
feat(minifier): minimize foo ? foo : bar and foo ? bar : foo ( #8229 )
2025-01-03 10:55:59 +00:00
Boshen
6e2ec17d51
feat(minifier): statement fusion switch cases; improved minimize exit poitns ( #8228 )
2025-01-03 10:07:04 +00:00
Boshen
574a2428fd
feat(minifier): minimize all variants of typeof x == 'undefined' ( #8227 )
2025-01-03 07:05:55 +00:00