Revision: 9901
Author:   sco...@google.com
Date:     Fri Mar 25 16:48:01 2011
Log:      Serialize GWT ASTs with CompilationUnits.

When we persist CompilationUnits, persist the GWT AST too.

http://gwt-code-reviews.appspot.com/1384807/

Review by: zun...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9901

Modified:
 /trunk/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
 /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
 /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
 /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
 /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
 /trunk/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
 /trunk/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
 /trunk/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java
 /trunk/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
 /trunk/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java

=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java Thu Mar 24 15:47:57 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java Fri Mar 25 16:48:01 2011
@@ -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
@@ -137,11 +137,6 @@
   protected Object writeReplace() {
     return this;
   }
-
-  @Override
-  Collection<CompiledClass> getCompiledClasses() {
-    return compiledClasses;
-  }

   @Override
   ContentId getContentId() {
@@ -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);
-  }
-}
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java Fri Mar 25 11:27:04 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java Fri Mar 25 16:48:01 2011
@@ -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);
         }

=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java Thu Mar 24 15:47:57 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java Fri Mar 25 16:48:01 2011
@@ -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;
@@ -254,6 +257,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() {
@@ -342,11 +368,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.
    */
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java Thu Mar 24 15:47:57 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java Fri Mar 25 16:48:01 2011
@@ -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
@@ -238,21 +238,31 @@
     return "generated://" + generatedUnit.getStrongHash() + "/"
         + 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();
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java Fri Mar 11 09:50:44 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java Fri Mar 25 16:48:01 2011
@@ -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,22 +90,14 @@
   }

   @Override
-  public List<JDeclaredType> getTypes() {
-    return exposedTypes;
+  public byte[] getTypesSerialized() {
+    return diskCache.readByteArray(astToken);
   }

   @Override
   public boolean isError() {
     return hasErrors;
   }
-
-  /**
-   * Returns all contained classes.
-   */
-  @Override
-  Collection<CompiledClass> getCompiledClasses() {
-    return exposedCompiledClasses;
-  }

   @Override
   Dependencies getDependencies() {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java Fri Mar 11 09:50:44 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java Fri Mar 25 16:48:01 2011
@@ -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());
     }
   }
   /**
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java Thu Mar 24 15:47:57 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java Fri Mar 25 16:48:01 2011
@@ -101,7 +101,7 @@
     if (sourceToken < 0) {
sourceToken = diskCache.transferFromStream(sourceFile.openContents());
     }
-    return new CachedCompilationUnit(this, sourceToken);
+    return new CachedCompilationUnit(this, sourceToken, astToken);
   }

   @Override
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java Sat Jan 22 17:19:44 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java Fri Mar 25 16:48:01 2011
@@ -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;
+    }
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java Wed Jan 26 16:52:13 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java Fri Mar 25 16:48:01 2011
@@ -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.
@@ -106,5 +126,13 @@
     visitor.endVisit(this, ctx);
     traceAfter(visitor, before);
   }
+
+  protected Object writeReplace() {
+    if (getEnclosingType() != null && getEnclosingType().isExternal()) {
+      return new ExternalSerializedForm(this);
+    } else {
+      return this;
+    }
+  }

 }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java Fri Mar 11 09:50:44 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java Fri Mar 25 16:48:01 2011
@@ -285,6 +285,12 @@
     Arrays.sort(a, 1, a.length, sort);
     methods = Lists.create(a);
   }
+
+  /**
+   * Subclasses must replace themselves with a shallow reference when
+   * {@link #isExternal()} is <code>true</code>.
+   */
+  protected abstract Object writeReplace();

   /**
    * Clears all existing implemented interfaces.
@@ -308,7 +314,7 @@
   }

   /**
-   * See {@link #writeMethodBodies(ObjectOutputStream).
+   * See {@link #writeMethodBodies(ObjectOutputStream)}.
    *
    * @see #writeMethodBodies(ObjectOutputStream)
    */
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java Wed Jan 26 16:52:13 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java Fri Mar 25 16:48:01 2011
@@ -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.
@@ -43,12 +46,32 @@
       return this == VOLATILE;
     }
   }
+
+  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) {
@@ -72,6 +95,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;
@@ -115,5 +149,13 @@
     }
     visitor.endVisit(this, ctx);
   }
+
+  protected Object writeReplace() {
+    if (enclosingType != null && enclosingType.isExternal()) {
+      return new ExternalSerializedForm(this);
+    } else {
+      return this;
+    }
+  }

 }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java Wed Jan 26 16:52:13 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java Fri Mar 25 16:48:01 2011
@@ -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;
+    }
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java Thu Sep 23 06:33:21 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java Fri Mar 25 16:48:01 2011
@@ -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) {
@@ -42,6 +63,8 @@
     System.out.println("---------------------------");
     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;
   }

@@ -185,6 +205,21 @@
   public List<JParameter> getParams() {
     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;
@@ -329,6 +364,14 @@
       body = (JAbstractMethodBody) visitor.accept(body);
     }
   }
+
+  protected Object writeReplace() {
+    if (enclosingType != null && enclosingType.isExternal()) {
+      return new ExternalSerializedForm(this);
+    } else {
+      return this;
+    }
+  }

   /**
    * See {@link #writeBody(ObjectOutputStream)}.
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Thu Mar 3 14:34:14 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Fri Mar 25 16:48:01 2011
@@ -201,6 +201,19 @@
     enclosingMethod.addParam(x);
     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);
@@ -255,6 +268,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
@@ -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();
   }
 }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java Fri Mar 25 16:15:23 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/util/DiskCacheToken.java Fri Mar 25 16:48:01 2011
@@ -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;
   }
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java Thu Mar 24 15:47:57 2011 +++ /trunk/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java Fri Mar 25 16:48:01 2011
@@ -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;
@@ -40,6 +38,11 @@
     contentId = new ContentId(typeName, source);
     lastModified = nextTimestamp.getAndIncrement();
   }
+
+  @Override
+  public Collection<CompiledClass> getCompiledClasses() {
+    return null;
+  }

   @Override
   public List<JsniMethod> getJsniMethods() {
@@ -72,7 +75,7 @@
   }

   @Override
-  public List<JDeclaredType> getTypes() {
+  public byte[] getTypesSerialized() {
     return null;
   }

@@ -94,11 +97,6 @@
   protected Object writeReplace() {
     return this;
   }
-
-  @Override
-  Collection<CompiledClass> getCompiledClasses() {
-    return null;
-  }

   @Override
   ContentId getContentId() {
=======================================
--- /trunk/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java Fri Mar 11 09:50:44 2011 +++ /trunk/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java Fri Mar 25 16:48:01 2011
@@ -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 {
-
-  private static TreeLogger createLogger() {
- PrintWriterTreeLogger logger = new PrintWriterTreeLogger(new PrintWriter(System.err, true));
-    logger.setMaxDetail(TreeLogger.ERROR);
+  /*
+ * 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 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);
+      }
+    }
+  }
+}

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to