…lines 1–19 unchanged (19 lines)
20import com.google.common.collect.HashMultiset;
21import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.Lists;
23import com.google.common.collect.Maps;
24import com.google.common.collect.Multiset;
25import com.google.common.collect.Sets;
26import com.google.javascript.jscomp.NodeTraversal.ScopedCallback;
27import com.google.javascript.jscomp.Scope.Var;
28import com.google.javascript.rhino.Node;
29import com.google.javascript.rhino.Token;
30import com.google.javascript.rhino.TokenStream;
31
32import java.util.ArrayDeque;
…lines 33–41 unchanged (9 lines)
42 * @author johnlenz@google.com (John Lenz)
43 * TODO(johnlenz): Try to merge this with the ScopeCreator.
44 */
45class MakeDeclaredNamesUnique
46 implements NodeTraversal.ScopedCallback {
47
48 public static final String ARGUMENTS = "arguments";
49 private Deque<Renamer> nameStack = new ArrayDeque<Renamer>();
50 private final Renamer rootRenamer;
51
52 MakeDeclaredNamesUnique() {
53 this.rootRenamer = new ContextualRenamer();
54 }
…lines 55–230 unchanged (176 lines)
231 }
232
233 /**
234 * Inverts the transformation by {@link ContextualRenamer}, when possible.
235 */
236 static class ContextualRenameInverter
237 implements ScopedCallback, CompilerPass {
238 private final AbstractCompiler compiler;
239
240 // The set of names referenced in the current scope.
241 private Set<String> referencedNames = ImmutableSet.of();
242
243 // Stack reference sets.
244 private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>();
245
246 // Name are globally unique initially, so we don't need a per-scope map.
247 private Map<String, List<Node>> nameMap = Maps.newHashMap();
248
249 private ContextualRenameInverter(AbstractCompiler compiler) {
250 this.compiler = compiler;
251 }
252
253 public void process(Node externs, Node js) {
…lines 254–262 unchanged (9 lines)
263 return name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR);
264 }
265
266 private boolean containsSeparator(String name) {
267 return name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1;
268 }
269
270 /**
271 * Prepare a set for the new scope.
272 */
273 public void enterScope(NodeTraversal t) {
274 if (t.inGlobalScope()) {
275 return;
276 }
277
278 referenceStack.push(referencedNames);
279 referencedNames = Sets.newHashSet();
280 }
281
282 /**
283 * Rename vars for the current scope, and merge any referenced
284 * names into the parent scope reference set.
285 */
286 public void exitScope(NodeTraversal t) {
287 if (t.inGlobalScope()) {
288 return;
289 }
290
291 for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) {
292 Var v = it.next();
293 handleScopeVar(v);
294 }
295
296 // Merge any names that were referenced but not declared in the current
297 // scope.
298 Set<String> current = referencedNames;
299 referencedNames = referenceStack.pop();
300 // If there isn't anything left in the stack we will be going into the
301 // global scope: don't try to build a set of referenced names for the
302 // global scope.
303 if (!referenceStack.isEmpty()) {
304 referencedNames.addAll(current);
305 }
306 }
307
308 /**
309 * For the Var declared in the current scope determine if it is possible
310 * to revert the name to its orginal form without conflicting with other
311 * values.
312 */
313 void handleScopeVar(Var v) {
314 String name = v.getName();
315 if (containsSeparator(name)) {
316 String newName = getOrginalName(name);
317 // Check if the new name is valid and if it would cause conflicts.
318 if (TokenStream.isJSIdentifier(newName) &&
319 !referencedNames.contains(newName) &&
320 !newName.equals(ARGUMENTS)) {
321 referencedNames.remove(name);
322 // Adding a reference to the new name to prevent either the parent
323 // scopes or the current scope renaming another var to this new name.
324 referencedNames.add(newName);
325 List<Node> references = nameMap.get(name);
326 Preconditions.checkState(references != null);
327 for (Node n : references) {
328 Preconditions.checkState(n.getType() == Token.NAME);
329 n.setString(newName);
330 }
331 compiler.reportCodeChange();
332 }
333 nameMap.remove(name);
334 }
335 }
336
337 @Override
338 public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
339 return true;
340 }
341
342 @Override
343 public void visit(NodeTraversal t, Node node, Node parent) {
344 if (t.inGlobalScope()) {
345 return;
346 }
347
348 if (NodeUtil.isReferenceName(node)) {
349 String name = node.getString();
350 // Add all referenced names to the set so it is possible to check for
351 // conflicts.
352 referencedNames.add(name);
353 // Store only references to candidate names in the node map.
354 if (containsSeparator(name)) {
355 addCandidateNameReference(name, node);
356 }
357 }
358 }
359
360 private void addCandidateNameReference(String name, Node n) {
361 List<Node> nodes = nameMap.get(name);
362 if (null == nodes) {
363 nodes = Lists.newLinkedList();
364 nameMap.put(name, nodes);
365 }
366 nodes.add(n);
367 }
368 }
369
370 /**
371 * Rename every locally name to be unique, the first encountered declaration
372 * (specifically global names) are left in their original form. Those that are
373 * renamed are made unique by giving them a unique suffix based on
…lines 374–406 unchanged (33 lines)
407 }
408
409 /**
410 * Adds a name to the map of names declared in this scope.
411 */
412 @Override
413 public void addDeclaredName(String name) {
414 if (!name.equals(ARGUMENTS)) {
415 if (global) {
416 reserveName(name);
417 } else {
418 // It hasn't been declared locally yet, so increment the count.
419 if (!declarations.containsKey(name)) {
420 int id = incrementNameCount(name);
421 String newName = null;
422 if (id != 0) {
423 newName = getUniqueName(name, id);
424 }
425 declarations.put(name, newName);
426 }
427 }
428 }
429 }
430
431 @Override
432 public String getReplacementName(String oldName) {
433 return declarations.get(oldName);
434 }
435
…lines 436–479 unchanged (44 lines)
480 this.idPrefix = idPrefix;
481 this.removeConstness = removeConstness;
482 }
483
484 @Override
485 public void addDeclaredName(String name) {
486 Preconditions.checkState(!name.equals(ARGUMENTS));
487 if (!declarations.containsKey(name)) {
488 declarations.put(name, getUniqueName(name));
489 }
490 }
491
492 private String getUniqueName(String name) {
…lines 493–525 unchanged (33 lines)