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); + } + } + } }