1
- use std:: { collections:: HashSet , hash:: BuildHasherDefault } ;
2
-
3
- use indexmap:: IndexSet ;
4
- use rustc_hash:: { FxHashMap , FxHashSet , FxHasher } ;
5
- use swc_atoms:: { js_word, JsWord } ;
6
- use swc_common:: {
7
- collections:: { AHashMap , AHashSet } ,
8
- SyntaxContext ,
9
- } ;
1
+ use swc_atoms:: js_word;
2
+ use swc_common:: { collections:: AHashMap , SyntaxContext } ;
10
3
use swc_ecma_ast:: * ;
11
4
use swc_ecma_utils:: { find_pat_ids, IsEmpty , StmtExt } ;
12
5
use swc_ecma_visit:: { noop_visit_type, Visit , VisitWith } ;
13
6
use swc_timer:: timer;
14
7
15
- use self :: {
16
- ctx:: Ctx ,
17
- storage:: { Storage , * } ,
18
- } ;
8
+ pub use self :: ctx:: Ctx ;
9
+ use self :: storage:: { Storage , * } ;
19
10
use crate :: {
20
- alias:: { collect_infects_from, Access , AccessKind , AliasConfig } ,
11
+ alias:: { collect_infects_from, AliasConfig } ,
21
12
marks:: Marks ,
22
13
util:: can_end_conditionally,
23
14
} ;
24
15
25
16
mod ctx;
26
- pub ( crate ) mod storage;
27
-
28
- #[ derive( Debug , Default ) ]
29
- pub ( crate ) struct ModuleInfo {
30
- /// Imported identifiers which should be treated as a black box.
31
- ///
32
- /// Imports from `@swc/helpers` are excluded as helpers are not modified by
33
- /// accessing/calling other modules.
34
- pub blackbox_imports : AHashSet < Id > ,
35
- }
36
-
37
- pub ( crate ) fn analyze < N > ( n : & N , _module_info : & ModuleInfo , marks : Option < Marks > ) -> ProgramData
38
- where
39
- N : VisitWith < UsageAnalyzer > ,
40
- {
41
- analyze_with_storage :: < ProgramData , _ > ( n, marks)
42
- }
17
+ pub mod storage;
43
18
44
19
/// TODO: Track assignments to variables via `arguments`.
45
20
/// TODO: Scope-local. (Including block)
46
21
///
47
22
/// If `marks` is [None], markers are ignored.
48
- pub ( crate ) fn analyze_with_storage < S , N > ( n : & N , marks : Option < Marks > ) -> S
23
+ pub fn analyze_with_storage < S , N > ( n : & N , marks : Option < Marks > ) -> S
49
24
where
50
25
S : Storage ,
51
26
N : VisitWith < UsageAnalyzer < S > > ,
@@ -68,292 +43,21 @@ where
68
43
v. data
69
44
}
70
45
71
- #[ derive( Debug , Clone ) ]
72
- pub ( crate ) struct VarUsageInfo {
73
- pub inline_prevented : bool ,
74
-
75
- /// The number of direct reference to this identifier.
76
- pub ref_count : u32 ,
77
-
78
- /// `true` if a variable is conditionally initialized.
79
- pub cond_init : bool ,
80
-
81
- /// `false` if it's only used.
82
- pub declared : bool ,
83
- pub declared_count : u32 ,
84
-
85
- /// `true` if the enclosing function defines this variable as a parameter.
86
- pub declared_as_fn_param : bool ,
87
-
88
- pub declared_as_fn_decl : bool ,
89
- pub declared_as_fn_expr : bool ,
90
-
91
- pub assign_count : u32 ,
92
- pub mutation_by_call_count : u32 ,
93
-
94
- /// The number of direct and indirect reference to this identifier.
95
- /// ## Things to note
96
- ///
97
- /// - Update is counted as usage, but assign is not
98
- pub usage_count : u32 ,
99
-
100
- /// The variable itself is modified.
101
- reassigned_with_assignment : bool ,
102
- reassigned_with_var_decl : bool ,
103
- /// The variable itself or a property of it is modified.
104
- pub mutated : bool ,
105
-
106
- pub has_property_access : bool ,
107
- pub has_property_mutation : bool ,
108
-
109
- pub exported : bool ,
110
- /// True if used **above** the declaration or in init. (Not eval order).
111
- pub used_above_decl : bool ,
112
- /// `true` if it's declared by function parameters or variables declared in
113
- /// a closest function and used only within it and not used by child
114
- /// functions.
115
- pub is_fn_local : bool ,
116
-
117
- pub executed_multiple_time : bool ,
118
- pub used_in_cond : bool ,
119
-
120
- pub var_kind : Option < VarDeclKind > ,
121
- pub var_initialized : bool ,
122
-
123
- pub declared_as_catch_param : bool ,
124
-
125
- pub no_side_effect_for_member_access : bool ,
126
-
127
- pub callee_count : u32 ,
128
-
129
- pub used_as_arg : bool ,
130
-
131
- pub indexed_with_dynamic_key : bool ,
132
-
133
- pub pure_fn : bool ,
134
-
135
- /// `infects_to`. This should be renamed, but it will be done with another
136
- /// PR. (because it's hard to review)
137
- infects : Vec < Access > ,
138
-
139
- pub used_in_non_child_fn : bool ,
140
- /// Only **string** properties.
141
- pub accessed_props : Box < AHashMap < JsWord , u32 > > ,
142
-
143
- pub used_recursively : bool ,
144
- }
145
-
146
- impl Default for VarUsageInfo {
147
- fn default ( ) -> Self {
148
- Self {
149
- inline_prevented : Default :: default ( ) ,
150
- ref_count : Default :: default ( ) ,
151
- cond_init : Default :: default ( ) ,
152
- declared : Default :: default ( ) ,
153
- declared_count : Default :: default ( ) ,
154
- declared_as_fn_param : Default :: default ( ) ,
155
- declared_as_fn_decl : Default :: default ( ) ,
156
- declared_as_fn_expr : Default :: default ( ) ,
157
- assign_count : Default :: default ( ) ,
158
- mutation_by_call_count : Default :: default ( ) ,
159
- usage_count : Default :: default ( ) ,
160
- reassigned_with_assignment : Default :: default ( ) ,
161
- reassigned_with_var_decl : Default :: default ( ) ,
162
- mutated : Default :: default ( ) ,
163
- has_property_access : Default :: default ( ) ,
164
- has_property_mutation : Default :: default ( ) ,
165
- exported : Default :: default ( ) ,
166
- used_above_decl : Default :: default ( ) ,
167
- is_fn_local : true ,
168
- executed_multiple_time : Default :: default ( ) ,
169
- used_in_cond : Default :: default ( ) ,
170
- var_kind : Default :: default ( ) ,
171
- var_initialized : Default :: default ( ) ,
172
- declared_as_catch_param : Default :: default ( ) ,
173
- no_side_effect_for_member_access : Default :: default ( ) ,
174
- callee_count : Default :: default ( ) ,
175
- used_as_arg : Default :: default ( ) ,
176
- indexed_with_dynamic_key : Default :: default ( ) ,
177
- pure_fn : Default :: default ( ) ,
178
- infects : Default :: default ( ) ,
179
- used_in_non_child_fn : Default :: default ( ) ,
180
- accessed_props : Default :: default ( ) ,
181
- used_recursively : Default :: default ( ) ,
182
- }
183
- }
184
- }
185
-
186
- impl VarUsageInfo {
187
- pub fn is_mutated_only_by_one_call ( & self ) -> bool {
188
- self . assign_count == 0 && self . mutation_by_call_count == 1
189
- }
190
-
191
- pub fn is_infected ( & self ) -> bool {
192
- !self . infects . is_empty ( )
193
- }
194
-
195
- pub fn reassigned ( & self ) -> bool {
196
- self . reassigned_with_assignment
197
- || self . reassigned_with_var_decl
198
- || ( u32:: from ( self . var_initialized )
199
- + u32:: from ( self . declared_as_catch_param )
200
- + u32:: from ( self . declared_as_fn_param )
201
- + self . assign_count )
202
- > 1
203
- }
204
-
205
- pub fn can_inline_var ( & self ) -> bool {
206
- !self . mutated
207
- || ( self . assign_count == 0 && !self . reassigned ( ) && !self . has_property_mutation )
208
- }
209
-
210
- pub fn can_inline_fn_once ( & self ) -> bool {
211
- self . callee_count > 0
212
- || !self . executed_multiple_time && ( self . is_fn_local || !self . used_in_non_child_fn )
213
- }
214
- }
215
-
216
46
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
217
- pub ( crate ) enum ScopeKind {
47
+ pub enum ScopeKind {
218
48
Fn ,
219
49
Block ,
220
50
}
221
51
222
- #[ derive( Debug , Default , Clone ) ]
223
- pub ( crate ) struct ScopeData {
224
- pub has_with_stmt : bool ,
225
- pub has_eval_call : bool ,
226
- pub used_arguments : bool ,
227
- }
228
-
229
52
#[ derive( Debug , Clone ) ]
230
53
enum RecursiveUsage {
231
54
FnOrClass ,
232
55
Var { can_ignore : bool } ,
233
56
}
234
57
235
- /// Analyzed info of a whole program we are working on.
236
- #[ derive( Debug , Default ) ]
237
- pub ( crate ) struct ProgramData {
238
- pub vars : FxHashMap < Id , VarUsageInfo > ,
239
-
240
- pub top : ScopeData ,
241
-
242
- pub scopes : FxHashMap < SyntaxContext , ScopeData > ,
243
-
244
- initialized_vars : IndexSet < Id , ahash:: RandomState > ,
245
- }
246
-
247
- impl ProgramData {
248
- pub ( crate ) fn expand_infected (
249
- & self ,
250
- module_info : & ModuleInfo ,
251
- ids : FxHashSet < Access > ,
252
- max_num : usize ,
253
- ) -> Result < FxHashSet < Access > , ( ) > {
254
- let init =
255
- HashSet :: with_capacity_and_hasher ( max_num, BuildHasherDefault :: < FxHasher > :: default ( ) ) ;
256
- ids. into_iter ( ) . try_fold ( init, |mut res, id| {
257
- let mut ids = Vec :: with_capacity ( max_num) ;
258
- ids. push ( id) ;
259
- let mut ranges = vec ! [ 0 ..1usize ] ;
260
- loop {
261
- let range = ranges. remove ( 0 ) ;
262
- for index in range {
263
- let iid = ids. get ( index) . unwrap ( ) ;
264
-
265
- // Abort on imported variables, because we can't analyze them
266
- if module_info. blackbox_imports . contains ( & iid. 0 ) {
267
- return Err ( ( ) ) ;
268
- }
269
- if !res. insert ( iid. clone ( ) ) {
270
- continue ;
271
- }
272
- if res. len ( ) >= max_num {
273
- return Err ( ( ) ) ;
274
- }
275
- if let Some ( info) = self . vars . get ( & iid. 0 ) {
276
- let infects = & info. infects ;
277
- if !infects. is_empty ( ) {
278
- let old_len = ids. len ( ) ;
279
-
280
- // This is not a call, so effects from call can be skipped
281
- let can_skip_non_call = matches ! ( iid. 1 , AccessKind :: Reference )
282
- || ( info. declared_count == 1
283
- && info. declared_as_fn_decl
284
- && !info. reassigned ( ) ) ;
285
-
286
- if can_skip_non_call {
287
- ids. extend (
288
- infects
289
- . iter ( )
290
- . filter ( |( _, kind) | * kind != AccessKind :: Call )
291
- . cloned ( ) ,
292
- ) ;
293
- } else {
294
- ids. extend_from_slice ( infects. as_slice ( ) ) ;
295
- }
296
- let new_len = ids. len ( ) ;
297
- ranges. push ( old_len..new_len) ;
298
- }
299
- }
300
- }
301
- if ranges. is_empty ( ) {
302
- break ;
303
- }
304
- }
305
- Ok ( res)
306
- } )
307
- }
308
-
309
- pub ( crate ) fn contains_unresolved ( & self , e : & Expr ) -> bool {
310
- match e {
311
- Expr :: Ident ( i) => {
312
- if let Some ( v) = self . vars . get ( & i. to_id ( ) ) {
313
- return !v. declared ;
314
- }
315
-
316
- true
317
- }
318
-
319
- Expr :: Member ( MemberExpr { obj, prop, .. } ) => {
320
- if self . contains_unresolved ( obj) {
321
- return true ;
322
- }
323
-
324
- if let MemberProp :: Computed ( prop) = prop {
325
- if self . contains_unresolved ( & prop. expr ) {
326
- return true ;
327
- }
328
- }
329
-
330
- false
331
- }
332
-
333
- Expr :: Call ( CallExpr {
334
- callee : Callee :: Expr ( callee) ,
335
- args,
336
- ..
337
- } ) => {
338
- if self . contains_unresolved ( callee) {
339
- return true ;
340
- }
341
-
342
- if args. iter ( ) . any ( |arg| self . contains_unresolved ( & arg. expr ) ) {
343
- return true ;
344
- }
345
-
346
- false
347
- }
348
-
349
- _ => false ,
350
- }
351
- }
352
- }
353
-
354
58
/// This assumes there are no two variable with same name and same span hygiene.
355
59
#[ derive( Debug ) ]
356
- pub ( crate ) struct UsageAnalyzer < S = ProgramData >
60
+ pub struct UsageAnalyzer < S >
357
61
where
358
62
S : Storage ,
359
63
{
@@ -1504,10 +1208,8 @@ where
1504
1208
} = e
1505
1209
{
1506
1210
// TODO: merge with may_have_side_effects
1507
- let can_ignore = match & * * init {
1508
- Expr :: Call ( call) if call. span . has_mark ( marks. pure ) => true ,
1509
- _ => false ,
1510
- } ;
1211
+ let can_ignore =
1212
+ matches ! ( & * * init, Expr :: Call ( call) if call. span. has_mark( marks. pure) ) ;
1511
1213
let id = id. to_id ( ) ;
1512
1214
self . used_recursively
1513
1215
. insert ( id. clone ( ) , RecursiveUsage :: Var { can_ignore } ) ;
0 commit comments