Fix nondeterminism in JsDuplicateFunctionRemover.

JsDuplicateFunctionRemover is only used under some circumstances,
in particular requires compiler.stackMode=strip.

Bug: issue 8871.
Change-Id: I9ab1a85ea604a65e477c076b5e3028032ef61d8d
diff --git a/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java b/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
index 7caf39b..d571ffd 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
@@ -25,11 +25,10 @@
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsVisitor;
-import com.google.gwt.dev.util.collect.IdentityHashSet;
 import com.google.gwt.dev.util.collect.Stack;
+import com.google.gwt.thirdparty.guava.common.collect.Maps;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
 
-import java.util.HashMap;
-import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -42,20 +41,19 @@
 
   private class DuplicateFunctionBodyRecorder extends JsVisitor {
 
-    private final Set<JsName> dontReplace = new IdentityHashSet<JsName>();
+    private final Set<JsName> dontReplace = Sets.newIdentityHashSet();
 
-    private final Map<JsName, JsName> duplicateOriginalMap = new IdentityHashMap<JsName, JsName>();
+    private final Map<JsName, JsName> duplicateOriginalMap = Maps.newIdentityHashMap();
 
-    private final Map<JsFunction, JsFunction> duplicateMethodOriginalMap = new IdentityHashMap<JsFunction, JsFunction>();
-
+    private final Map<JsFunction, JsFunction> duplicateMethodOriginalMap = Maps.newLinkedHashMap();
 
     private final Stack<JsNameRef> invocationQualifiers = new Stack<JsNameRef>();
 
     // static / global methods
-    private final Map<String, JsName> uniqueBodies = new HashMap<String, JsName>();
+    private final Map<String, JsName> uniqueBodies = Maps.newHashMap();
 
     // vtable methods
-    private final Map<String, JsFunction> uniqueMethodBodies = new HashMap<String, JsFunction>();
+    private final Map<String, JsFunction> uniqueMethodBodies = Maps.newHashMap();
 
     public DuplicateFunctionBodyRecorder() {
       // Add sentinel to stop Stack.peek() from throwing exception.
@@ -245,7 +243,7 @@
 
       DuplicateFunctionBodyRecorder dfbr = new DuplicateFunctionBodyRecorder();
       dfbr.accept(fragment);
-      Map<JsFunction, JsName> newNamesByHoistedFunction = new HashMap<JsFunction, JsName>();
+      Map<JsFunction, JsName> newNamesByHoistedFunction = Maps.newHashMap();
       // Hoist all anonymous duplicate functions.
       Map<JsFunction, JsFunction> dupMethodMap = dfbr.getDuplicateMethodMap();
       for (JsFunction dupMethod : dupMethodMap.values()) {
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index 6696850..daa726f 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -45,6 +45,9 @@
 public class CompilerTest extends ArgProcessorTestBase {
 
   public static final String GWT_PERSISTENTUNITCACHE = "gwt.persistentunitcache";
+  public static final String HELLO_MODULE = "com.google.gwt.sample.hello.Hello";
+  public static final String HELLO_MODULE_STACKMODE_STRIP =
+      "com.google.gwt.sample.hello.Hello_stackMode_strip";
   private final Compiler.ArgProcessor argProcessor;
   private final CompilerOptionsImpl options = new CompilerOptionsImpl();
 
@@ -391,16 +394,22 @@
     assertEquals(SourceLevel.JAVA7, SourceLevel.getBestMatchingVersion("1.7b3"));
   }
 
+  public void testDeterministicBuild_Draft_StackModeStrip() throws
+      UnableToCompleteException, IOException {
+    assertDeterministicBuild(HELLO_MODULE_STACKMODE_STRIP, 0);
+  }
+
+  public void testDeterministicBuild_Optimized_StackModeStrip() throws
+      UnableToCompleteException, IOException {
+    assertDeterministicBuild(HELLO_MODULE_STACKMODE_STRIP, 9);
+  }
+
   public void testDeterministicBuild_Draft() throws UnableToCompleteException, IOException {
-    final CompilerOptionsImpl options = new CompilerOptionsImpl();
-    options.setOptimizationLevel(0);
-    assertDeterministicBuild(options);
+    assertDeterministicBuild(HELLO_MODULE, 0);
   }
 
   public void testDeterministicBuild_Optimized() throws UnableToCompleteException, IOException {
-    final CompilerOptionsImpl options = new CompilerOptionsImpl();
-    options.setOptimizationLevel(9);
-    assertDeterministicBuild(options);
+    assertDeterministicBuild(HELLO_MODULE, 9);
   }
 
   // TODO(stalcup): add recompile tests for file deletion.
@@ -631,13 +640,17 @@
         outputOption);
   }
 
-  private void assertDeterministicBuild(CompilerOptions options)
+  private void assertDeterministicBuild(String topLevelModule, int optimizationLevel)
       throws UnableToCompleteException, IOException {
+
+    final CompilerOptionsImpl options = new CompilerOptionsImpl();
+    options.setOptimizationLevel(optimizationLevel);
+
     File firstCompileWorkDir = Utility.makeTemporaryDirectory(null, "hellowork");
     File secondCompileWorkDir = Utility.makeTemporaryDirectory(null, "hellowork");
     String oldPersistentUnitCacheValue = System.setProperty(GWT_PERSISTENTUNITCACHE, "false");
     try {
-      options.addModuleName("com.google.gwt.sample.hello.Hello");
+      options.addModuleName(topLevelModule);
       options.setWarDir(new File(firstCompileWorkDir, "war"));
       options.setExtraDir(new File(firstCompileWorkDir, "extra"));
       PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
diff --git a/samples/hello/src/com/google/gwt/sample/hello/Hello_stackMode_strip.gwt.xml b/samples/hello/src/com/google/gwt/sample/hello/Hello_stackMode_strip.gwt.xml
new file mode 100644
index 0000000..95d6bc1
--- /dev/null
+++ b/samples/hello/src/com/google/gwt/sample/hello/Hello_stackMode_strip.gwt.xml
@@ -0,0 +1,19 @@
+<!--                                                                        -->
+<!-- Copyright 2014 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- may obtain a copy of the License at                                    -->
+<!--                                                                        -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                        -->
+<!-- Unless required by applicable law or agreed to in writing, software    -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
+<!-- implied. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<module rename-to="hello">
+  <inherits name="com.google.gwt.user.User"/>
+  <entry-point class="com.google.gwt.sample.hello.client.Hello"/>
+  <set-property name="compiler.stackMode" value="strip" />
+</module>