CachedCompilationUnits were inadvertently writing
two compies when serialized, because CompiledClass
contained a reference to the previous compilation
unit used to create the CCU.
Review at http://gwt-code-reviews.appspot.com/1517803
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10521 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 7995d81..14ecc1a 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
@@ -53,7 +53,7 @@
public CachedCompilationUnit(CachedCompilationUnit unit, long lastModified,
String resourceLocation) {
assert unit != null;
- this.compiledClasses = unit.getCompiledClasses();
+ this.compiledClasses = CompiledClass.copyForUnit(unit.getCompiledClasses(), this);
this.contentId = unit.getContentId();
this.dependencies = unit.getDependencies();
this.resourcePath = unit.getResourcePath();
@@ -85,7 +85,7 @@
@SuppressWarnings("deprecation")
CachedCompilationUnit(CompilationUnit unit, long astToken) {
assert unit != null;
- this.compiledClasses = unit.getCompiledClasses();
+ this.compiledClasses = CompiledClass.copyForUnit(unit.getCompiledClasses(), this);
this.contentId = unit.getContentId();
this.dependencies = unit.getDependencies();
this.resourceLocation = unit.getResourceLocation();
@@ -109,7 +109,7 @@
this.astToken = new DiskCacheToken(astToken);
this.astVersion = GwtAstBuilder.getSerializationVersion();
}
-
+
@Override
public CachedCompilationUnit asCachedCompilationUnit() {
return this;
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 89d1e45..f253eba 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -16,16 +16,23 @@
package com.google.gwt.dev.javac;
import com.google.gwt.dev.javac.TypeOracleMediator.TypeData;
+import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.util.DiskCache;
import com.google.gwt.dev.util.DiskCacheToken;
-import com.google.gwt.dev.util.StringInterner;
import com.google.gwt.dev.util.Name.InternalName;
+import com.google.gwt.dev.util.StringInterner;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* Encapsulates the state of a single compiled class file.
@@ -34,20 +41,52 @@
private static final DiskCache diskCache = DiskCache.INSTANCE;
- private final CompiledClass enclosingClass;
- private final String internalName;
- private final String sourceName;
- private final boolean isLocal;
- private transient TypeData typeData;
- private CompilationUnit unit;
- private String signatureHash;
+ static Collection<CompiledClass> copyForUnit(Collection<CompiledClass> in, CompilationUnit newUnit) {
+ if (in == null) {
+ return null;
+ }
+ CompiledClass[] orig = new CompiledClass[in.size()];
+ List<CompiledClass> copy = new ArrayList<CompiledClass>();
+
+ Map<CompiledClass, CompiledClass> enclosingClassMap = new HashMap<CompiledClass, CompiledClass>();
+ for (CompiledClass cc : in) {
+ CompiledClass copyCc = new CompiledClass(cc, newUnit);
+ copy.add(copyCc);
+ enclosingClassMap.put(cc, copyCc);
+ }
+
+ // Update the enclosing class references. With enough effort, we could determine the
+ // hierarchical relationship of compiled classes and initialize the copies with the
+ // copied enclosing class, but this is less effort.
+ for (CompiledClass copyCc : copy) {
+ if (copyCc.enclosingClass == null) {
+ continue;
+ }
+ CompiledClass newRef = enclosingClassMap.get(copyCc.enclosingClass);
+ if (null == newRef) {
+ throw new InternalCompilerException("Enclosing type not found for " + copyCc.sourceName);
+ }
+ copyCc.enclosingClass = newRef;
+ }
+
+ return Collections.unmodifiableCollection(copy);
+ }
/**
* A token to retrieve this object's bytes from the disk cache. byte code is
* placed in the cache when the object is deserialized.
*/
private final DiskCacheToken classBytesToken;
+ private CompiledClass enclosingClass;
+ private final String internalName;
+ private final boolean isLocal;
private transient NameEnvironmentAnswer nameEnvironmentAnswer;
+ private String signatureHash;
+
+ private final String sourceName;
+ private transient TypeData typeData;
+
+ private CompilationUnit unit;
/**
* Create a compiled class from raw class bytes.
@@ -70,6 +109,20 @@
}
/**
+ * Used for cloning all compiled classes in one compilation unit.
+ */
+ private CompiledClass(CompiledClass orig, CompilationUnit newUnit) {
+ this.enclosingClass = orig.enclosingClass;
+ this.internalName = orig.internalName;
+ this.sourceName = orig.sourceName;
+ this.classBytesToken = orig.classBytesToken;
+ this.isLocal = orig.isLocal;
+ this.typeData = orig.typeData;
+ this.unit = newUnit;
+ this.signatureHash = orig.signatureHash;
+ }
+
+ /**
* Returns the bytes of the compiled class.
*/
public byte[] getBytes() {
@@ -111,7 +164,7 @@
public String getSourceName() {
return sourceName;
}
-
+
public TypeData getTypeData() {
if (typeData == null) {
typeData =
@@ -150,5 +203,4 @@
void initUnit(CompilationUnit unit) {
this.unit = unit;
}
-
}