Skip to content

Commit 35345da

Browse files
brad4dcopybara-github
authored andcommitted
Use LinkedHash(Map|Set) in more places for deterministic behavior
It's important that the compiler always iterate over maps and sets in a deterministic order. PiperOrigin-RevId: 564534609
1 parent 037ef68 commit 35345da

17 files changed

+107
-113
lines changed

src/com/google/javascript/jscomp/CheckConformance.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import com.google.protobuf.Descriptors;
2727
import com.google.protobuf.TextFormat;
2828
import java.util.ArrayList;
29-
import java.util.HashMap;
29+
import java.util.LinkedHashMap;
3030
import java.util.List;
3131
import java.util.Map;
3232
import org.jspecify.nullness.Nullable;
@@ -106,7 +106,9 @@ private static final class Category {
106106
}
107107
}
108108

109-
/** @param configs The rules to check. */
109+
/**
110+
* @param configs The rules to check.
111+
*/
110112
CheckConformance(AbstractCompiler compiler, ImmutableList<ConformanceConfig> configs) {
111113
this.compiler = compiler;
112114
// Initialize the map of functions to inspect for renaming candidates.
@@ -183,7 +185,7 @@ private static ImmutableList<Category> initRules(
183185
static List<Requirement> mergeRequirements(
184186
AbstractCompiler compiler, List<ConformanceConfig> configs) {
185187
List<Requirement.Builder> builders = new ArrayList<>();
186-
Map<String, Requirement.Builder> extendable = new HashMap<>();
188+
Map<String, Requirement.Builder> extendable = new LinkedHashMap<>();
187189
for (ConformanceConfig config : configs) {
188190
for (Requirement requirement : config.getRequirementList()) {
189191
Requirement.Builder builder = requirement.toBuilder();

src/com/google/javascript/jscomp/ConformanceRules.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
import java.lang.reflect.InvocationTargetException;
5858
import java.util.ArrayList;
5959
import java.util.Collection;
60-
import java.util.HashSet;
60+
import java.util.LinkedHashSet;
6161
import java.util.List;
6262
import java.util.Locale;
6363
import java.util.Set;
@@ -2021,7 +2021,7 @@ public static final class BanCreateElement extends AbstractRule {
20212021
public BanCreateElement(AbstractCompiler compiler, Requirement requirement)
20222022
throws InvalidRequirementSpec {
20232023
super(compiler, requirement);
2024-
bannedTags = new HashSet<>();
2024+
bannedTags = new LinkedHashSet<>();
20252025
for (String value : requirement.getValueList()) {
20262026
bannedTags.add(Ascii.toLowerCase(value));
20272027
}

src/com/google/javascript/jscomp/CrossChunkCodeMotion.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131
import java.util.BitSet;
3232
import java.util.Collection;
3333
import java.util.Deque;
34-
import java.util.HashMap;
35-
import java.util.HashSet;
34+
import java.util.LinkedHashMap;
3635
import java.util.LinkedHashSet;
3736
import java.util.List;
3837
import java.util.Map;
@@ -79,7 +78,7 @@ class CrossChunkCodeMotion implements CompilerPass {
7978
* Map from chunk to the node in that chunk that should parent variable declarations that have to
8079
* be moved into that chunk
8180
*/
82-
private final Map<JSChunk, Node> moduleInsertionPointMap = new HashMap<>();
81+
private final Map<JSChunk, Node> moduleInsertionPointMap = new LinkedHashMap<>();
8382

8483
private final boolean parentModuleCanSeeSymbolsDeclaredInChildren;
8584

@@ -132,7 +131,7 @@ private void addInstanceofGuards(Collection<GlobalSymbol> globalSymbols) {
132131
/** Collects all global symbols, their declaration statements and references. */
133132
private class GlobalSymbolCollector {
134133

135-
final Map<Var, GlobalSymbol> globalSymbolforVar = new HashMap<>();
134+
final Map<Var, GlobalSymbol> globalSymbolforVar = new LinkedHashMap<>();
136135

137136
/**
138137
* Returning the symbols in the reverse order in which they are defined helps to minimize
@@ -251,9 +250,10 @@ private class GlobalSymbol {
251250
*
252251
* <p>This is a LinkedHashSet in order to enforce a consistent ordering when we iterate over
253252
* these to identify cycles. This guarantees that the order in which statements are moved won't
254-
* depend on the arbitrary ordering of HashSet.
253+
* depend on the arbitrary ordering of LinkedHashSet.
255254
*/
256255
final Set<GlobalSymbol> referencingGlobalSymbols = new LinkedHashSet<>();
256+
257257
/** Instanceof references we may need to update with a guard after moving declarations. */
258258
final Deque<InstanceofReference> instanceofReferencesToGuard = new ArrayDeque<>();
259259
/** Used by OrderAndCombineGlobalSymbols to find reference cycles. */
@@ -422,7 +422,7 @@ private void addGuardToInstanceofReference(Node referenceNode) {
422422
private static class DeclarationStatementGroup {
423423

424424
final GlobalSymbol declaredGlobalSymbol;
425-
final Set<GlobalSymbol> referencedGlobalSymbols = new HashSet<>();
425+
final Set<GlobalSymbol> referencedGlobalSymbols = new LinkedHashSet<>();
426426

427427
/** chunk containing the statements */
428428
JSChunk currentChunk;

src/com/google/javascript/jscomp/CrossChunkReferenceCollector.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.google.javascript.rhino.Node;
2727
import java.util.ArrayList;
2828
import java.util.Collections;
29-
import java.util.HashMap;
3029
import java.util.LinkedHashMap;
3130
import java.util.List;
3231
import java.util.Map;
@@ -37,7 +36,7 @@
3736
public final class CrossChunkReferenceCollector implements ScopedCallback, CompilerPass {
3837

3938
/** Maps global variable name to the corresponding {@link Var} object. */
40-
private final Map<String, Var> varsByName = new HashMap<>();
39+
private final Map<String, Var> varsByName = new LinkedHashMap<>();
4140

4241
/**
4342
* Maps a given variable to a collection of references to that name. Note that Var objects are not

src/com/google/javascript/jscomp/DotFormatter.java

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,19 @@
2828
import com.google.javascript.rhino.jstype.JSType;
2929
import java.io.IOException;
3030
import java.util.Arrays;
31-
import java.util.HashMap;
31+
import java.util.LinkedHashMap;
3232
import java.util.List;
3333
import org.jspecify.nullness.Nullable;
3434

3535
/**
36-
* <p>DotFormatter prints out a dot file of the Abstract Syntax Tree.
37-
* For a detailed description of the dot format and visualization tool refer
38-
* to <a href="http://www.graphviz.org">Graphviz</a>.</p>
39-
* <p>Typical usage of this class</p>
40-
* <code>System.out.println(new DotFormatter().toDot(<i>node</i>));</code>
41-
* <p>This class is <b>not</b> thread safe and should not be used without proper
42-
* external synchronization.</p>
36+
* DotFormatter prints out a dot file of the Abstract Syntax Tree. For a detailed description of the
37+
* dot format and visualization tool refer to <a href="http://www.graphviz.org">Graphviz</a>.
38+
*
39+
* <p>Typical usage of this class <code>logfile.write(new DotFormatter().toDot(<i>node</i>));
40+
* </code>
41+
*
42+
* <p>This class is <b>not</b> thread safe and should not be used without proper external
43+
* synchronization.
4344
*/
4445
public final class DotFormatter {
4546
private static final String INDENT = " ";
@@ -48,7 +49,7 @@ public final class DotFormatter {
4849
private static final int MAX_LABEL_NAME_LENGTH = 10;
4950

5051
// stores the current assignment of node to keys
51-
private final HashMap<Node, Integer> assignments = new HashMap<>();
52+
private final LinkedHashMap<Node, Integer> assignments = new LinkedHashMap<>();
5253

5354
// key count in order to assign a unique key to each node
5455
private int keyCount = 0;
@@ -67,8 +68,9 @@ private DotFormatter() {
6768
this.printAnnotations = false;
6869
}
6970

70-
private DotFormatter(Node n, ControlFlowGraph<Node> cfg,
71-
Appendable builder, boolean printAnnotations) throws IOException {
71+
private DotFormatter(
72+
Node n, ControlFlowGraph<Node> cfg, Appendable builder, boolean printAnnotations)
73+
throws IOException {
7274
this.cfg = cfg;
7375
this.builder = builder;
7476
this.printAnnotations = printAnnotations;
@@ -80,10 +82,11 @@ private DotFormatter(Node n, ControlFlowGraph<Node> cfg,
8082

8183
/**
8284
* Converts an AST to dot representation.
85+
*
8386
* @param n the root of the AST described in the dot formatted string
8487
* @return the dot representation of the AST
8588
*/
86-
public static String toDot(Node n) throws IOException {
89+
public static String toDot(Node n) throws IOException {
8790
return toDot(n, null);
8891
}
8992

@@ -166,18 +169,17 @@ public static String toDot(GraphvizGraph graph) {
166169

167170
/**
168171
* Converts an AST to dot representation and appends it to the given buffer.
172+
*
169173
* @param n the root of the AST described in the dot formatted string
170174
* @param inCFG Control Flow Graph.
171175
* @param builder A place to dump the graph.
172176
*/
173-
static void appendDot(Node n, ControlFlowGraph<Node> inCFG,
174-
Appendable builder) throws IOException {
177+
static void appendDot(Node n, ControlFlowGraph<Node> inCFG, Appendable builder)
178+
throws IOException {
175179
DotFormatter unused = new DotFormatter(n, inCFG, builder, false);
176180
}
177181

178-
/**
179-
* Creates a DotFormatter purely for testing DotFormatter's internal methods.
180-
*/
182+
/** Creates a DotFormatter purely for testing DotFormatter's internal methods. */
181183
static DotFormatter newInstanceForTesting() {
182184
return new DotFormatter();
183185
}
@@ -187,8 +189,7 @@ private void traverseNodes(Node parent) throws IOException {
187189
int keyParent = key(parent);
188190

189191
// edges
190-
for (Node child = parent.getFirstChild(); child != null;
191-
child = child.getNext()) {
192+
for (Node child = parent.getFirstChild(); child != null; child = child.getNext()) {
192193
int keyChild = key(child);
193194
builder.append(INDENT);
194195
builder.append(formatNodeName(keyParent));
@@ -216,9 +217,14 @@ private void traverseNodes(Node parent) throws IOException {
216217
}
217218

218219
edgeList[i] =
219-
formatNodeName(keyParent) + ARROW + toNode + " [label=\"" + edge.getValue() + "\", "
220-
+ "fontcolor=\"red\", "
221-
+ "weight=0.01, color=\"red\"];\n";
220+
formatNodeName(keyParent)
221+
+ ARROW
222+
+ toNode
223+
+ " [label=\""
224+
+ edge.getValue()
225+
+ "\", "
226+
+ "fontcolor=\"red\", "
227+
+ "weight=0.01, color=\"red\"];\n";
222228
}
223229

224230
Arrays.sort(edgeList);

src/com/google/javascript/jscomp/GenerateExports.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import com.google.javascript.rhino.Node;
2525
import com.google.javascript.rhino.jstype.JSType;
2626
import com.google.javascript.rhino.jstype.JSTypeNative;
27-
import java.util.HashSet;
27+
import java.util.LinkedHashSet;
2828
import java.util.Map;
2929
import java.util.Set;
3030
import org.jspecify.nullness.Nullable;
@@ -42,7 +42,7 @@ public class GenerateExports implements CompilerPass {
4242

4343
private final boolean allowNonGlobalExports;
4444

45-
private final Set<String> exportedVariables = new HashSet<>();
45+
private final Set<String> exportedVariables = new LinkedHashSet<>();
4646

4747
static final DiagnosticType MISSING_EXPORT_CONVENTION =
4848
DiagnosticType.error(

src/com/google/javascript/jscomp/IdMappingUtil.java

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,38 +23,29 @@
2323
import com.google.common.collect.HashBiMap;
2424
import com.google.common.collect.ImmutableMap;
2525
import com.google.javascript.jscomp.base.format.SimpleFormat;
26-
import java.util.HashMap;
26+
import java.util.LinkedHashMap;
2727
import java.util.Map;
2828

29-
/**
30-
* A utility class for generating and parsing id mappings held by {@link ReplaceIdGenerators}.
31-
*/
29+
/** A utility class for generating and parsing id mappings held by {@link ReplaceIdGenerators}. */
3230
public final class IdMappingUtil {
3331

34-
@VisibleForTesting
35-
static final char NEW_LINE = '\n';
32+
@VisibleForTesting static final char NEW_LINE = '\n';
3633

3734
private static final Splitter LINE_SPLITTER = Splitter.on(NEW_LINE).omitEmptyStrings();
3835

3936
// Prevent instantiation.
4037
private IdMappingUtil() {}
4138

4239
/**
43-
* @return The serialize map of generators and their ids and their
44-
* replacements.
40+
* @return The serialize map of generators and their ids and their replacements.
4541
*/
4642
static String generateSerializedIdMappings(Map<String, Map<String, String>> idGeneratorMaps) {
4743
StringBuilder sb = new StringBuilder();
4844
for (Map.Entry<String, Map<String, String>> replacements : idGeneratorMaps.entrySet()) {
4945
if (!replacements.getValue().isEmpty()) {
50-
sb.append('[')
51-
.append(replacements.getKey())
52-
.append(']')
53-
.append(NEW_LINE)
54-
.append(NEW_LINE);
46+
sb.append('[').append(replacements.getKey()).append(']').append(NEW_LINE).append(NEW_LINE);
5547

56-
for (Map.Entry<String, String> replacement :
57-
replacements.getValue().entrySet()) {
48+
for (Map.Entry<String, String> replacement : replacements.getValue().entrySet()) {
5849
sb.append(replacement.getKey())
5950
.append(':')
6051
.append(replacement.getValue())
@@ -69,27 +60,23 @@ static String generateSerializedIdMappings(Map<String, Map<String, String>> idGe
6960
/**
7061
* The expected format looks like this:
7162
*
72-
* <p>[generatorName1]
73-
* someId1:someFile:theLine:theColumn
74-
* ...
63+
* <p>[generatorName1] someId1:someFile:theLine:theColumn ...
7564
*
76-
* <p>[[generatorName2]
77-
* someId2:someFile:theLine:theColumn]
78-
* ...
65+
* <p>[[generatorName2] someId2:someFile:theLine:theColumn] ...
7966
*
8067
* <p>The returned data is grouped by generator name (the map key). The inner map provides
81-
* mappings from id to content (file, line and column info). In a glimpse, the structure is
82-
* {@code Map<generator name, BiMap<id, value>>}.
68+
* mappings from id to content (file, line and column info). In a glimpse, the structure is {@code
69+
* Map<generator name, BiMap<id, value>>}.
8370
*
8471
* <p>@throws IllegalArgumentException malformed input where there it 1) has duplicate generator
85-
* name, or 2) the line has no ':' for id and its content.
72+
* name, or 2) the line has no ':' for id and its content.
8673
*/
8774
public static Map<String, BiMap<String, String>> parseSerializedIdMappings(String idMappings) {
8875
if (Strings.isNullOrEmpty(idMappings)) {
8976
return ImmutableMap.of();
9077
}
9178

92-
Map<String, BiMap<String, String>> resultMap = new HashMap<>();
79+
Map<String, BiMap<String, String>> resultMap = new LinkedHashMap<>();
9380
BiMap<String, String> currentSectionMap = null;
9481

9582
int lineIndex = 0;
@@ -106,7 +93,8 @@ public static Map<String, BiMap<String, String>> parseSerializedIdMappings(Strin
10693
resultMap.put(currentSection, currentSectionMap);
10794
} else {
10895
throw new IllegalArgumentException(
109-
SimpleFormat.format("Cannot parse id map: %s\n Line: $s, lineIndex: %s",
96+
SimpleFormat.format(
97+
"Cannot parse id map: %s\n Line: $s, lineIndex: %s",
11098
idMappings, line, lineIndex));
11199
}
112100
} else {
@@ -117,7 +105,8 @@ public static Map<String, BiMap<String, String>> parseSerializedIdMappings(Strin
117105
currentSectionMap.put(name, location);
118106
} else {
119107
throw new IllegalArgumentException(
120-
SimpleFormat.format("Cannot parse id map: %s\n Line: $s, lineIndex: %s",
108+
SimpleFormat.format(
109+
"Cannot parse id map: %s\n Line: $s, lineIndex: %s",
121110
idMappings, line, lineIndex));
122111
}
123112
}

src/com/google/javascript/jscomp/InlineSimpleMethods.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import com.google.javascript.rhino.IR;
2424
import com.google.javascript.rhino.JSDocInfo;
2525
import com.google.javascript.rhino.Node;
26-
import java.util.HashSet;
26+
import java.util.LinkedHashSet;
2727
import java.util.Set;
2828
import org.jspecify.nullness.Nullable;
2929

@@ -61,7 +61,7 @@ class InlineSimpleMethods implements CompilerPass {
6161
// - non-method properties
6262
// - methods with @noinline
6363
// - methods with multiple, non-equivalent definitions
64-
private final Set<String> nonInlineableProperties = new HashSet<>();
64+
private final Set<String> nonInlineableProperties = new LinkedHashSet<>();
6565

6666
// Use a linked map here to keep the output deterministic. Otherwise,
6767
// the choice of method bodies is random when multiple identical definitions

src/com/google/javascript/jscomp/Normalize.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import com.google.javascript.rhino.JSDocInfo;
2828
import com.google.javascript.rhino.Node;
2929
import com.google.javascript.rhino.Token;
30-
import java.util.HashMap;
31-
import java.util.HashSet;
30+
import java.util.LinkedHashMap;
31+
import java.util.LinkedHashSet;
3232
import java.util.Map;
3333
import java.util.Set;
3434
import org.jspecify.nullness.Nullable;
@@ -167,7 +167,7 @@ public void process(Node externs, Node root) {
167167
public void visit(NodeTraversal t, Node n, Node parent) {
168168
// Note: Constant properties annotations are not propagated.
169169
if (!n.isName() || n.getString().isEmpty()) {
170-
return;
170+
return;
171171
}
172172

173173
// Find the JSDocInfo for a top-level variable
@@ -214,7 +214,7 @@ public void process(Node externs, Node root) {
214214
NodeTraversal.traverseRoots(compiler, this, externs, root);
215215
}
216216

217-
private final Map<String, Boolean> constantMap = new HashMap<>();
217+
private final Map<String, Boolean> constantMap = new LinkedHashMap<>();
218218

219219
@Override
220220
public void visit(NodeTraversal t, Node n, Node parent) {
@@ -683,7 +683,7 @@ private void removeDuplicateDeclarations(Node externs, Node root) {
683683
private final class DuplicateDeclarationHandler
684684
implements SyntacticScopeCreator.RedeclarationHandler {
685685

686-
private final Set<Var> hasOkDuplicateDeclaration = new HashSet<>();
686+
private final Set<Var> hasOkDuplicateDeclaration = new LinkedHashSet<>();
687687

688688
/** Remove duplicate VAR declarations discovered during scope creation. */
689689
@Override

0 commit comments

Comments
 (0)