COMPILER SHARDING: Make generated artifacts serializable so that they can be passed across processes from Precompile to Link.

Review by: bobv


git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@3741 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java b/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
index f5fe286..a54eaea 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
@@ -17,6 +17,8 @@
 
 import com.google.gwt.core.ext.Linker;
 
+import java.io.Serializable;
+
 /**
  * A base type for all artifacts relating to the link process. In order to
  * ensure stable output between runs of the compiler, Artifact types must
@@ -27,8 +29,9 @@
  *          to.
  */
 public abstract class Artifact<C extends Artifact<C>> implements
-    Comparable<Artifact<?>> {
-  private final Class<? extends Linker> linker;
+    Comparable<Artifact<?>>, Serializable {
+  private final String linkerName;
+  private transient Class<? extends Linker> linker;
 
   /**
    * Constructor.
@@ -37,6 +40,7 @@
    */
   protected Artifact(Class<? extends Linker> linker) {
     assert linker != null;
+    this.linkerName = linker.getName();
     this.linker = linker;
   }
 
@@ -65,6 +69,17 @@
    * Returns the Linker that created the Artifact.
    */
   public final Class<? extends Linker> getLinker() {
+    // linker is null when deserialized.
+    if (linker == null) {
+      try {
+        Class<?> clazz = Class.forName(linkerName, false,
+            Thread.currentThread().getContextClassLoader());
+        linker = clazz.asSubclass(Linker.class);
+      } catch (ClassNotFoundException e) {
+        // The class may not be available.
+        linker = Linker.class;
+      }
+    }
     return linker;
   }
 
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java b/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
index c04ad6d..96435bc 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.core.ext.linker;
 
+import java.io.Serializable;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -25,7 +26,7 @@
 /**
  * Provides stable ordering and de-duplication of artifacts.
  */
-public final class ArtifactSet implements SortedSet<Artifact<?>> {
+public final class ArtifactSet implements SortedSet<Artifact<?>>, Serializable {
 
   private SortedSet<Artifact<?>> treeSet = new TreeSet<Artifact<?>>();
 
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java b/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java
index 28c9400..3bb8000 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java
@@ -24,11 +24,13 @@
  * during the compilation process.
  */
 public abstract class GeneratedResource extends EmittedArtifact {
-  private final Class<? extends Generator> generatorType;
+  private final String generatorTypeName;
+  private transient Class<? extends Generator> generatorType;
 
   protected GeneratedResource(Class<? extends Linker> linkerType,
       Class<? extends Generator> generatorType, String partialPath) {
     super(linkerType, partialPath);
+    this.generatorTypeName = generatorType.getName();
     this.generatorType = generatorType;
   }
 
@@ -36,6 +38,17 @@
    * The type of Generator that created the resource.
    */
   public final Class<? extends Generator> getGenerator() {
+    // generatorType is null when deserialized.
+    if (generatorType == null) {
+      try {
+        Class<?> clazz = Class.forName(generatorTypeName, false,
+            Thread.currentThread().getContextClassLoader());
+        generatorType = clazz.asSubclass(Generator.class);
+      } catch (ClassNotFoundException e) {
+        // The class may not be available.
+        generatorType = Generator.class;
+      }
+    }
     return generatorType;
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
index 9a99727..89b4492 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.util.Util;
 
 import java.io.File;
+import java.io.Serializable;
 import java.lang.ref.SoftReference;
 import java.util.Collections;
 import java.util.Comparator;
@@ -37,11 +38,8 @@
  */
 public class StandardCompilationResult extends CompilationResult {
 
-  /**
-   * Smaller maps come before larger maps, then we compare the concatenation of
-   * every value.
-   */
-  public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new Comparator<SortedMap<SelectionProperty, String>>() {
+  private static final class MapComparator implements
+      Comparator<SortedMap<SelectionProperty, String>>, Serializable {
     public int compare(SortedMap<SelectionProperty, String> arg0,
         SortedMap<SelectionProperty, String> arg1) {
       int diff = arg0.size() - arg1.size();
@@ -64,10 +62,16 @@
 
       return sb0.toString().compareTo(sb1.toString());
     }
-  };
+  }
+
+  /**
+   * Smaller maps come before larger maps, then we compare the concatenation of
+   * every value.
+   */
+  public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new MapComparator();
 
   private final File cacheFile;
-  private SoftReference<String> js;
+  private transient SoftReference<String> js;
   private final SortedSet<SortedMap<SelectionProperty, String>> propertyValues = new TreeSet<SortedMap<SelectionProperty, String>>(
       MAP_COMPARATOR);
   private final String strongName;
@@ -93,7 +97,10 @@
   }
 
   public String getJavaScript() {
-    String toReturn = js.get();
+    String toReturn = null;
+    if (js != null) {
+      toReturn = js.get();
+    }
     if (toReturn == null) {
       toReturn = Util.readFileAsString(cacheFile);
       js = new SoftReference<String>(toReturn);
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java
index 401b1b8..2a1a0bd 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java
@@ -19,13 +19,37 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.linker.PublicResource;
 import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.util.Util;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.Serializable;
 
 /**
  * The standard implementation of {@link PublicResource}.
  */
 public class StandardPublicResource extends PublicResource {
+
+  /**
+   * Serializes a public resource via a snapshot of the content.
+   */
+  private static final class SerializedPublicResource extends PublicResource {
+    private final byte[] data;
+
+    protected SerializedPublicResource(String partialPath, byte[] data) {
+      super(StandardLinkerContext.class, partialPath);
+      this.data = data;
+    }
+
+    @Override
+    public InputStream getContents(TreeLogger logger)
+        throws UnableToCompleteException {
+      return new ByteArrayInputStream(data);
+    }
+  }
+
   private final Resource resource;
 
   public StandardPublicResource(String partialPath, Resource resource) {
@@ -38,4 +62,18 @@
       throws UnableToCompleteException {
     return resource.openContents();
   }
+
+  private Object writeReplace() {
+    if (resource instanceof Serializable) {
+      return this;
+    }
+    // Resource is not serializable, must replace myself.
+    try {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      Util.copy(resource.openContents(), baos);
+      return new SerializedPublicResource(getPartialPath(), baos.toByteArray());
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
index 14972c1..5f71786 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
@@ -40,8 +40,7 @@
       activeValue = null;
     }
     name = p.getName();
-    provider = p.getProvider() == null ? null
-        : p.getProvider().getBody().toSource();
+    provider = p.getProvider() == null ? null : p.getProvider().getBody();
     values = Collections.unmodifiableSortedSet(new TreeSet<String>(
         Arrays.asList(p.getDefinedValues())));
   }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java b/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java
deleted file mode 100644
index b6f0e77..0000000
--- a/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.cfg;
-
-import com.google.gwt.dev.js.JsParser;
-import com.google.gwt.dev.js.JsParserException;
-import com.google.gwt.dev.js.ast.JsBlock;
-import com.google.gwt.dev.js.ast.JsExprStmt;
-import com.google.gwt.dev.js.ast.JsFunction;
-import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsStatement;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.List;
-
-/**
- * A property provider that reports property values specified literally in a
- * host HTML page.
- */
-public class DefaultPropertyProvider extends PropertyProvider {
-
-  /*
-   * TODO: this references 'parent' literally, which could be a problem if you
-   * were to include the selector script in the host page itself rather than in
-   * an iframe.
-   */
-  public DefaultPropertyProvider(ModuleDef module, Property property) {
-    super(module, property);
-    String src = "function () {";
-    src += "return __gwt_getMetaProperty(\"";
-    src += property.getName();
-    src += "\"); }";
-    setBody(parseFunction(src));
-  }
-
-  private JsBlock parseFunction(String jsniSrc) {
-    Throwable caught = null;
-    try {
-      JsProgram jsPgm = new JsProgram();
-      JsParser jsParser = new JsParser();
-      StringReader r = new StringReader(jsniSrc);
-      List<JsStatement> stmts = jsParser.parse(jsPgm.getScope(), r, 1);
-      JsFunction fn = (JsFunction) ((JsExprStmt) stmts.get(0)).getExpression();
-      return fn.getBody();
-    } catch (IOException e) {
-      caught = e;
-    } catch (JsParserException e) {
-      caught = e;
-    }
-    throw new RuntimeException(
-        "Internal error parsing source for default property provider", caught);
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index 405e93c..3d6efb6 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -408,7 +408,11 @@
          * one possible value and no existing provider.
          */
         if (prop.getProvider() == null && prop.getAllowedValues().length > 1) {
-          prop.setProvider(new DefaultPropertyProvider(this, prop));
+          String src = "{";
+          src += "return __gwt_getMetaProperty(\"";
+          src += prop.getName();
+          src += "\"); }";
+          prop.setProvider(new PropertyProvider(src));
         }
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
index b7b9a5d..a0c2e94 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -214,7 +214,6 @@
     }
 
     protected Schema __property_provider_begin(BindingProperty property) {
-      property.setProvider(new PropertyProvider(moduleDef, property));
       return fChild = new PropertyProviderBodySchema();
     }
 
@@ -233,7 +232,7 @@
       int lineNumber = childSchema.getStartLineNumber();
       JsFunction fn = parseJsBlock(lineNumber, script);
 
-      property.getProvider().setBody(fn.getBody());
+      property.setProvider(new PropertyProvider(fn.getBody().toSource()));
     }
 
     protected Schema __public_begin(String path, String includes,
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Property.java b/dev/core/src/com/google/gwt/dev/cfg/Property.java
index ad30a8b..5615672 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Property.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Property.java
@@ -15,10 +15,12 @@
  */
 package com.google.gwt.dev.cfg;
 
+import java.io.Serializable;
+
 /**
  * Represents an abstract module property.
  */
-public abstract class Property implements Comparable<Property> {
+public abstract class Property implements Comparable<Property>, Serializable {
 
   protected final String name;
 
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
index 13c44d6..55f5f17 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,36 +15,20 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.dev.js.ast.JsBlock;
+import java.io.Serializable;
 
 /**
  * Produces a deferred binding property value by executing JavaScript code.
  */
-public class PropertyProvider {
+public class PropertyProvider implements Serializable {
 
-  private JsBlock body;
+  private final String body;
 
-  private final ModuleDef module;
-  private final Property property;
-
-  public PropertyProvider(ModuleDef module, Property property) {
-    this.module = module;
-    this.property = property;
-  }
-
-  public JsBlock getBody() {
-    return body;
-  }
-
-  public ModuleDef getModule() {
-    return module;
-  }
-
-  public Property getProperty() {
-    return property;
-  }
-
-  public void setBody(JsBlock body) {
+  public PropertyProvider(String body) {
     this.body = body;
   }
+
+  public String getBody() {
+    return body;
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
index f501fd2..d0cb379 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
@@ -19,11 +19,13 @@
 import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 
+import java.io.Serializable;
+
 /**
  * An implementation of {@link PropertyOracle} that contains property values,
  * rather than computing them.
  */
-public class StaticPropertyOracle implements PropertyOracle {
+public class StaticPropertyOracle implements PropertyOracle, Serializable {
 
   private final ConfigurationProperty[] configProps;
 
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index 6f3d637..aabd864 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -40,11 +40,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
 import java.io.Reader;
+import java.io.Serializable;
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Array;
@@ -140,6 +143,19 @@
     return toHexString(md5.digest());
   }
 
+  public static void copy(InputStream is, OutputStream os) throws IOException {
+    try {
+      byte[] buf = new byte[8 * 1024];
+      int i;
+      while ((i = is.read(buf)) != -1) {
+        os.write(buf, 0, i);
+      }
+    } finally {
+      Utility.close(is);
+      Utility.close(os);
+    }
+  }
+
   public static boolean copy(TreeLogger logger, File in, File out)
       throws UnableToCompleteException {
     try {
@@ -548,6 +564,43 @@
     return null;
   }
 
+  public static <T extends Serializable> T readFileAsObject(File file,
+      Class<T> type) throws ClassNotFoundException {
+    FileInputStream fileInputStream = null;
+    ObjectInputStream objectInputStream = null;
+    try {
+      fileInputStream = new FileInputStream(file);
+      objectInputStream = new ObjectInputStream(fileInputStream);
+      return type.cast(objectInputStream.readObject());
+    } catch (IOException e) {
+      return null;
+    } finally {
+      Utility.close(objectInputStream);
+      Utility.close(fileInputStream);
+    }
+  }
+
+  public static Serializable[] readFileAsObjects(File file,
+      Class<? extends Serializable>... types) throws ClassNotFoundException {
+    FileInputStream fileInputStream = null;
+    ObjectInputStream objectInputStream = null;
+    try {
+      fileInputStream = new FileInputStream(file);
+      objectInputStream = new ObjectInputStream(fileInputStream);
+      Serializable[] results = new Serializable[types.length];
+      for (int i = 0; i < results.length; ++i) {
+        Object object = objectInputStream.readObject();
+        results[i] = types[i].cast(object);
+      }
+      return results;
+    } catch (IOException e) {
+      return null;
+    } finally {
+      Utility.close(objectInputStream);
+      Utility.close(fileInputStream);
+    }
+  }
+
   public static String readFileAsString(File file) {
     byte[] bytes = readFileAsBytes(file);
     if (bytes != null) {
@@ -974,24 +1027,26 @@
     }
   }
 
-  public static void writeStringAsFile(TreeLogger logger, File file,
-      String string) throws UnableToCompleteException {
+  /**
+   * Serializes an object and writes it to a file.
+   */
+  public static void writeObjectAsFile(TreeLogger logger, File file,
+      Serializable... objects) throws UnableToCompleteException {
     FileOutputStream stream = null;
-    OutputStreamWriter writer = null;
-    BufferedWriter buffered = null;
+    ObjectOutputStream objectStream = null;
     try {
-      stream = new FileOutputStream(file);
-      writer = new OutputStreamWriter(stream, DEFAULT_ENCODING);
-      buffered = new BufferedWriter(writer);
       file.getParentFile().mkdirs();
-      buffered.write(string);
+      stream = new FileOutputStream(file);
+      objectStream = new ObjectOutputStream(stream);
+      for (Serializable object : objects) {
+        objectStream.writeObject(object);
+      }
     } catch (IOException e) {
       logger.log(TreeLogger.ERROR, "Unable to write file: "
           + file.getAbsolutePath(), e);
       throw new UnableToCompleteException();
     } finally {
-      Utility.close(buffered);
-      Utility.close(writer);
+      Utility.close(objectStream);
       Utility.close(stream);
     }
   }
@@ -1016,16 +1071,25 @@
     return true;
   }
 
-  private static void copy(InputStream is, OutputStream os) throws IOException {
+  public static void writeStringAsFile(TreeLogger logger, File file,
+      String string) throws UnableToCompleteException {
+    FileOutputStream stream = null;
+    OutputStreamWriter writer = null;
+    BufferedWriter buffered = null;
     try {
-      byte[] buf = new byte[8 * 1024];
-      int i;
-      while ((i = is.read(buf)) != -1) {
-        os.write(buf, 0, i);
-      }
+      stream = new FileOutputStream(file);
+      writer = new OutputStreamWriter(stream, DEFAULT_ENCODING);
+      buffered = new BufferedWriter(writer);
+      file.getParentFile().mkdirs();
+      buffered.write(string);
+    } catch (IOException e) {
+      logger.log(TreeLogger.ERROR, "Unable to write file: "
+          + file.getAbsolutePath(), e);
+      throw new UnableToCompleteException();
     } finally {
-      Utility.close(is);
-      Utility.close(os);
+      Utility.close(buffered);
+      Utility.close(writer);
+      Utility.close(stream);
     }
   }