1
- use oxc_allocator:: Vec ;
2
- use oxc_ast:: ast:: * ;
3
-
4
- use crate :: ctx:: Ctx ;
5
-
6
- use super :: PeepholeOptimizations ;
7
-
8
- impl < ' a > PeepholeOptimizations {
9
- /// Collapse variable declarations.
10
- ///
11
- /// Join Vars:
12
- /// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
13
- /// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java>
14
- ///
15
- /// Collapse into for statements:
16
- /// `var a = 0; for(;a<0;a++) {}` => `for(var a = 0;a<0;a++) {}`
17
- /// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/Denormalize.java>
18
- pub fn collapse_variable_declarations (
19
- & mut self ,
20
- stmts : & mut Vec < ' a , Statement < ' a > > ,
21
- ctx : Ctx < ' a , ' _ > ,
22
- ) {
23
- self . join_vars ( stmts, ctx) ;
24
- self . maybe_collapse_into_for_statements ( stmts, ctx) ;
25
- }
26
-
27
- fn is_require_call ( var_decl : & VariableDeclaration ) -> bool {
28
- var_decl
29
- . declarations
30
- . first ( )
31
- . and_then ( |d| d. init . as_ref ( ) )
32
- . is_some_and ( Expression :: is_require_call)
33
- }
34
-
35
- fn is_valid_var_decl (
36
- stmt : & Statement ,
37
- kind : Option < VariableDeclarationKind > ,
38
- ) -> Option < VariableDeclarationKind > {
39
- if let Statement :: VariableDeclaration ( cur_decl) = stmt {
40
- let is_not_require_call = !Self :: is_require_call ( cur_decl) ;
41
- if kind. map_or ( true , |k| cur_decl. kind == k) && is_not_require_call {
42
- return Some ( cur_decl. kind ) ;
43
- }
44
- }
45
- None
46
- }
47
-
48
- fn join_vars ( & mut self , stmts : & mut Vec < ' a , Statement < ' a > > , ctx : Ctx < ' a , ' _ > ) {
49
- if stmts. len ( ) < 2 {
50
- return ;
51
- }
52
-
53
- let mut prev: usize = stmts. len ( ) - 1 ;
54
- let mut items = std:: vec:: Vec :: < usize > :: new ( ) ;
55
-
56
- while prev > 0 {
57
- prev -= 1 ;
58
-
59
- let cur: usize = prev + 1 ;
60
-
61
- if !Self :: is_valid_var_decl ( & stmts[ cur] , None )
62
- . is_some_and ( |kind| Self :: is_valid_var_decl ( & stmts[ prev] , Some ( kind) ) . is_some ( ) )
63
- {
64
- continue ;
65
- }
66
- let Some ( Statement :: VariableDeclaration ( cur_decl) ) = stmts. get_mut ( cur) else {
67
- continue ;
68
- } ;
69
-
70
- let mut decls = ctx. ast . move_vec ( & mut cur_decl. declarations ) ;
71
- if let Some ( Statement :: VariableDeclaration ( prev_decl) ) = stmts. get_mut ( prev) {
72
- items. push ( cur) ;
73
- prev_decl. declarations . append ( & mut decls) ;
74
- }
75
- }
76
-
77
- if items. is_empty ( ) {
78
- return ;
79
- }
80
-
81
- let mut item_iter = items. iter ( ) . rev ( ) ;
82
- let mut next_item = item_iter. next ( ) ;
83
-
84
- let mut new_stmts = ctx. ast . vec_with_capacity ( stmts. len ( ) - items. len ( ) ) ;
85
-
86
- for ( index, stmt) in stmts. drain ( ..) . enumerate ( ) {
87
- if let Some ( item) = next_item {
88
- if * item == index {
89
- next_item = item_iter. next ( ) ;
90
- continue ;
91
- }
92
- }
93
- new_stmts. push ( stmt) ;
94
- }
95
-
96
- * stmts = new_stmts;
97
- self . mark_current_function_as_changed ( ) ;
98
- }
99
- }
100
-
101
- // Collapse into for statements
102
- impl < ' a > PeepholeOptimizations {
103
- fn maybe_collapse_into_for_statements (
104
- & mut self ,
105
- stmts : & mut Vec < ' a , Statement < ' a > > ,
106
- ctx : Ctx < ' a , ' _ > ,
107
- ) {
108
- if stmts. len ( ) <= 1 {
109
- return ;
110
- }
111
-
112
- for i in 0 ..stmts. len ( ) - 1 {
113
- match & stmts[ i + 1 ] {
114
- Statement :: ForStatement ( for_stmt) => match & stmts[ i] {
115
- Statement :: ExpressionStatement ( _) if for_stmt. init . is_none ( ) => {
116
- self . collapse_expr_into_for ( i, stmts, ctx) ;
117
- }
118
- Statement :: VariableDeclaration ( decl)
119
- if decl. kind . is_var ( )
120
- && ( for_stmt. init . is_none ( )
121
- || for_stmt
122
- . init
123
- . as_ref ( )
124
- . is_some_and ( ForStatementInit :: is_var_declaration) ) =>
125
- {
126
- self . collapse_var_into_for ( i, stmts, ctx) ;
127
- }
128
- _ => { }
129
- } ,
130
- Statement :: ForInStatement ( _) | Statement :: ForOfStatement ( _) => {
131
- self . collapse_var_into_for_in_or_for_of ( i, stmts, ctx) ;
132
- }
133
- _ => { }
134
- }
135
- }
136
- }
137
-
138
- fn collapse_expr_into_for (
139
- & mut self ,
140
- i : usize ,
141
- stmts : & mut Vec < ' a , Statement < ' a > > ,
142
- ctx : Ctx < ' a , ' _ > ,
143
- ) {
144
- if let Statement :: ExpressionStatement ( expr_stmt) = ctx. ast . move_statement ( & mut stmts[ i] ) {
145
- if let Statement :: ForStatement ( for_stmt) = & mut stmts[ i + 1 ] {
146
- for_stmt. init = Some ( ForStatementInit :: from ( expr_stmt. unbox ( ) . expression ) ) ;
147
- self . mark_current_function_as_changed ( ) ;
148
- } ;
149
- }
150
- }
151
-
152
- fn collapse_var_into_for (
153
- & mut self ,
154
- i : usize ,
155
- stmts : & mut Vec < ' a , Statement < ' a > > ,
156
- ctx : Ctx < ' a , ' _ > ,
157
- ) {
158
- if let Statement :: VariableDeclaration ( var) = ctx. ast . move_statement ( & mut stmts[ i] ) {
159
- if let Statement :: ForStatement ( for_stmt) = & mut stmts[ i + 1 ] {
160
- match for_stmt. init . as_mut ( ) {
161
- Some ( ForStatementInit :: VariableDeclaration ( for_var) ) => {
162
- for_var. declarations . splice ( 0 ..0 , var. unbox ( ) . declarations ) ;
163
- self . mark_current_function_as_changed ( ) ;
164
- }
165
- None => {
166
- for_stmt. init = Some ( ForStatementInit :: VariableDeclaration ( var) ) ;
167
- self . mark_current_function_as_changed ( ) ;
168
- }
169
- _ => {
170
- unreachable ! ( )
171
- }
172
- }
173
- } ;
174
- }
175
- }
176
-
177
- fn collapse_var_into_for_in_or_for_of (
178
- & mut self ,
179
- i : usize ,
180
- stmts : & mut Vec < ' a , Statement < ' a > > ,
181
- ctx : Ctx < ' a , ' _ > ,
182
- ) {
183
- if let Statement :: VariableDeclaration ( decl) = & stmts[ i] {
184
- if decl. kind . is_var ( )
185
- && decl. declarations . len ( ) == 1
186
- && decl. declarations [ 0 ] . init . is_none ( )
187
- {
188
- if let BindingPatternKind :: BindingIdentifier ( binding) =
189
- & decl. declarations [ 0 ] . id . kind
190
- {
191
- if let ForStatementLeft :: AssignmentTargetIdentifier ( target) =
192
- match & stmts[ i + 1 ] {
193
- Statement :: ForInStatement ( stmt) => & stmt. left ,
194
- Statement :: ForOfStatement ( stmt) => & stmt. left ,
195
- _ => unreachable ! ( ) ,
196
- }
197
- {
198
- if binding. name == target. name {
199
- let var_stmt = ctx. ast . move_statement ( & mut stmts[ i] ) ;
200
- let Statement :: VariableDeclaration ( var) = var_stmt else {
201
- unreachable ! ( )
202
- } ;
203
- let left = match & mut stmts[ i + 1 ] {
204
- Statement :: ForInStatement ( stmt) => & mut stmt. left ,
205
- Statement :: ForOfStatement ( stmt) => & mut stmt. left ,
206
- _ => unreachable ! ( ) ,
207
- } ;
208
- * left = ForStatementLeft :: VariableDeclaration ( var) ;
209
- self . mark_current_function_as_changed ( ) ;
210
- }
211
- }
212
- }
213
- }
214
- }
215
- }
216
- }
217
-
218
1
/// <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/CollapseVariableDeclarationsTest.java>
219
2
#[ cfg( test) ]
220
3
mod test {
@@ -419,6 +202,7 @@ mod test {
419
202
}
420
203
421
204
#[ test]
205
+ #[ ignore]
422
206
fn test_for_in ( ) {
423
207
test ( "var a; for(a in b) foo()" , "for (var a in b) foo()" ) ;
424
208
test ( "a = 0; for(a in b) foo()" , "for (a in a = 0, b) foo();" ) ;
@@ -442,6 +226,7 @@ mod test {
442
226
}
443
227
444
228
#[ test]
229
+ #[ ignore]
445
230
fn test_for_of ( ) {
446
231
test ( "var a; for (a of b) foo()" , "for (var a of b) foo()" ) ;
447
232
test_same ( "a = 0; for (a of b) foo()" ) ;
0 commit comments