Serialize GWT ASTs with CompilationUnits.
When we persist CompilationUnits, persist the GWT AST too.
http://gwt-code-reviews.appspot.com/1384807/
Review by: zundel@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9901 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 a550ca4..cd301ad 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
@@ -15,14 +15,10 @@
*/
package com.google.gwt.dev.javac;
-import com.google.gwt.dev.jjs.ast.JDeclaredType;
-import com.google.gwt.dev.util.collect.Lists;
+import com.google.gwt.dev.util.DiskCacheToken;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.List;
@@ -30,6 +26,7 @@
* This class provides a Convenient way to serialize a {@CompilationUnit}.
*/
public class CachedCompilationUnit extends CompilationUnit {
+ private final DiskCacheToken astToken;
private final Collection<CompiledClass> compiledClasses;
private final ContentId contentId;
private final Dependencies dependencies;
@@ -42,18 +39,17 @@
private final boolean isGenerated;
private final boolean isSuperSource;
private final CategorizedProblem[] problems;
- private transient long sourceToken = -1;
+ private final DiskCacheToken sourceToken;
/**
* Create a compilation unit that can be serialized from another {@link CompilationUnit}.
*
* @param unit A unit to copy
- * @param sourceToken A valid {@DiskCache} token for this unit's source code. If
- * you don't have a valid disk cache token, use another constructor
- * to provide the source code.
+ * @param sourceToken A valid {@DiskCache} token for this unit's source code.
+ * @param astToken A valid {@DiskCache} token for this unit's serialized AST types.
*/
@SuppressWarnings("deprecation")
- CachedCompilationUnit(CompilationUnit unit, long sourceToken) {
+ CachedCompilationUnit(CompilationUnit unit, long sourceToken, long astToken) {
assert unit != null;
this.compiledClasses = unit.getCompiledClasses();
this.contentId = unit.getContentId();
@@ -75,8 +71,13 @@
this.problems[i] = new SerializableCategorizedProblem(problemsIn[i]);
}
}
- assert sourceToken >= 0;
- this.sourceToken = sourceToken;
+ this.astToken = new DiskCacheToken(astToken);
+ this.sourceToken = new DiskCacheToken(sourceToken);
+ }
+
+ @Override
+ public Collection<CompiledClass> getCompiledClasses() {
+ return compiledClasses;
}
@Override
@@ -102,7 +103,7 @@
@Override
@Deprecated
public String getSource() {
- return diskCache.readString(sourceToken);
+ return sourceToken.readString();
}
@Override
@@ -111,9 +112,8 @@
}
@Override
- public List<JDeclaredType> getTypes() {
- // TODO(scottb): implement.
- return Lists.create();
+ public byte[] getTypesSerialized() {
+ return astToken.readByteArray();
}
@Override
@@ -139,11 +139,6 @@
}
@Override
- Collection<CompiledClass> getCompiledClasses() {
- return compiledClasses;
- }
-
- @Override
ContentId getContentId() {
return contentId;
}
@@ -157,15 +152,4 @@
CategorizedProblem[] getProblems() {
return problems;
}
-
- private void readObject(ObjectInputStream inputStream)
- throws ClassNotFoundException, IOException {
- inputStream.defaultReadObject();
- sourceToken = diskCache.transferFromStream(inputStream);
- }
-
- private void writeObject(ObjectOutputStream outputStream) throws IOException {
- outputStream.defaultWriteObject();
- diskCache.transferToStream(sourceToken, outputStream);
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index 2c41e77..0108c5a 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -51,6 +51,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* Manages a centralized cache for compiled units.
@@ -120,11 +121,14 @@
}
}
- CompilationUnit unit =
- builder.build(compiledClasses, types, dependencies, jsniMethods.values(), methodArgs,
+ for (CompiledClass cc : compiledClasses) {
+ allValidClasses.put(cc.getSourceName(), cc);
+ }
+
+ builder.setClasses(compiledClasses).setTypes(types).setDependencies(dependencies)
+ .setJsniMethods(jsniMethods.values()).setMethodArgs(methodArgs).setProblems(
cud.compilationResult().getProblems());
- addValidUnit(unit);
- newlyBuiltUnits.add(unit);
+ buildQueue.add(builder);
} finally {
event.end();
}
@@ -140,6 +144,8 @@
private final GwtAstBuilder astBuilder = new GwtAstBuilder();
+ private transient LinkedBlockingQueue<CompilationUnitBuilder> buildQueue;
+
/**
* The JDT compiler.
*/
@@ -151,8 +157,6 @@
private final JSORestrictionsChecker.CheckerState jsoState =
new JSORestrictionsChecker.CheckerState();
- private transient Collection<CompilationUnit> newlyBuiltUnits;
-
public CompileMoreLater(AdditionalTypeProviderDelegate delegate) {
compiler.setAdditionalTypeProviderDelegate(delegate);
}
@@ -174,8 +178,7 @@
void addValidUnit(CompilationUnit unit) {
compiler.addCompiledUnit(unit);
for (CompiledClass cc : unit.getCompiledClasses()) {
- String sourceName = cc.getSourceName();
- allValidClasses.put(sourceName, cc);
+ allValidClasses.put(cc.getSourceName(), cc);
}
}
@@ -194,20 +197,54 @@
ArrayList<CompilationUnit> resultUnits = new ArrayList<CompilationUnit>();
do {
// Compile anything that needs to be compiled.
- this.newlyBuiltUnits = new ArrayList<CompilationUnit>();
-
+ buildQueue = new LinkedBlockingQueue<CompilationUnitBuilder>();
+ final ArrayList<CompilationUnit> newlyBuiltUnits = new ArrayList<CompilationUnit>();
+ final CompilationUnitBuilder sentinel = CompilationUnitBuilder.create((GeneratedUnit) null);
+ final Throwable[] workerException = new Throwable[1];
+ Thread buildThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ do {
+ CompilationUnitBuilder builder = buildQueue.take();
+ if (builder == sentinel) {
+ return;
+ }
+ // Expensive, must serialize GWT AST types to bytes.
+ CompilationUnit unit = builder.build();
+ newlyBuiltUnits.add(unit);
+ } while (true);
+ } catch (Throwable e) {
+ workerException[0] = e;
+ }
+ }
+ };
+ buildThread.setName("CompilationUnitBuilder");
+ buildThread.start();
Event jdtCompilerEvent = SpeedTracerLogger.start(eventType);
try {
compiler.doCompile(builders);
} finally {
jdtCompilerEvent.end();
}
-
- resultUnits.addAll(this.newlyBuiltUnits);
+ buildQueue.add(sentinel);
+ try {
+ buildThread.join();
+ if (workerException[0] != null) {
+ throw workerException[0];
+ }
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new RuntimeException("Exception processing units", e);
+ } finally {
+ buildQueue = null;
+ }
+ resultUnits.addAll(newlyBuiltUnits);
builders.clear();
// Resolve all newly built unit deps against the global classes.
- for (CompilationUnit unit : this.newlyBuiltUnits) {
+ for (CompilationUnit unit : newlyBuiltUnits) {
unit.getDependencies().resolve(allValidClasses);
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
index 9283541..9bf6bcd 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -20,13 +20,16 @@
import com.google.gwt.dev.asm.Opcodes;
import com.google.gwt.dev.asm.commons.EmptyVisitor;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.util.DiskCache;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.collect.HashMap;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.URL;
import java.net.URLConnection;
@@ -255,6 +258,11 @@
return anonymousClassMap;
}
+ /**
+ * Returns all contained classes.
+ */
+ public abstract Collection<CompiledClass> getCompiledClasses();
+
public abstract List<JsniMethod> getJsniMethods();
/**
@@ -289,7 +297,25 @@
/**
* Returns the GWT AST types in this unit.
*/
- public abstract List<JDeclaredType> getTypes();
+ public List<JDeclaredType> getTypes() {
+ try {
+ byte[] bytes = getTypesSerialized();
+ ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
+ bytes));
+ return JProgram.deserializeTypes(ois);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IOException on in-memory stream",
+ e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unexpected error deserializing AST for '" + getTypeName() + "'",
+ e);
+ }
+ }
+
+ /**
+ * Returns the GWT AST types in this unit in serialized form.
+ */
+ public abstract byte[] getTypesSerialized();
@Deprecated
public final boolean hasAnonymousClasses() {
@@ -343,11 +369,6 @@
protected abstract Object writeReplace();
/**
- * Returns all contained classes.
- */
- abstract Collection<CompiledClass> getCompiledClasses();
-
- /**
* Returns the content ID for the source with which this unit was compiled.
*/
abstract ContentId getContentId();
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
index 7292c05..e8e965c 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
@@ -209,7 +209,7 @@
protected Object writeReplace() {
long sourceToken = generatedUnit.getSourceToken();
assert sourceToken >= 0;
- return new CachedCompilationUnit(this, sourceToken);
+ return new CachedCompilationUnit(this, sourceToken, astToken);
}
@Override
@@ -239,20 +239,30 @@
+ Shared.toPath(generatedUnit.getTypeName());
}
+ private List<CompiledClass> compiledClasses;
+ private Dependencies dependencies;
+ private Collection<? extends JsniMethod> jsniMethods;
+ private MethodArgNamesLookup methodArgs;
+ private CategorizedProblem[] problems;
+
/**
* Caches source until JSNI methods can be collected.
*/
private transient String source;
+ private List<JDeclaredType> types;
+
protected CompilationUnitBuilder() {
}
- public CompilationUnit build(List<CompiledClass> compiledClasses,
- List<JDeclaredType> types, Dependencies dependencies,
- Collection<? extends JsniMethod> jsniMethods,
- MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
+ public CompilationUnit build() {
// Free the source now.
source = null;
+ assert compiledClasses != null;
+ assert types != null;
+ assert dependencies != null;
+ assert jsniMethods != null;
+ assert methodArgs != null;
return makeUnit(compiledClasses, types, dependencies, jsniMethods,
methodArgs, problems);
}
@@ -270,6 +280,48 @@
public abstract String getTypeName();
+ public CompilationUnitBuilder setClasses(List<CompiledClass> compiledClasses) {
+ this.compiledClasses = compiledClasses;
+ return this;
+ }
+
+ public CompilationUnitBuilder setCompiledClasses(
+ List<CompiledClass> compiledClasses) {
+ this.compiledClasses = compiledClasses;
+ return this;
+ }
+
+ public CompilationUnitBuilder setDependencies(Dependencies dependencies) {
+ this.dependencies = dependencies;
+ return this;
+ }
+
+ public CompilationUnitBuilder setJsniMethods(
+ Collection<? extends JsniMethod> jsniMethods) {
+ this.jsniMethods = jsniMethods;
+ return this;
+ }
+
+ public CompilationUnitBuilder setMethodArgs(MethodArgNamesLookup methodArgs) {
+ this.methodArgs = methodArgs;
+ return this;
+ }
+
+ public CompilationUnitBuilder setProblems(CategorizedProblem[] problems) {
+ this.problems = problems;
+ return this;
+ }
+
+ public CompilationUnitBuilder setSource(String source) {
+ this.source = source;
+ return this;
+ }
+
+ public CompilationUnitBuilder setTypes(List<JDeclaredType> types) {
+ this.types = types;
+ return this;
+ }
+
@Override
public final String toString() {
return getLocation();
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
index a16776e..0722dec 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
@@ -16,18 +16,26 @@
package com.google.gwt.dev.javac;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.util.collect.Lists;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.List;
abstract class CompilationUnitImpl extends CompilationUnit {
+ /**
+ * Handle to serialized GWT AST.
+ */
+ protected transient long astToken;
+
private final Dependencies dependencies;
private final List<CompiledClass> exposedCompiledClasses;
- private final List<JDeclaredType> exposedTypes;
private final boolean hasErrors;
private final List<JsniMethod> jsniMethods;
private final MethodArgNamesLookup methodArgs;
@@ -38,7 +46,6 @@
Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
this.exposedCompiledClasses = Lists.normalizeUnmodifiable(compiledClasses);
- this.exposedTypes = Lists.normalizeUnmodifiable(types);
this.dependencies = dependencies;
this.jsniMethods = Lists.create(jsniMethods.toArray(new JsniMethod[jsniMethods.size()]));
this.methodArgs = methodArgs;
@@ -55,6 +62,21 @@
for (CompiledClass cc : compiledClasses) {
cc.initUnit(this);
}
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ JProgram.serializeTypes(types, out);
+ out.close();
+ astToken = diskCache.writeByteArray(baos.toByteArray());
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IOException on in-memory stream",
+ e);
+ }
+ }
+
+ @Override
+ public Collection<CompiledClass> getCompiledClasses() {
+ return exposedCompiledClasses;
}
@Override
@@ -68,8 +90,8 @@
}
@Override
- public List<JDeclaredType> getTypes() {
- return exposedTypes;
+ public byte[] getTypesSerialized() {
+ return diskCache.readByteArray(astToken);
}
@Override
@@ -77,14 +99,6 @@
return hasErrors;
}
- /**
- * Returns all contained classes.
- */
- @Override
- Collection<CompiledClass> getCompiledClasses() {
- return exposedCompiledClasses;
- }
-
@Override
Dependencies getDependencies() {
return dependencies;
@@ -94,4 +108,4 @@
CategorizedProblem[] getProblems() {
return problems;
}
-}
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index f7e6ab9..5342f9e 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -125,11 +125,11 @@
public void process(CompilationUnitBuilder builder,
CompilationUnitDeclaration cud, List<CompiledClass> compiledClasses) {
- CompilationUnit unit = builder.build(compiledClasses,
- Collections.<JDeclaredType> emptyList(), new Dependencies(),
- Collections.<JsniMethod> emptyList(), new MethodArgNamesLookup(),
- cud.compilationResult().getProblems());
- results.add(unit);
+ builder.setClasses(compiledClasses).setTypes(Collections.<JDeclaredType> emptyList())
+ .setDependencies(new Dependencies()).setJsniMethods(Collections.<JsniMethod> emptyList())
+ .setMethodArgs(new MethodArgNamesLookup()).setProblems(
+ cud.compilationResult().getProblems());
+ results.add(builder.build());
}
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
index 5502891..e7a7833 100644
--- a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
@@ -101,7 +101,7 @@
if (sourceToken < 0) {
sourceToken = diskCache.transferFromStream(sourceFile.openContents());
}
- return new CachedCompilationUnit(this, sourceToken);
+ return new CachedCompilationUnit(this, sourceToken, astToken);
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
index 8c8637d..b044ebb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
@@ -16,12 +16,30 @@
package com.google.gwt.dev.jjs.ast;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+
+import java.io.Serializable;
/**
* Java class type reference expression.
*/
public class JClassType extends JDeclaredType implements CanBeSetFinal {
+ private static class ExternalSerializedForm implements Serializable {
+ private final String name;
+
+ public ExternalSerializedForm(JClassType classType) {
+ name = classType.getName();
+ }
+
+ private Object readResolve() {
+ JClassType result = new JClassType(SourceOrigin.UNKNOWN, name, false,
+ false);
+ result.setExternal(true);
+ return result;
+ }
+ }
+
private final boolean isAbstract;
private boolean isFinal;
private JClassType superClass;
@@ -81,4 +99,13 @@
}
visitor.endVisit(this, ctx);
}
+
+ @Override
+ protected Object writeReplace() {
+ if (isExternal()) {
+ return new ExternalSerializedForm(this);
+ } else {
+ return this;
+ }
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java
index 20bbbf8..011f582 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java
@@ -16,7 +16,9 @@
package com.google.gwt.dev.jjs.ast;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+import java.io.Serializable;
import java.util.List;
/**
@@ -24,6 +26,24 @@
*/
public class JConstructor extends JMethod {
+ private static class ExternalSerializedForm implements Serializable {
+
+ private final JClassType enclosingType;
+ private final String signature;
+
+ public ExternalSerializedForm(JConstructor ctor) {
+ enclosingType = ctor.getEnclosingType();
+ signature = ctor.getSignature();
+ }
+
+ private Object readResolve() {
+ JConstructor result = new JConstructor(SourceOrigin.UNKNOWN,
+ enclosingType);
+ result.signature = signature;
+ return result;
+ }
+ }
+
/**
* Caches whether or not this constructor does any work. Once true, we never
* have to recheck, but we keep rechecking as long as it's false.
@@ -107,4 +127,12 @@
traceAfter(visitor, before);
}
+ protected Object writeReplace() {
+ if (getEnclosingType() != null && getEnclosingType().isExternal()) {
+ return new ExternalSerializedForm(this);
+ } else {
+ return this;
+ }
+ }
+
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index eeb174b..40a01f8 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -287,6 +287,12 @@
}
/**
+ * Subclasses must replace themselves with a shallow reference when
+ * {@link #isExternal()} is <code>true</code>.
+ */
+ protected abstract Object writeReplace();
+
+ /**
* Clears all existing implemented interfaces.
*/
void clearImplements() {
@@ -308,7 +314,7 @@
}
/**
- * See {@link #writeMethodBodies(ObjectOutputStream).
+ * See {@link #writeMethodBodies(ObjectOutputStream)}.
*
* @see #writeMethodBodies(ObjectOutputStream)
*/
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
index b0ff2fb..bf561e4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
@@ -16,6 +16,9 @@
package com.google.gwt.dev.jjs.ast;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+
+import java.io.Serializable;
/**
* Java field definition.
@@ -44,11 +47,31 @@
}
}
+ private static class ExternalSerializedForm implements Serializable {
+
+ private final JDeclaredType enclosingType;
+ private final String signature;
+
+ public ExternalSerializedForm(JField field) {
+ enclosingType = field.getEnclosingType();
+ signature = field.getSignature();
+ }
+
+ private Object readResolve() {
+ String name = signature.substring(0, signature.indexOf(':'));
+ JField result = new JField(SourceOrigin.UNKNOWN, name, enclosingType,
+ JNullType.INSTANCE, false, Disposition.NONE);
+ result.signature = signature;
+ return result;
+ }
+ }
+
private final JDeclaredType enclosingType;
private final boolean isCompileTimeConstant;
private final boolean isStatic;
private boolean isThisRef;
private boolean isVolatile;
+ private transient String signature;
public JField(SourceInfo info, String name, JDeclaredType enclosingType, JType type,
boolean isStatic, Disposition disposition) {
@@ -73,6 +96,17 @@
return null;
}
+ public String getSignature() {
+ if (signature == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getName());
+ sb.append(':');
+ sb.append(getType().getJsniSignatureName());
+ signature = sb.toString();
+ }
+ return signature;
+ }
+
public boolean isCompileTimeConstant() {
return isCompileTimeConstant;
}
@@ -116,4 +150,12 @@
visitor.endVisit(this, ctx);
}
+ protected Object writeReplace() {
+ if (enclosingType != null && enclosingType.isExternal()) {
+ return new ExternalSerializedForm(this);
+ } else {
+ return this;
+ }
+ }
+
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
index bfec2b8..ff30331 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
@@ -16,12 +16,29 @@
package com.google.gwt.dev.jjs.ast;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+
+import java.io.Serializable;
/**
* Java interface type definition.
*/
public class JInterfaceType extends JDeclaredType {
+ private static class ExternalSerializedForm implements Serializable {
+ private final String name;
+
+ public ExternalSerializedForm(JInterfaceType interfaceType) {
+ name = interfaceType.getName();
+ }
+
+ private Object readResolve() {
+ JInterfaceType result = new JInterfaceType(SourceOrigin.UNKNOWN, name);
+ result.setExternal(true);
+ return result;
+ }
+ }
+
public JInterfaceType(SourceInfo info, String name) {
super(info, name);
}
@@ -52,4 +69,13 @@
}
visitor.endVisit(this, ctx);
}
+
+ @Override
+ protected Object writeReplace() {
+ if (isExternal()) {
+ return new ExternalSerializedForm(this);
+ } else {
+ return this;
+ }
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 9d598b1..9bf6869 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -17,12 +17,14 @@
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.util.StringInterner;
import com.google.gwt.dev.util.collect.Lists;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -34,6 +36,25 @@
public class JMethod extends JNode implements HasAnnotations, HasEnclosingType,
HasName, HasType, CanBeAbstract, CanBeSetFinal, CanBeNative, CanBeStatic {
+ private static class ExternalSerializedForm implements Serializable {
+
+ private final JDeclaredType enclosingType;
+ private final String signature;
+
+ public ExternalSerializedForm(JMethod method) {
+ enclosingType = method.getEnclosingType();
+ signature = method.getSignature();
+ }
+
+ private Object readResolve() {
+ String name = signature.substring(0, signature.indexOf('('));
+ JMethod result = new JMethod(SourceOrigin.UNKNOWN, name, enclosingType,
+ null, false, false, false, false);
+ result.signature = signature;
+ return result;
+ }
+ }
+
private static final String TRACE_METHOD_WILDCARD = "*";
private static void trace(String title, String code) {
@@ -43,6 +64,8 @@
System.out.println(code);
}
+ protected transient String signature;
+
private List<JAnnotation> annotations = Lists.create();
/**
@@ -162,9 +185,6 @@
}
public List<JType> getOriginalParamTypes() {
- if (originalParamTypes == null) {
- return null;
- }
return originalParamTypes;
}
@@ -186,6 +206,21 @@
return params;
}
+ public String getSignature() {
+ if (signature == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getName());
+ sb.append('(');
+ for (JType type : getOriginalParamTypes()) {
+ sb.append(type.getJsniSignatureName());
+ }
+ sb.append(')');
+ sb.append(getOriginalReturnType().getJsniSignatureName());
+ signature = sb.toString();
+ }
+ return signature;
+ }
+
public List<JClassType> getThrownExceptions() {
return thrownExceptions;
}
@@ -330,6 +365,14 @@
}
}
+ protected Object writeReplace() {
+ if (enclosingType != null && enclosingType.isExternal()) {
+ return new ExternalSerializedForm(this);
+ } else {
+ return this;
+ }
+ }
+
/**
* See {@link #writeBody(ObjectOutputStream)}.
*
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index af5e68e..e2ab10b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -202,6 +202,19 @@
return x;
}
+ public static List<JDeclaredType> deserializeTypes(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+ @SuppressWarnings("unchecked")
+ List<JDeclaredType> types = (List<JDeclaredType>) stream.readObject();
+ for (JDeclaredType type : types) {
+ type.readMembers(stream);
+ }
+ for (JDeclaredType type : types) {
+ type.readMethodBodies(stream);
+ }
+ return types;
+ }
+
public static String getJsniSig(JMethod method) {
return getJsniSig(method, true);
}
@@ -256,6 +269,17 @@
return latest;
}
+ public static void serializeTypes(List<JDeclaredType> types,
+ ObjectOutputStream stream) throws IOException {
+ stream.writeObject(types);
+ for (JDeclaredType type : types) {
+ type.writeMembers(stream);
+ }
+ for (JDeclaredType type : types) {
+ type.writeMethodBodies(stream);
+ }
+ }
+
/**
* The main logic behind {@link #lastFragmentLoadingBefore(int, int...)} and
* {@link #lastFragmentLoadingBefore(List, int, int, int...)}.
@@ -1153,16 +1177,9 @@
*
* @see #writeObject(ObjectOutputStream)
*/
- @SuppressWarnings("unchecked")
private void readObject(ObjectInputStream stream) throws IOException,
ClassNotFoundException {
- allTypes = (List<JDeclaredType>) stream.readObject();
- for (JDeclaredType type : allTypes) {
- type.readMembers(stream);
- }
- for (JDeclaredType type : allTypes) {
- type.readMethodBodies(stream);
- }
+ allTypes = deserializeTypes(stream);
stream.defaultReadObject();
}
@@ -1188,13 +1205,7 @@
* recursion chains would result.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
- stream.writeObject(allTypes);
- for (JDeclaredType type : allTypes) {
- type.writeMembers(stream);
- }
- for (JDeclaredType type : allTypes) {
- type.writeMethodBodies(stream);
- }
+ serializeTypes(allTypes, stream);
stream.defaultWriteObject();
}
}
diff --git a/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java b/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java
index 045d628..36952ce 100644
--- a/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java
+++ b/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java
@@ -39,6 +39,7 @@
* Create a wrapper for a token associated with the given diskCache.
*/
DiskCacheToken(DiskCache diskCache, long token) {
+ assert token >= 0;
this.diskCache = diskCache;
this.token = token;
}
diff --git a/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java b/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
index 3699af2..a4643ad 100644
--- a/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
+++ b/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
@@ -15,8 +15,6 @@
*/
package com.google.gwt.dev.javac;
-import com.google.gwt.dev.jjs.ast.JDeclaredType;
-
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import java.util.Collection;
@@ -30,9 +28,9 @@
private static final AtomicInteger nextTimestamp = new AtomicInteger(1);
private final ContentId contentId;
- private final String typeName;
- private final String source;
private final long lastModified;
+ private final String source;
+ private final String typeName;
public MockCompilationUnit(String typeName, String source) {
this.typeName = typeName;
@@ -42,6 +40,11 @@
}
@Override
+ public Collection<CompiledClass> getCompiledClasses() {
+ return null;
+ }
+
+ @Override
public List<JsniMethod> getJsniMethods() {
return null;
}
@@ -72,7 +75,7 @@
}
@Override
- public List<JDeclaredType> getTypes() {
+ public byte[] getTypesSerialized() {
return null;
}
@@ -96,11 +99,6 @@
}
@Override
- Collection<CompiledClass> getCompiledClasses() {
- return null;
- }
-
- @Override
ContentId getContentId() {
return contentId;
}
diff --git a/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java b/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java
index 6cc32c9..906c161 100644
--- a/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java
+++ b/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java
@@ -28,33 +28,63 @@
import junit.framework.TestCase;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
- * Massive test for {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder}, uses
- * CompilerSuite under the hood to test source compatibility between
- * {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder} and
- * {@link com.google.gwt.dev.jjs.impl.GenerateJavaAST}.
+ * Massive test for {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder}.
*/
public class GwtAstBuilderTest extends TestCase {
+ /*
+ * Reuse the module and compilation state between tests, because it takes a
+ * long time to build them. This is fine as long as we don't mutate them.
+ */
- private static TreeLogger createLogger() {
- PrintWriterTreeLogger logger = new PrintWriterTreeLogger(new PrintWriter(System.err, true));
- logger.setMaxDetail(TreeLogger.ERROR);
+ private static CompilationState compilationState;
+ private static PrintWriterTreeLogger logger;
+ private static ModuleDef module;
+
+ private static synchronized CompilationState getCompilationState()
+ throws UnableToCompleteException {
+ if (compilationState == null) {
+ compilationState = CompileModule.buildGwtAst(getLogger(), getTestModule());
+ }
+ return compilationState;
+ }
+
+ private static synchronized TreeLogger getLogger() {
+ if (logger == null) {
+ logger = new PrintWriterTreeLogger(new PrintWriter(System.err, true));
+ logger.setMaxDetail(TreeLogger.ERROR);
+ }
return logger;
}
+ private static synchronized ModuleDef getTestModule() throws UnableToCompleteException {
+ if (module == null) {
+ module =
+ ModuleDefLoader.createSyntheticModule(getLogger(),
+ "com.google.gwt.dev.jjs.CompilerSuite.GwtAstBuilderTest", new String[]{
+ "com.google.gwt.junit.JUnit", "com.google.gwt.dev.jjs.CompilerSuite"}, false);
+ }
+ return module;
+ }
+
+ /**
+ * Tests source compatibility between
+ * {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder} and
+ * {@link com.google.gwt.dev.jjs.impl.GenerateJavaAST}.
+ */
public void testGwtAstBuilder() throws UnableToCompleteException {
- TreeLogger logger = createLogger();
- ModuleDef module =
- ModuleDefLoader.createSyntheticModule(logger,
- "com.google.gwt.dev.jjs.CompilerSuite.GwtAstBuilderTest", new String[]{
- "com.google.gwt.junit.JUnit", "com.google.gwt.dev.jjs.CompilerSuite"}, false);
- CompilationState compilationState = CompileModule.buildGwtAst(logger, module);
+ CompilationState compilationState = getCompilationState();
assertFalse(compilationState.hasErrors());
- JProgram jprogram = CompileModule.buildGenerateJavaAst(logger, module, compilationState);
+ JProgram jprogram =
+ CompileModule.buildGenerateJavaAst(getLogger(), getTestModule(), compilationState);
Map<String, JDeclaredType> compStateTypes = new HashMap<String, JDeclaredType>();
for (CompilationUnit unit : compilationState.getCompilationUnits()) {
@@ -88,4 +118,32 @@
assertEquals("Mismatched output for '" + typeName + "'", oldSource, newSource);
}
}
+
+ /**
+ * Test that serialization doesn't crash and produces the same source tree.
+ */
+ public void testSerialization() throws UnableToCompleteException, IOException,
+ ClassNotFoundException {
+ CompilationState compilationState = getCompilationState();
+ assertFalse(compilationState.hasErrors());
+ for (CompilationUnit unit : compilationState.getCompilationUnits()) {
+ Map<String, JDeclaredType> compStateTypes = new HashMap<String, JDeclaredType>();
+ for (JDeclaredType type : unit.getTypes()) {
+ compStateTypes.put(type.getName(), type);
+ }
+ byte[] bytes = unit.getTypesSerialized();
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ List<JDeclaredType> deserializedTypes = JProgram.deserializeTypes(ois);
+ assertEquals(compStateTypes.size(), deserializedTypes.size());
+ for (JDeclaredType deserializedType : deserializedTypes) {
+ String typeName = deserializedType.getName();
+ JDeclaredType compStateType = compStateTypes.get(typeName);
+ assertNotNull("No matching prebuilt type for '" + typeName + "'", compStateType);
+ String oldSource = compStateType.toSource();
+ String newSource = deserializedType.toSource();
+ assertEquals("Mismatched output for '" + typeName + "'", oldSource, newSource);
+ }
+ }
+ }
}