28
28
import com .google .javascript .jscomp .CodingConvention .SubclassRelationship ;
29
29
import com .google .javascript .jscomp .PolyfillUsageFinder .PolyfillUsage ;
30
30
import com .google .javascript .jscomp .PolyfillUsageFinder .Polyfills ;
31
+ import com .google .javascript .jscomp .base .format .SimpleFormat ;
31
32
import com .google .javascript .jscomp .diagnostic .LogFile ;
32
33
import com .google .javascript .jscomp .resources .ResourceLoader ;
33
34
import com .google .javascript .rhino .IR ;
@@ -142,6 +143,7 @@ class RemoveUnusedCode implements CompilerPass {
142
143
143
144
// Allocated & cleaned up by process()
144
145
private @ Nullable LogFile removalLog ;
146
+ private @ Nullable LogFile unremovableLog ;
145
147
146
148
RemoveUnusedCode (Builder builder ) {
147
149
this .compiler = builder .compiler ;
@@ -342,11 +344,15 @@ public void process(Node externs, Node root) {
342
344
pinnedPropertyNames .addAll (compiler .getExternProperties ());
343
345
344
346
try (LogFile removalLogFile =
345
- compiler .createOrReopenIndexedLog (this .getClass (), "removals.log" )) {
347
+ compiler .createOrReopenIndexedLog (this .getClass (), "removals.log" );
348
+ LogFile keepLogFile =
349
+ compiler .createOrReopenIndexedLog (this .getClass (), "unremovable.log" )) {
346
350
removalLog = removalLogFile ; // avoid passing the log file through a bunch of methods
351
+ unremovableLog = keepLogFile ;
347
352
traverseAndRemoveUnusedReferences (root );
348
353
} finally {
349
354
removalLog = null ;
355
+ unremovableLog = null ;
350
356
}
351
357
}
352
358
@@ -420,7 +426,7 @@ private void traverseNode(Node n, Scope scope) {
420
426
.buildFunctionDeclaration (n );
421
427
varInfo .addRemovable (functionDeclaration );
422
428
if (parent .isExport ()) {
423
- varInfo .setIsExplicitlyNotRemovable ();
429
+ varInfo .setIsExplicitlyNotRemovable (() -> "exported class" );
424
430
}
425
431
} else {
426
432
traverseFunction (n , scope );
@@ -526,7 +532,9 @@ private void traverseNode(Node n, Scope scope) {
526
532
// class name() {}
527
533
// handled at a higher level
528
534
checkState (!((parent .isFunction () || parent .isClass ()) && parent .getFirstChild () == n ));
529
- traverseNameNode (n , scope ).setIsExplicitlyNotRemovable ();
535
+ traverseNameNode (n , scope )
536
+ .setIsExplicitlyNotRemovable (
537
+ () -> SimpleFormat .format ("reference found: %s" , n .getLocation ()));
530
538
}
531
539
break ;
532
540
@@ -925,7 +933,7 @@ private void traverseCatch(Node catchNode, Scope scope) {
925
933
if (exceptionNameNode .isName ()) {
926
934
// exceptionNameNode can be an empty node if not using a binding in 2019.
927
935
VarInfo exceptionVarInfo = traverseNameNode (exceptionNameNode , scope );
928
- exceptionVarInfo .setIsExplicitlyNotRemovable ();
936
+ exceptionVarInfo .setIsExplicitlyNotRemovable (() -> "catch variable" );
929
937
}
930
938
traverseNode (block , scope );
931
939
}
@@ -940,7 +948,7 @@ private void traverseEnhancedFor(Node enhancedFor, Scope scope) {
940
948
// using previously-declared loop variable. e.g.
941
949
// `for (varName of collection) {}`
942
950
VarInfo varInfo = traverseNameNode (iterationTarget , forScope );
943
- varInfo .setIsExplicitlyNotRemovable ();
951
+ varInfo .setIsExplicitlyNotRemovable (() -> "for-of or for-in loop variable" );
944
952
} else if (NodeUtil .isNameDeclaration (iterationTarget )) {
945
953
// loop has const/var/let declaration
946
954
Node declNode = iterationTarget .getOnlyChild ();
@@ -962,7 +970,7 @@ private void traverseEnhancedFor(Node enhancedFor, Scope scope) {
962
970
// We can never remove the loop variable of a for-in or for-of loop, because it's
963
971
// essential to loop syntax.
964
972
VarInfo varInfo = traverseNameNode (declNode , forScope );
965
- varInfo .setIsExplicitlyNotRemovable ();
973
+ varInfo .setIsExplicitlyNotRemovable (() -> "for-of or for-in loop variable" );
966
974
}
967
975
} else {
968
976
// using some general LHS value e.g.
@@ -1005,7 +1013,8 @@ private void traverseVanillaForNameDeclarations(Node nameDeclaration, Scope scop
1005
1013
} else if (mayHaveSideEffects (valueNode )) {
1006
1014
// TODO(bradfordcsmith): Actually allow for removing the variable while keeping the
1007
1015
// valueNode for its side-effects.
1008
- varInfo .setIsExplicitlyNotRemovable ();
1016
+ varInfo .setIsExplicitlyNotRemovable (
1017
+ () -> "for-loop variable initialization has side-effects" );
1009
1018
traverseNode (valueNode , scope );
1010
1019
} else {
1011
1020
VanillaForNameDeclaration vanillaForNameDeclaration =
@@ -1277,18 +1286,18 @@ private void traverseClassDeclaration(Node classNode, Scope scope) {
1277
1286
VarInfo varInfo = traverseNameNode (classNameNode , scope );
1278
1287
if (classNode .getParent ().isExport ()) {
1279
1288
// Cannot remove an exported class.
1280
- varInfo .setIsExplicitlyNotRemovable ();
1289
+ varInfo .setIsExplicitlyNotRemovable (() -> "exported class" );
1281
1290
traverseNode (baseClassExpression , scope );
1282
1291
// Use traverseChildren() here, because we should not consider any properties on the exported
1283
1292
// class to be removable.
1284
1293
traverseChildren (classBodyNode , classScope );
1285
1294
} else if (mayHaveSideEffects (baseClassExpression )) {
1286
1295
// TODO(bradfordcsmith): implement removal without losing side-effects for this case
1287
- varInfo .setIsExplicitlyNotRemovable ();
1296
+ varInfo .setIsExplicitlyNotRemovable (() -> "base class expression has side-effects" );
1288
1297
traverseNode (baseClassExpression , scope );
1289
1298
traverseClassMembers (classBodyNode , classScope );
1290
1299
} else if (mayHaveSideEffects (classBodyNode )) {
1291
- varInfo .setIsExplicitlyNotRemovable ();
1300
+ varInfo .setIsExplicitlyNotRemovable (() -> "class body has side-effects" );
1292
1301
traverseNode (baseClassExpression , scope );
1293
1302
traverseClassMembers (classBodyNode , classScope );
1294
1303
} else {
@@ -1620,7 +1629,8 @@ private VarInfo traverseVar(Var var) {
1620
1629
continue ;
1621
1630
}
1622
1631
1623
- getVarInfo (getVarForNameNode (lValue , functionScope )).setIsExplicitlyNotRemovable ();
1632
+ getVarInfo (getVarForNameNode (lValue , functionScope ))
1633
+ .setIsExplicitlyNotRemovable (() -> "parameter in function using arguments" );
1624
1634
}
1625
1635
// `arguments` is never removable.
1626
1636
return canonicalUnremovableVarInfo ;
@@ -1661,10 +1671,13 @@ private VarInfo getVarInfo(Var var) {
1661
1671
checkNotNull (var );
1662
1672
boolean isGlobal = var .isGlobal ();
1663
1673
if (var .isExtern ()) {
1674
+ unremovableLog .log (() -> SimpleFormat .format ("%s: extern" , var .getName ()));
1664
1675
return canonicalUnremovableVarInfo ;
1665
1676
} else if (codingConvention .isExported (var .getName (), /* local= */ !isGlobal )) {
1677
+ unremovableLog .log (() -> SimpleFormat .format ("%s: exported by convention" , var .getName ()));
1666
1678
return canonicalUnremovableVarInfo ;
1667
1679
} else if (var .isArguments ()) {
1680
+ // No point in logging that we cannot remove "arguments"
1668
1681
return canonicalUnremovableVarInfo ;
1669
1682
} else {
1670
1683
VarInfo varInfo = varInfoMap .get (var );
@@ -1677,9 +1690,9 @@ private VarInfo getVarInfo(Var var) {
1677
1690
// varInfo needs to track what value is assigned to it for the purpose of correctly allowing
1678
1691
// or preventing removal of properties set on it.
1679
1692
if (!removeGlobals && isGlobal ) {
1680
- varInfo .setIsExplicitlyNotRemovable ();
1693
+ varInfo .setIsExplicitlyNotRemovable (() -> "not removing globals" );
1681
1694
} else if (!removeLocalVars && !isGlobal ) {
1682
- varInfo .setIsExplicitlyNotRemovable ();
1695
+ varInfo .setIsExplicitlyNotRemovable (() -> "not removing locals" );
1683
1696
}
1684
1697
varInfoMap .put (var , varInfo );
1685
1698
}
@@ -1851,8 +1864,7 @@ boolean isAssignedValueLocal() {
1851
1864
* @return the Node representing the local value that is being assigned or `null` if the value
1852
1865
* is non-local or cannot be determined.
1853
1866
*/
1854
- @ Nullable
1855
- Node getLocalAssignedValue () {
1867
+ @ Nullable Node getLocalAssignedValue () {
1856
1868
return null ;
1857
1869
}
1858
1870
@@ -2852,7 +2864,7 @@ private interface VarInfo {
2852
2864
* all of the `Removable` objects associated with this variable and either apply their
2853
2865
* continuations or consider them for independent removal.
2854
2866
*/
2855
- void setIsExplicitlyNotRemovable ();
2867
+ void setIsExplicitlyNotRemovable (Supplier < String > reasonSupplier );
2856
2868
2857
2869
/**
2858
2870
* Record that at least one value assigned to the variable is non-local (comes from or escapes
@@ -2891,7 +2903,7 @@ public boolean isRemovable() {
2891
2903
}
2892
2904
2893
2905
@ Override
2894
- public void setIsExplicitlyNotRemovable () {
2906
+ public void setIsExplicitlyNotRemovable (Supplier < String > reasonSupplier ) {
2895
2907
// nothing to do
2896
2908
}
2897
2909
@@ -2917,6 +2929,7 @@ public void removeAllRemovables() {
2917
2929
/** Tracks the removable code and other state related to variables we may be able to remove. */
2918
2930
private final class RealVarInfo implements VarInfo {
2919
2931
final String varName ;
2932
+
2920
2933
/**
2921
2934
* Objects that represent variable declarations, assignments, or class setup calls that can be
2922
2935
* removed.
@@ -2975,7 +2988,8 @@ public void addRemovable(Removable removable) {
2975
2988
requiresLocalLiteralValueForRemoval = true ;
2976
2989
}
2977
2990
if (hasNonLocalOrNonLiteralValue && requiresLocalLiteralValueForRemoval ) {
2978
- setIsExplicitlyNotRemovable ();
2991
+ setIsExplicitlyNotRemovable (
2992
+ () -> "hasNonLocalOrNonLiteralValue && requiresLocalLiteralValueForRemoval" );
2979
2993
}
2980
2994
2981
2995
if (isEntirelyRemovable ) {
@@ -2992,9 +3006,10 @@ public boolean isRemovable() {
2992
3006
}
2993
3007
2994
3008
@ Override
2995
- public void setIsExplicitlyNotRemovable () {
3009
+ public void setIsExplicitlyNotRemovable (Supplier < String > reasonSupplier ) {
2996
3010
if (isEntirelyRemovable ) {
2997
3011
isEntirelyRemovable = false ;
3012
+ unremovableLog .log (SimpleFormat .format ("%s: %s" , varName , reasonSupplier .get ()));
2998
3013
for (Removable r : removables ) {
2999
3014
considerForIndependentRemoval (r );
3000
3015
}
@@ -3097,8 +3112,10 @@ private PolyfillInfo createPolyfillInfo(Node call, Scope scope, String name) {
3097
3112
private abstract class PolyfillInfo {
3098
3113
/** The {@link Polyfill} instance corresponding to the polyfill's definition. */
3099
3114
final Polyfill removable ;
3115
+
3100
3116
/** The rightmost component of the polyfill's qualified name (does not contain a dot). */
3101
3117
final String key ;
3118
+
3102
3119
/** Whether the polyfill is unreferenced and this can be removed safely. */
3103
3120
boolean isRemovable = true ;
3104
3121
0 commit comments