16
16
17
17
package com .google .javascript .jscomp ;
18
18
19
- import com .google .common .annotations .VisibleForTesting ;
20
19
import com .google .common .collect .ImmutableMap ;
21
20
import com .google .javascript .jscomp .NodeTraversal .AbstractPostOrderCallback ;
22
21
import com .google .javascript .rhino .Node ;
26
25
import java .util .LinkedHashMap ;
27
26
import java .util .Map ;
28
27
import java .util .Set ;
28
+ import org .jspecify .nullness .Nullable ;
29
29
30
30
/**
31
31
* Replaces calls to id generators with ids.
32
32
*
33
33
* <p>Use this to get unique and short ids.
34
34
*/
35
35
class ReplaceToggles implements CompilerPass {
36
+
37
+ // NOTE: These diagnostics are only checked in Stage 2 (optimization), since none of this
38
+ // code should ever be hand-written: the `CLOSURE_TOGGLE_ORDINALS` bootstrap and the calls to
39
+ // `goog.readToggleInternalDoNotCallDirectly` are all generated automatically by the build
40
+ // system, so it's unexpected that anyone should ever run into these diagnostics when doing
41
+ // ordinary development.
42
+
36
43
static final DiagnosticType INVALID_TOGGLE_PARAMETER =
37
44
DiagnosticType .error (
38
45
"JSC_INVALID_TOGGLE_PARAMETER" ,
@@ -53,18 +60,17 @@ class ReplaceToggles implements CompilerPass {
53
60
// NOTE: These values are chosen as negative integers because actual toggle ordinals must always
54
61
// be non-negative (at least zero). Any negative integers would do to distinguish them from real
55
62
// toggle ordinals, but -1 and -2 are the simplest.
56
- @ VisibleForTesting static final int TRUE_VALUE = -2 ;
57
-
58
- @ VisibleForTesting static final int FALSE_VALUE = -1 ;
63
+ private static final int TRUE_VALUE = -2 ;
64
+ private static final int FALSE_VALUE = -1 ;
59
65
60
66
private final AbstractCompiler compiler ;
61
67
private final AstFactory astFactory ;
62
- private final boolean check ;
63
68
64
- ReplaceToggles (AbstractCompiler compiler , boolean check ) {
69
+ private @ Nullable ImmutableMap <String , Integer > ordinalMapping = null ;
70
+
71
+ ReplaceToggles (AbstractCompiler compiler ) {
65
72
this .compiler = compiler ;
66
73
this .astFactory = compiler .createAstFactory ();
67
- this .check = check ;
68
74
}
69
75
70
76
@ Override
@@ -80,20 +86,21 @@ private class Traversal extends AbstractPostOrderCallback {
80
86
81
87
@ Override
82
88
public void visit (NodeTraversal t , Node n , Node parent ) {
83
- // Look for `var CLOSURE_TOGGLE_ORDINALS = {...};`. Note that we only do this in check mode.
84
- // It's not our responsibility to delete the unused variable in optimized mode - if all
85
- // the calls to readToggle are deleted, then the bootstrap will be unused and deleted, too.
86
- if (check
87
- && NodeUtil .isNameDeclaration (n )
88
- && n .getFirstChild ().matchesName (ORDINAL_VAR_NAME )) {
89
+ // Look for `var CLOSURE_TOGGLE_ORDINALS = {...};`, record the mapping, and then delete the
90
+ // declaration from the AST since we should no longer need it (and the optimizer won't
91
+ // delete it in case the global assignment was an intended side effect).
92
+ if (NodeUtil .isNameDeclaration (n ) && n .getFirstChild ().matchesName (ORDINAL_VAR_NAME )) {
89
93
Node rhs = n .getFirstFirstChild ();
90
94
91
95
if (rhs == null && n .getToken () == Token .VAR ) {
92
- // An empty var is a type definition, which is OK .
96
+ // An empty var is fine; it should get deleted later .
93
97
return ;
94
98
} else if (!rhs .isObjectLit ()) {
95
99
compiler .report (JSError .make (n , INVALID_ORDINAL_MAPPING , "not an object literal" ));
96
100
return ;
101
+ } else if (ordinalMapping != null ) {
102
+ compiler .report (JSError .make (n , INVALID_ORDINAL_MAPPING , "multiple initialized copies" ));
103
+ return ;
97
104
}
98
105
99
106
Map <String , Integer > mapping = new LinkedHashMap <>();
@@ -126,10 +133,10 @@ public void visit(NodeTraversal t, Node n, Node parent) {
126
133
mapping .put (key , intValue );
127
134
ordinals .add (intValue );
128
135
}
129
- compiler . setToggleOrdinalMapping ( ImmutableMap .copyOf (mapping ) );
136
+ ReplaceToggles . this . ordinalMapping = ImmutableMap .copyOf (mapping );
130
137
131
138
// NOTE: We do not support a simple assignment without `var` since reassignment or later
132
- // augmentation is not allowed.
139
+ // augmentation (i.e. `CLOSURE_TOGGLE_ORDINALS['foo'] = true`) is not allowed.
133
140
return ;
134
141
}
135
142
@@ -147,15 +154,11 @@ public void visit(NodeTraversal t, Node n, Node parent) {
147
154
return ;
148
155
}
149
156
150
- ImmutableMap <String , Integer > toggles = compiler .getToggleOrdinalMapping ();
151
- if (check ) {
152
- if (toggles != null && !toggles .containsKey (arg .getString ())) {
153
- compiler .report (JSError .make (n , UNKNOWN_TOGGLE ));
154
- }
155
- return ;
157
+ if (ordinalMapping != null && !ordinalMapping .containsKey (arg .getString ())) {
158
+ compiler .report (JSError .make (n , UNKNOWN_TOGGLE ));
156
159
}
157
160
158
- Integer ordinal = toggles != null ? toggles .get (arg .getString ()) : null ;
161
+ Integer ordinal = ordinalMapping != null ? ordinalMapping .get (arg .getString ()) : null ;
159
162
if (ordinal == null || ordinal < 0 ) {
160
163
// No ordinals given: hard-code `true` if explicitly set as true, or `false` otherwise.
161
164
n .replaceWith (
0 commit comments