fix(minifier): allow argument rewrite with with leading var assignments (#28056)

allow rewrite of `arguments` loop with leading empty var assignments.

```js
function _() { for (var keep, r = [], a = 0; a < arguments.length; a++) r[a] = arguments[a]; use(r, keep) }

function _() { var keep, r = [...arguments]; use(r, keep) }
```
This commit is contained in:
Armano authored and GitHub committed 2026-10-21 15:46:36 -07:00
1 parent 4629bb294a
commit 09bdb80a45
3 files changed
+31 -6

No files matched your search

@@ -855,7 +855,18 @@ impl<'a> PeepholeOptimizations {
let Some(init) = &mut for_stmt.init else { return };
let ForStatementInit::VariableDeclaration(var_init) = init else { return };
let Some(siblings) = var_init.declarations.get(init_decl_len..) else { return };
let leading_var_count = if var_init.kind.is_var() {
let Some(var_position) =
var_init.declarations.iter().position(|decl| decl.init.is_some())
else {
return;
};
var_position
} else {
0
};
let pattern_end = leading_var_count + init_decl_len;
let Some(siblings) = var_init.declarations.get(pattern_end..) else { return };
// Sibling declarators may be in the source loop or folded into it by `sequences`.
// Keep uninitialized `var` siblings, but don't move an initializer across the spread.
if !siblings.is_empty()
@@ -873,7 +884,7 @@ impl<'a> PeepholeOptimizations {
return;
}
let mut idx = 0usize;
let mut idx = leading_var_count;
// Check `e = arguments.length`
if let Some((e_id_name, _)) = e_id_info {
@@ -1012,13 +1023,13 @@ impl<'a> PeepholeOptimizations {
let new_decl = VariableDeclarator::new(SPAN, r_id_pat, None, Some(arr), false, ctx);
// Drop only the copy-loop declarators. Siblings remain in the `var` statement.
// Walk removed declarators so refs inside them reach `PassChanges`.
for decl in var_init.declarations.drain(..init_decl_len) {
for decl in var_init.declarations.drain(leading_var_count..pattern_end) {
ctx.drop_variable_declarator(&decl);
}
var_init.declarations.insert(0, new_decl);
var_init.declarations.insert(leading_var_count, new_decl);
} else {
// The copied array is unused, but sibling declarations still need to survive.
for decl in var_init.declarations.drain(..init_decl_len) {
for decl in var_init.declarations.drain(leading_var_count..pattern_end) {
ctx.drop_variable_declarator(&decl);
}
// Avoid invalid `for (var; 0;)` when no siblings remain.
@@ -1078,6 +1078,14 @@ fn test_rewrite_arguments_copy_loop() {
"function _() { for (var r = [], a = 0, keep; a < arguments.length; a++) r[a] = arguments[a]; use(r, keep) }",
"function _() { var r = [...arguments], keep; use(r, keep) }",
);
test(
"function _() { for (var keep, r = [], a = 0; a < arguments.length; a++) r[a] = arguments[a]; use(r, keep) }",
"function _() { var keep, r = [...arguments]; use(r, keep) }",
);
test(
"function _() { for (var first, second, r = [], a = 0; a < arguments.length; a++) r[a] = arguments[a]; use(r, first, second) }",
"function _() { var first, second, r = [...arguments]; use(r, first, second) }",
);
test(
"function _() { var items = [], t = 0, i, u, r; for (; t < arguments.length; t++) items[t] = arguments[t]; if (this.list = [], items != null) for (i = 0, u = items; i < u.length; i++) r = u[i], this.list.push(r) }",
"function _() { var items = [...arguments], i, u, r; if (this.list = [], items != null) for (i = 0, u = items; i < u.length; i++) r = u[i], this.list.push(r) }",
@@ -1089,6 +1097,12 @@ fn test_rewrite_arguments_copy_loop() {
test_same(
"function _() { for (var r = [], a = 0, keep = side(); a < arguments.length; a++) r[a] = arguments[a]; use(r, keep) }",
);
test_same(
"function _() { for (var keep = side(), r = [], a = 0; a < arguments.length; a++) r[a] = arguments[a]; use(r, keep) }",
);
test_same(
"function _() { for (var keep; a < arguments.length; a++) r[a] = arguments[a]; use(r, keep) }",
);
}
#[test]
+1 -1
View File
@@ -23,5 +23,5 @@ Original | minified | minified | gzip | gzip | Iterations | Fi
6.69 MB | 2.12 MB | 2.31 MB | 453.41 kB | 488.28 kB | 4 | antd.js
10.95 MB | 3.31 MB | 3.49 MB | 849.81 kB | 915.50 kB | 4 | typescript.js
10.95 MB | 3.31 MB | 3.49 MB | 849.76 kB | 915.50 kB | 4 | typescript.js