This change attempts to fix some compile instabilities. In particular, there are
three issues:

1) Source origin information can vary based on the directory in which something
is checked-out into and built, even if it's identical
2) Timestamps on output emitted artifacts are set to current time. They should
be set based on the original file stamp, or in the case of synthetic artifacts
like the output cache.js files (which depend on all of the compiler inputs), the
timestamp should perhaps be set based on the MAX(over all input lastmodified
timestamps)
3) CompilationUnitArchive was serializing an array of CompiledClass in a
non-stable way.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10799 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
index 14ecc1a..c1d6412 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
@@ -17,10 +17,14 @@
 
 import com.google.gwt.dev.jjs.impl.GwtAstBuilder;
 import com.google.gwt.dev.util.DiskCacheToken;
+import com.google.gwt.dev.util.Util;
 
 import org.eclipse.jdt.core.compiler.CategorizedProblem;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -57,7 +61,8 @@
     this.contentId = unit.getContentId();
     this.dependencies = unit.getDependencies();
     this.resourcePath = unit.getResourcePath();
-    this.jsniMethods = unit.getJsniMethods();
+    this.resourceLocation = Util.stripJarPathPrefix(resourceLocation);
+    this.jsniMethods = sortJsniMethods(unit.getJsniMethods());
     this.methodArgNamesLookup = unit.getMethodArgs();
     this.typeName = unit.getTypeName();
     this.isError = unit.isError();
@@ -69,7 +74,6 @@
 
     // Override these fields
     this.lastModified = lastModified;
-    this.resourceLocation = resourceLocation;
   }
 
   /**
@@ -88,9 +92,9 @@
     this.compiledClasses = CompiledClass.copyForUnit(unit.getCompiledClasses(), this);
     this.contentId = unit.getContentId();
     this.dependencies = unit.getDependencies();
-    this.resourceLocation = unit.getResourceLocation();
     this.resourcePath = unit.getResourcePath();
-    this.jsniMethods = unit.getJsniMethods();
+    this.resourceLocation = Util.stripJarPathPrefix(unit.getResourceLocation());
+    this.jsniMethods = sortJsniMethods(unit.getJsniMethods());
     this.lastModified = unit.getLastModified();
     this.methodArgNamesLookup = unit.getMethodArgs();
     this.typeName = unit.getTypeName();
@@ -190,4 +194,21 @@
   long getTypesSerializedVersion() {
     return astVersion;
   }
+
+  private List<JsniMethod> sortJsniMethods(List<JsniMethod> jsniMethods) {
+    if (jsniMethods == null) {
+      return null;
+    }
+    
+    // copy because the source may be unmodifiable or singleton
+    ArrayList<JsniMethod> copy = new ArrayList<JsniMethod>(jsniMethods);
+
+    Collections.sort(copy, new Comparator<JsniMethod>() {
+      @Override
+      public int compare(JsniMethod o1, JsniMethod o2) {
+        return o1.name().compareTo(o2.name());
+      }
+    });
+    return copy;
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
index f253eba..9a0f73d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -30,6 +30,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -68,7 +69,13 @@
       }
       copyCc.enclosingClass = newRef;
     }
-
+    // sort classes to maintain stability in compilation unit archives
+    Collections.sort(copy, new Comparator<CompiledClass>() {
+      @Override
+      public int compare(CompiledClass o1, CompiledClass o2) {
+        return o1.getSourceName().compareTo(o2.getSourceName());
+      }
+    });
     return Collections.unmodifiableCollection(copy);
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
index b19eb9a..4a879c0 100644
--- a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
+++ b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
@@ -182,7 +182,7 @@
 
     @Override
     public String optionalFileLocation() {
-      return file.exists() ? file.getAbsolutePath() : null;
+      return file.exists() ? Util.stripJarPathPrefix(file.getAbsolutePath()) : null;
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
index 3e42270..8d102a5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.jjs.Correlation.Axis;
 import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
 import com.google.gwt.dev.util.StringInterner;
+import com.google.gwt.dev.util.Util;
 
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -117,7 +118,7 @@
   private final int startLine;
 
   private SourceOrigin(String location, int startLine) {
-    this.fileName = StringInterner.get().intern(location);
+    this.fileName = StringInterner.get().intern(Util.stripJarPathPrefix(location));
     this.startLine = startLine;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index 0bbd4fe..89ccbed 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -910,6 +910,21 @@
   }
 
   /**
+   * Remove leading file:jar:...!/ prefix from source paths for source located in jars.
+   * @param absolutePath an absolute JAR file URL path
+   * @return the location of the file within the JAR
+   */
+  public static String stripJarPathPrefix(String absolutePath) {
+    if (absolutePath != null) {
+      int bang = absolutePath.lastIndexOf('!');
+      if (bang != -1) {
+        return absolutePath.substring(bang + 2);
+      }
+    }
+    return absolutePath;
+  }
+
+  /**
    * Get a large byte buffer local to this thread. Currently this is set to a
    * 16k buffer, which is small enough to fit into the L2 cache on modern
    * processors. The contents of the returned buffer are undefined. Calling
@@ -1480,5 +1495,4 @@
    */
   private Util() {
   }
-
 }