1
1
use std:: { iter:: once, mem:: take} ;
2
2
3
+ use rustc_hash:: FxHashSet ;
3
4
use swc_common:: { pass:: Either , util:: take:: Take , Spanned , DUMMY_SP } ;
4
5
use swc_ecma_ast:: * ;
5
6
use swc_ecma_usage_analyzer:: {
6
7
alias:: { collect_infects_from, AccessKind , AliasConfig } ,
7
8
util:: is_global_var_with_pure_property_access,
8
9
} ;
9
10
use swc_ecma_utils:: {
10
- contains_arguments, contains_this_expr, prepend_stmts, ExprExt , IdentUsageFinder , StmtLike ,
11
- Type , Value ,
11
+ contains_arguments, contains_this_expr, prepend_stmts, ExprExt , StmtLike , Type , Value ,
12
12
} ;
13
13
use swc_ecma_visit:: { noop_visit_type, Visit , VisitWith } ;
14
14
#[ cfg( feature = "debug" ) ]
@@ -23,7 +23,10 @@ use crate::{
23
23
util:: { is_directive, is_ident_used_by, replace_expr} ,
24
24
} ,
25
25
option:: CompressOptions ,
26
- util:: { idents_used_by, idents_used_by_ignoring_nested, ExprOptExt , ModuleItemExt } ,
26
+ util:: {
27
+ idents_used_by, idents_used_by_ignoring_nested, ExprOptExt , IdentUsageCollector ,
28
+ ModuleItemExt ,
29
+ } ,
27
30
} ;
28
31
29
32
/// Methods related to the option `sequences`. All methods are noop if
@@ -821,22 +824,17 @@ impl Optimizer<'_> {
821
824
)
822
825
} ;
823
826
827
+ let mut merge_seq_cache = MergeSequenceCache :: new ( exprs. len ( ) ) ;
824
828
loop {
825
- let mut did_work = false ;
826
-
827
- for idx in 0 ..exprs. len ( ) {
828
- for j in idx..exprs. len ( ) {
829
- let ( a1, a2) = exprs. split_at_mut ( idx) ;
830
-
831
- if a1. is_empty ( ) || a2. is_empty ( ) {
832
- break ;
833
- }
834
-
829
+ let mut changed = false ;
830
+ for a_idx in 0 ..exprs. len ( ) . saturating_sub ( 1 ) {
831
+ for b_idx in ( a_idx + 1 ) ..exprs. len ( ) {
832
+ let ( a1, a2) = exprs. split_at_mut ( a_idx + 1 ) ;
835
833
let a = a1. last_mut ( ) . unwrap ( ) ;
834
+ let b = & mut a2[ b_idx - a_idx - 1 ] ;
836
835
837
836
if self . options . unused && self . options . sequences ( ) {
838
- if let ( Mergable :: Var ( av) , Mergable :: Var ( bv) ) = ( & mut * a, & mut a2[ j - idx] )
839
- {
837
+ if let ( Mergable :: Var ( av) , Mergable :: Var ( bv) ) = ( & mut * a, & mut * b) {
840
838
// We try dropping variable assignments first.
841
839
842
840
// Currently, we only drop variable declarations if they have the same
@@ -847,7 +845,7 @@ impl Optimizer<'_> {
847
845
848
846
match bv. init . as_deref_mut ( ) {
849
847
Some ( b_init) => {
850
- if IdentUsageFinder :: find ( & an. to_id ( ) , b_init) {
848
+ if is_ident_used_by ( an. to_id ( ) , b_init) {
851
849
log_abort ! (
852
850
"We can't duplicated binding because \
853
851
initializer uses the previous declaration of \
@@ -859,14 +857,16 @@ impl Optimizer<'_> {
859
857
if let Some ( a_init) = av. init . take ( ) {
860
858
let b_seq = b_init. force_seq ( ) ;
861
859
b_seq. exprs . insert ( 0 , a_init) ;
860
+ merge_seq_cache. invalidate ( a_idx) ;
861
+ merge_seq_cache. invalidate ( b_idx) ;
862
862
863
863
self . changed = true ;
864
864
report_change ! (
865
865
"Moving initializer sequentially as they have \
866
866
a same name"
867
867
) ;
868
868
av. name . take ( ) ;
869
- continue ;
869
+ break ;
870
870
} else {
871
871
self . changed = true ;
872
872
report_change ! (
@@ -875,7 +875,7 @@ impl Optimizer<'_> {
875
875
an. id
876
876
) ;
877
877
av. name . take ( ) ;
878
- continue ;
878
+ break ;
879
879
}
880
880
}
881
881
None => {
@@ -890,13 +890,15 @@ impl Optimizer<'_> {
890
890
//
891
891
// prints 5
892
892
bv. init = av. init . take ( ) ;
893
+ merge_seq_cache. invalidate ( a_idx) ;
894
+ merge_seq_cache. invalidate ( b_idx) ;
893
895
self . changed = true ;
894
896
report_change ! (
895
897
"Moving initializer to the next variable \
896
898
declaration as they have the same name"
897
899
) ;
898
900
av. name . take ( ) ;
899
- continue ;
901
+ break ;
900
902
}
901
903
}
902
904
}
@@ -906,26 +908,36 @@ impl Optimizer<'_> {
906
908
907
909
// Merge sequentially
908
910
909
- match & mut a2 [ j - idx ] {
911
+ match b {
910
912
Mergable :: Var ( b) => match b. init . as_deref_mut ( ) {
911
913
Some ( b) => {
912
- if self . merge_sequential_expr ( a, b) ? {
913
- did_work = true ;
914
+ if !merge_seq_cache. is_top_retain ( self , a, a_idx)
915
+ && self . merge_sequential_expr ( a, b) ?
916
+ {
917
+ changed = true ;
918
+ merge_seq_cache. invalidate ( a_idx) ;
919
+ merge_seq_cache. invalidate ( b_idx) ;
914
920
break ;
915
921
}
916
922
}
917
923
None => continue ,
918
924
} ,
919
925
Mergable :: Expr ( b) => {
920
- if self . merge_sequential_expr ( a, b) ? {
921
- did_work = true ;
926
+ if !merge_seq_cache. is_top_retain ( self , a, a_idx)
927
+ && self . merge_sequential_expr ( a, b) ?
928
+ {
929
+ changed = true ;
930
+ merge_seq_cache. invalidate ( a_idx) ;
931
+ merge_seq_cache. invalidate ( b_idx) ;
922
932
break ;
923
933
}
924
934
}
925
935
Mergable :: FnDecl ( ..) => continue ,
926
936
Mergable :: Drop => {
927
937
if self . drop_mergable_seq ( a) ? {
928
- did_work = true ;
938
+ changed = true ;
939
+ merge_seq_cache. invalidate ( a_idx) ;
940
+ merge_seq_cache. invalidate ( b_idx) ;
929
941
break ;
930
942
}
931
943
}
@@ -972,16 +984,16 @@ impl Optimizer<'_> {
972
984
_ => { }
973
985
}
974
986
975
- match & a2 [ j - idx ] {
987
+ match b {
976
988
Mergable :: Var ( e2) => {
977
989
if let Some ( e2) = & e2. init {
978
990
if !self . is_skippable_for_seq ( Some ( a) , e2) {
979
991
break ;
980
992
}
981
993
}
982
994
983
- if let Some ( id) = a1 . last_mut ( ) . unwrap ( ) . id ( ) {
984
- if IdentUsageFinder :: find ( & id, & * * e2) {
995
+ if let Some ( id) = a . id ( ) {
996
+ if merge_seq_cache . is_ident_used_by ( & id, & * * e2, b_idx ) {
985
997
break ;
986
998
}
987
999
}
@@ -991,9 +1003,8 @@ impl Optimizer<'_> {
991
1003
break ;
992
1004
}
993
1005
994
- if let Some ( id) = a1. last_mut ( ) . unwrap ( ) . id ( ) {
995
- // TODO(kdy1): Optimize
996
- if IdentUsageFinder :: find ( & id, & * * e2) {
1006
+ if let Some ( id) = a. id ( ) {
1007
+ if merge_seq_cache. is_ident_used_by ( & id, & * * e2, b_idx) {
997
1008
break ;
998
1009
}
999
1010
}
@@ -1019,7 +1030,7 @@ impl Optimizer<'_> {
1019
1030
}
1020
1031
}
1021
1032
1022
- if !did_work {
1033
+ if !changed {
1023
1034
break ;
1024
1035
}
1025
1036
}
@@ -1527,10 +1538,6 @@ impl Optimizer<'_> {
1527
1538
///
1528
1539
/// Returns [Err] iff we should stop checking.
1529
1540
fn merge_sequential_expr ( & mut self , a : & mut Mergable , b : & mut Expr ) -> Result < bool , ( ) > {
1530
- if let Mergable :: Drop = a {
1531
- return Ok ( false ) ;
1532
- }
1533
-
1534
1541
#[ cfg( feature = "debug" ) ]
1535
1542
let _tracing = {
1536
1543
let b_str = dump ( & * b, false ) ;
@@ -1552,16 +1559,6 @@ impl Optimizer<'_> {
1552
1559
)
1553
1560
} ;
1554
1561
1555
- // Respect top_retain
1556
- if let Some ( a_id) = a. id ( ) {
1557
- if a_id. 0 == "arguments"
1558
- || ( matches ! ( a, Mergable :: Var ( _) | Mergable :: FnDecl ( _) )
1559
- && !self . may_remove_ident ( & Ident :: from ( a_id) ) )
1560
- {
1561
- return Ok ( false ) ;
1562
- }
1563
- }
1564
-
1565
1562
match & * b {
1566
1563
Expr :: Arrow ( ..)
1567
1564
| Expr :: Fn ( ..)
@@ -1801,7 +1798,7 @@ impl Optimizer<'_> {
1801
1798
1802
1799
if !self . is_skippable_for_seq ( Some ( a) , & b_left. id . clone ( ) . into ( ) ) {
1803
1800
// Let's be safe
1804
- if IdentUsageFinder :: find ( & b_left. to_id ( ) , & b_assign. right ) {
1801
+ if is_ident_used_by ( b_left. to_id ( ) , & b_assign. right ) {
1805
1802
return Ok ( false ) ;
1806
1803
}
1807
1804
@@ -1817,7 +1814,7 @@ impl Optimizer<'_> {
1817
1814
return Ok ( false ) ;
1818
1815
}
1819
1816
1820
- if IdentUsageFinder :: find ( & b_left. to_id ( ) , & b_assign. right ) {
1817
+ if is_ident_used_by ( b_left. to_id ( ) , & b_assign. right ) {
1821
1818
return Err ( ( ) ) ;
1822
1819
}
1823
1820
@@ -2710,6 +2707,54 @@ impl Mergable<'_> {
2710
2707
}
2711
2708
}
2712
2709
2710
+ #[ derive( Debug , Default ) ]
2711
+ struct MergeSequenceCache {
2712
+ ident_usage_cache : Vec < Option < FxHashSet < Id > > > ,
2713
+ top_retain_cache : Vec < Option < bool > > ,
2714
+ }
2715
+
2716
+ impl MergeSequenceCache {
2717
+ fn new ( cap : usize ) -> Self {
2718
+ Self {
2719
+ ident_usage_cache : vec ! [ None ; cap] ,
2720
+ top_retain_cache : vec ! [ None ; cap] ,
2721
+ }
2722
+ }
2723
+
2724
+ fn is_ident_used_by < N : VisitWith < IdentUsageCollector > > (
2725
+ & mut self ,
2726
+ ident : & Id ,
2727
+ node : & N ,
2728
+ node_id : usize ,
2729
+ ) -> bool {
2730
+ let idents = self . ident_usage_cache [ node_id] . get_or_insert_with ( || idents_used_by ( node) ) ;
2731
+ idents. contains ( ident)
2732
+ }
2733
+
2734
+ fn invalidate ( & mut self , node_id : usize ) {
2735
+ self . ident_usage_cache [ node_id] = None ;
2736
+ }
2737
+
2738
+ fn is_top_retain ( & mut self , optimizer : & Optimizer , a : & Mergable , node_id : usize ) -> bool {
2739
+ * self . top_retain_cache [ node_id] . get_or_insert_with ( || {
2740
+ if let Mergable :: Drop = a {
2741
+ return true ;
2742
+ }
2743
+
2744
+ if let Some ( a_id) = a. id ( ) {
2745
+ if a_id. 0 == "arguments"
2746
+ || ( matches ! ( a, Mergable :: Var ( _) | Mergable :: FnDecl ( _) )
2747
+ && !optimizer. may_remove_ident ( & Ident :: from ( a_id) ) )
2748
+ {
2749
+ return true ;
2750
+ }
2751
+ }
2752
+
2753
+ false
2754
+ } )
2755
+ }
2756
+ }
2757
+
2713
2758
/// Returns true for trivial bool/numeric literals
2714
2759
pub ( crate ) fn is_trivial_lit ( e : & Expr ) -> bool {
2715
2760
match e {
0 commit comments