Skip to content

Commit c364ad1

Browse files
committed
feat(minifier): support ForStatements for single use variable inlining (#13755)
Inline single use variables in for statements: ```js function wrapper1() { var x = foo; for (var i = x; i < 10; i++) console.log(i) } function wrapper2() { var i, x = foo; for (i = x; i < 10; i++) console.log(i) } // to function wrapper1() { for (var i = foo; i < 10; i++) console.log(i) } function wrapper2() { var i; for (i = foo; i < 10; i++) console.log(i) } ``` ```js function wrapper() { var x = {}; for (var a in x) console.log(a) } // to function wrapper() { for (var a in {}) console.log(a) } ``` ```js function wrapper() { var x = []; for (var a of x) console.log(a) } // to function wrapper() { for (var a of []) console.log(a) } ``` refs #13277
1 parent c868796 commit c364ad1

File tree

4 files changed

+75
-6
lines changed

4 files changed

+75
-6
lines changed

crates/oxc_minifier/src/peephole/minimize_statements.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,33 @@ impl<'a> PeepholeOptimizations {
824824

825825
ctx: &mut Ctx<'a, '_>,
826826
) {
827+
if let Some(init) = &mut for_stmt.init {
828+
match init {
829+
ForStatementInit::VariableDeclaration(var_decl) => {
830+
if let Some(first_decl) = var_decl.declarations.first_mut()
831+
&& let Some(first_decl_init) = first_decl.init.as_mut()
832+
{
833+
let changed = Self::substitute_single_use_symbol_in_statement(
834+
first_decl_init,
835+
result,
836+
ctx,
837+
);
838+
if changed {
839+
ctx.state.changed = true;
840+
}
841+
}
842+
}
843+
match_expression!(ForStatementInit) => {
844+
let init = init.to_expression_mut();
845+
let changed =
846+
Self::substitute_single_use_symbol_in_statement(init, result, ctx);
847+
if changed {
848+
ctx.state.changed = true;
849+
}
850+
}
851+
}
852+
}
853+
827854
if let Some(ForStatementInit::VariableDeclaration(var_decl)) = &mut for_stmt.init {
828855
let old_len = var_decl.declarations.len();
829856
var_decl.declarations.retain(|decl| {
@@ -890,6 +917,21 @@ impl<'a> PeepholeOptimizations {
890917

891918
ctx: &mut Ctx<'a, '_>,
892919
) {
920+
// Annex B.3.5 allows initializers in non-strict mode
921+
// <https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-initializers-in-forin-statement-heads>
922+
// That is evaluated before the right hand side is evaluated. So, in that case, skip the single use substitution.
923+
if !matches!(&for_in_stmt.left, ForStatementLeft::VariableDeclaration(var_decl) if var_decl.has_init())
924+
{
925+
let changed = Self::substitute_single_use_symbol_in_statement(
926+
&mut for_in_stmt.right,
927+
result,
928+
ctx,
929+
);
930+
if changed {
931+
ctx.state.changed = true;
932+
}
933+
}
934+
893935
if ctx.options().sequences {
894936
match result.last_mut() {
895937
// "a; for (var b in c) d" => "for (var b in a, c) d"
@@ -963,6 +1005,12 @@ impl<'a> PeepholeOptimizations {
9631005
result: &mut Vec<'a, Statement<'a>>,
9641006
ctx: &mut Ctx<'a, '_>,
9651007
) {
1008+
let changed =
1009+
Self::substitute_single_use_symbol_in_statement(&mut for_of_stmt.right, result, ctx);
1010+
if changed {
1011+
ctx.state.changed = true;
1012+
}
1013+
9661014
// "var a; for (a of b) c" => "for (var a of b) c"
9671015
if let Some(Statement::VariableDeclaration(prev_var_decl)) = result.last_mut() {
9681016
if let ForStatementLeft::AssignmentTargetIdentifier(id) = &for_of_stmt.left {

crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,27 @@ fn test_inline_single_use_variable() {
154154
"function wrapper() { let x = function () { console.log() }; foo(x) }",
155155
"function wrapper() { foo(function() { console.log() }) }",
156156
);
157+
158+
test(
159+
"function wrapper() { var x = foo; for (var i = x; i < 10; i++) console.log(i) }",
160+
"function wrapper() { for (var i = foo; i < 10; i++) console.log(i) }",
161+
);
162+
test(
163+
"function wrapper() { var i, x = foo; for (i = x; i < 10; i++) console.log(i) }",
164+
"function wrapper() { var i; for (i = foo; i < 10; i++) console.log(i) }",
165+
);
166+
test(
167+
"function wrapper() { var x = {}; for (var a in x) console.log(a) }",
168+
"function wrapper() { for (var a in {}) console.log(a) }",
169+
);
170+
test(
171+
"function wrapper() { var x = {}; for (var a = 0 in x) console.log(a) }",
172+
"function wrapper() { var x = {}; for (var a = 0 in x) console.log(a) }",
173+
);
174+
test(
175+
"function wrapper() { var x = []; for (var a of x) console.log(a) }",
176+
"function wrapper() { for (var a of []) console.log(a) }",
177+
);
157178
}
158179

159180
#[test]

tasks/minsize/minsize.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ Original | minified | minified | gzip | gzip | Iterations | Fi
77

88
287.63 kB | 89.28 kB | 90.07 kB | 30.94 kB | 31.95 kB | 2 | jquery.js
99

10-
342.15 kB | 117.01 kB | 118.14 kB | 43.19 kB | 44.37 kB | 2 | vue.js
10+
342.15 kB | 117 kB | 118.14 kB | 43.19 kB | 44.37 kB | 2 | vue.js
1111

1212
544.10 kB | 71.18 kB | 72.48 kB | 25.85 kB | 26.20 kB | 2 | lodash.js
1313

1414
555.77 kB | 270.78 kB | 270.13 kB | 88.19 kB | 90.80 kB | 2 | d3.js
1515

16-
1.01 MB | 439.58 kB | 458.89 kB | 122.15 kB | 126.71 kB | 2 | bundle.min.js
16+
1.01 MB | 439.56 kB | 458.89 kB | 122.14 kB | 126.71 kB | 2 | bundle.min.js
1717

1818
1.25 MB | 645.63 kB | 646.76 kB | 159.54 kB | 163.73 kB | 2 | three.js
1919

20-
2.14 MB | 713.54 kB | 724.14 kB | 161.00 kB | 181.07 kB | 2 | victory.js
20+
2.14 MB | 713.53 kB | 724.14 kB | 160.99 kB | 181.07 kB | 2 | victory.js
2121

22-
3.20 MB | 1.00 MB | 1.01 MB | 323.12 kB | 331.56 kB | 3 | echarts.js
22+
3.20 MB | 1.00 MB | 1.01 MB | 323.10 kB | 331.56 kB | 3 | echarts.js
2323

2424
6.69 MB | 2.22 MB | 2.31 MB | 459.28 kB | 488.28 kB | 4 | antd.js
2525

tasks/track_memory_allocations/allocs_minifier.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
File | File size || Sys allocs | Sys reallocs || Arena allocs | Arena reallocs | Arena bytes
22
-------------------------------------------------------------------------------------------------------------------------------------------
3-
checker.ts | 2.92 MB || 84073 | 14190 || 153691 | 29463 | 5.625 MB
3+
checker.ts | 2.92 MB || 84058 | 14190 || 153533 | 29395 | 5.615 MB
44

55
cal.com.tsx | 1.06 MB || 40525 | 3033 || 37074 | 4733 | 1.654 MB
66

77
RadixUIAdoptionSection.jsx | 2.52 kB || 82 | 8 || 30 | 6 | 992 B
88

9-
pdf.mjs | 567.30 kB || 19577 | 2900 || 47405 | 7784 | 1.625 MB
9+
pdf.mjs | 567.30 kB || 19572 | 2900 || 47384 | 7770 | 1.623 MB
1010

1111
antd.js | 6.69 MB || 99854 | 13518 || 331725 | 70117 | 17.407 MB
1212

0 commit comments

Comments
 (0)