Refactor SOYC artifacts and link process to use disk cache instead of writing individual files into the work directory.

Also makes SyntheticArtifact publically usable and serializable between phases.

Review by: bobv (desk)

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5445 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java
index 06b9549..e8313f4 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java
@@ -38,7 +38,7 @@
    */
   protected final SyntheticArtifact emitBytes(TreeLogger logger, byte[] what,
       String partialPath) throws UnableToCompleteException {
-    return new SyntheticArtifact(logger, getClass(), partialPath, what);
+    return new SyntheticArtifact(getClass(), partialPath, what);
   }
 
   /**
@@ -53,7 +53,7 @@
    */
   protected final SyntheticArtifact emitBytes(TreeLogger logger, byte[] what,
       String partialPath, long lastModified) throws UnableToCompleteException {
-    return new SyntheticArtifact(logger, getClass(), partialPath, what, lastModified);
+    return new SyntheticArtifact(getClass(), partialPath, what, lastModified);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java b/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java
index 8496f0c..f4332b6 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java
@@ -17,8 +17,6 @@
 
 import com.google.gwt.core.ext.Linker;
 
-import java.io.File;
-
 /**
  * Represents analysis data for a CompilationResult.
  */
@@ -31,17 +29,17 @@
   /**
    * @return a file of dependencies
    */
-  public abstract File getDepFile();
+  public abstract EmittedArtifact getDepFile();
 
   /**
    * @return a file of split points
    */
-  public abstract File getSplitPointsFile();
+  public abstract EmittedArtifact getSplitPointsFile();
 
   /**
    * @return a file of stories
    */
-  public abstract File getStoriesFile();
+  public abstract EmittedArtifact getStoriesFile();
 
   @Override
   public final int hashCode() {
@@ -50,9 +48,9 @@
     assert (getStoriesFile() != null);
     assert (getSplitPointsFile() != null);
 
-    return 17 * (37 + getDepFile().getName().hashCode())
-        + (37 + getStoriesFile().getName().hashCode())
-        + (37 + getSplitPointsFile().getName().hashCode());
+    return 17 * (37 + getDepFile().getPartialPath().hashCode())
+        + (37 + getStoriesFile().getPartialPath().hashCode())
+        + (37 + getSplitPointsFile().getPartialPath().hashCode());
   }
 
   @Override
@@ -64,15 +62,16 @@
       return 1;
     } else if ((getDepFile() != null) && (o.getDepFile() == null)) {
       return -1;
-    } else if (getDepFile().getName().compareTo(o.getDepFile().getName()) == 0) {
+    } else if (getDepFile().getPartialPath().compareTo(
+        o.getDepFile().getPartialPath()) == 0) {
       if ((getStoriesFile() == null) && (o.getStoriesFile() == null)) {
         return 0;
       } else if ((getStoriesFile() == null) && (o.getStoriesFile() != null)) {
         return 1;
       } else if ((getStoriesFile() != null) && (o.getStoriesFile() == null)) {
         return -1;
-      } else if (getStoriesFile().getName().compareTo(
-          o.getStoriesFile().getName()) == 0) {
+      } else if (getStoriesFile().getPartialPath().compareTo(
+          o.getStoriesFile().getPartialPath()) == 0) {
         if ((getSplitPointsFile() == null) && (o.getSplitPointsFile() == null)) {
           return 0;
         }
@@ -82,15 +81,16 @@
             && (o.getSplitPointsFile() == null)) {
           return -1;
         } else {
-          return getSplitPointsFile().getName().compareTo(
-              o.getSplitPointsFile().getName());
+          return getSplitPointsFile().getPartialPath().compareTo(
+              o.getSplitPointsFile().getPartialPath());
         }
       } else {
-        return getStoriesFile().getName().compareTo(
-            o.getStoriesFile().getName());
+        return getStoriesFile().getPartialPath().compareTo(
+            o.getStoriesFile().getPartialPath());
       }
     } else {
-      return getDepFile().getName().compareTo(o.getDepFile().getName());
+      return getDepFile().getPartialPath().compareTo(
+          o.getDepFile().getPartialPath());
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java b/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java
index 9faa554..d65a214 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java
@@ -19,9 +19,14 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.util.DiskCache;
+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.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
 
 /**
@@ -31,14 +36,14 @@
   private static final DiskCache diskCache = new DiskCache();
 
   private final long lastModified;
-  private final long token;
+  private transient long token;
 
-  SyntheticArtifact(TreeLogger logger, Class<? extends Linker> linkerType,
+  public SyntheticArtifact(Class<? extends Linker> linkerType,
       String partialPath, byte[] data) {
-    this(logger, linkerType, partialPath, data, System.currentTimeMillis());
+    this(linkerType, partialPath, data, System.currentTimeMillis());
   }
 
-  SyntheticArtifact(TreeLogger logger, Class<? extends Linker> linkerType,
+  public SyntheticArtifact(Class<? extends Linker> linkerType,
       String partialPath, byte[] data, long lastModified) {
     super(linkerType, partialPath);
     assert data != null;
@@ -62,4 +67,17 @@
       throws UnableToCompleteException {
     diskCache.writeTo(token, out);
   }
+
+  private void readObject(ObjectInputStream stream) throws IOException,
+      ClassNotFoundException {
+    stream.defaultReadObject();
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    Util.copyNoClose(stream, baos);
+    token = diskCache.writeByteArray(baos.toByteArray());
+  }
+
+  private void writeObject(ObjectOutputStream stream) throws IOException {
+    stream.defaultWriteObject();
+    diskCache.writeTo(token, stream);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java
index 839bb8e..022baef 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java
@@ -15,11 +15,9 @@
  */
 package com.google.gwt.core.ext.linker.impl;
 
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.linker.CompilationAnalysis;
-
-import java.io.File;
+import com.google.gwt.core.ext.linker.SyntheticArtifact;
+import com.google.gwt.core.linker.SoycReportLinker;
 
 /**
  * An implementation of CompilationAnalysis. This class transforms SourceInfos
@@ -29,48 +27,54 @@
 public class StandardCompilationAnalysis extends CompilationAnalysis {
 
   /**
+   * A SOYC artifact. The existence of this class is an implementation detail.
+   */
+  public static class SoycArtifact extends SyntheticArtifact {
+    public SoycArtifact(String partialPath, byte[] bytes) {
+      super(SoycReportLinker.class, partialPath, bytes);
+      setPrivate(true);
+    }
+  }
+
+  /**
    * File containing method-level control-flow dependencies (corresponding to
    * the current report).
    */
-  private File depFile;
+  private SoycArtifact depFile;
 
   /**
    * File containing split points.
    */
-  private File splitPointsFile;
+  private SoycArtifact splitPointsFile;
 
   /**
    * File containing stories.
    */
-  private File storiesFile;
+  private SoycArtifact storiesFile;
 
   /**
    * Constructed by PermutationCompiler.
    */
-  public StandardCompilationAnalysis(TreeLogger logger, File depFile,
-      File storiesFile, File splitPointsFile) throws UnableToCompleteException {
+  public StandardCompilationAnalysis(SoycArtifact dependencies,
+      SoycArtifact stories, SoycArtifact splitPoints) {
     super(StandardLinkerContext.class);
-    logger = logger.branch(TreeLogger.INFO, "Creating CompilationAnalysis");
-
-    this.depFile = depFile;
-    this.storiesFile = storiesFile;
-    this.splitPointsFile = splitPointsFile;
-
-    logger.log(TreeLogger.INFO, "Done");
+    this.depFile = dependencies;
+    this.storiesFile = stories;
+    this.splitPointsFile = splitPoints;
   }
 
   @Override
-  public File getDepFile() {
+  public SoycArtifact getDepFile() {
     return depFile;
   }
 
   @Override
-  public File getSplitPointsFile() {
+  public SoycArtifact getSplitPointsFile() {
     return splitPointsFile;
   }
 
   @Override
-  public File getStoriesFile() {
+  public SoycArtifact getStoriesFile() {
     return storiesFile;
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/SplitPointRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/SplitPointRecorder.java
deleted file mode 100644
index 2d36a98..0000000
--- a/dev/core/src/com/google/gwt/core/ext/soyc/SplitPointRecorder.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2009 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.core.ext.soyc;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.JProgram;
-
-import java.io.File;
-
-/**
- * Used to record (runAsync) split points of a program.
- */
-public interface SplitPointRecorder {
-  File recordSplitPoints(JProgram jprogram, File workDir, int permutationId,
-      TreeLogger logger);
-}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/StoryRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/StoryRecorder.java
deleted file mode 100644
index 38cfa6a..0000000
--- a/dev/core/src/com/google/gwt/core/ext/soyc/StoryRecorder.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2009 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.core.ext.soyc;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.SourceInfo;
-import com.google.gwt.dev.jjs.ast.JProgram;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Used to record dependencies of a program.
- */
-public interface StoryRecorder {
-  File recordStories(JProgram jprogram, File workDir, int permutationId,
-      TreeLogger logger, List<Map<Range, SourceInfo>> sourceInfoMaps,
-      String[] js);
-}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorderImpl.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
similarity index 80%
rename from dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorderImpl.java
rename to dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
index 5991ec9..f5335ca 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorderImpl.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
@@ -13,7 +13,6 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package com.google.gwt.core.ext.soyc.impl;
 
 import com.google.gwt.core.ext.TreeLogger;
@@ -23,8 +22,7 @@
 import com.google.gwt.dev.util.HtmlTextOutput;
 import com.google.gwt.util.tools.Utility;
 
-import java.io.File;
-import java.io.FileOutputStream;
+import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -33,13 +31,25 @@
 /**
  * The control-flow dependency recorder for SOYC.
  */
-public class DependencyRecorderImpl implements
+public class DependencyRecorder implements
     ControlFlowAnalyzer.DependencyRecorder {
 
+  /**
+   * Used to record dependencies of a program.
+   */
+  public static void recordDependencies(TreeLogger logger, OutputStream out,
+      JProgram jprogram) {
+    new DependencyRecorder().recordDependenciesImpl(logger, out, jprogram);
+  }
+
   private HtmlTextOutput htmlOut;
   private PrintWriter pw;
+
   private OutputStreamWriter writer;
 
+  private DependencyRecorder() {
+  }
+
   /**
    * Used to record the dependencies of a specific method.
    * 
@@ -48,20 +58,14 @@
    */
   public void methodIsLiveBecause(JMethod liveMethod,
       ArrayList<JMethod> dependencyChain) {
-    printMethodDependency(liveMethod, dependencyChain);
+    printMethodDependency(dependencyChain);
   }
 
   /**
    * Used to record dependencies of a program.
-   * 
-   * @param jprogram
-   * @param workDir
-   * @param permutationId
-   * @param logger
-   * @return The file that the dependencies are recorded in
    */
-  public File recordDependencies(JProgram jprogram, File workDir,
-      int permutationId, TreeLogger logger) {
+  protected void recordDependenciesImpl(TreeLogger logger, OutputStream out,
+      JProgram jprogram) {
 
     logger = logger.branch(TreeLogger.INFO,
         "Creating Dependencies file for SOYC");
@@ -69,13 +73,8 @@
     ControlFlowAnalyzer dependencyAnalyzer = new ControlFlowAnalyzer(jprogram);
     dependencyAnalyzer.setDependencyRecorder(this);
 
-    File appendDepFile = new File(workDir, "dependencies" + permutationId
-        + ".xml.gz");
     try {
-      // No need to check mkdirs result because an IOException will occur anyway
-      appendDepFile.getParentFile().mkdirs();
-      FileOutputStream stream = new FileOutputStream(appendDepFile, true);
-      writer = new OutputStreamWriter(new GZIPOutputStream(stream), "UTF-8");
+      writer = new OutputStreamWriter(new GZIPOutputStream(out), "UTF-8");
       pw = new PrintWriter(writer);
       htmlOut = new HtmlTextOutput(pw, false);
     } catch (Throwable e) {
@@ -91,8 +90,6 @@
     Utility.close(writer);
 
     logger.log(TreeLogger.INFO, "Done");
-
-    return appendDepFile;
   }
 
   /**
@@ -101,8 +98,7 @@
    * @param liveMethod
    * @param dependencyChain
    */
-  private void printMethodDependency(JMethod liveMethod,
-      ArrayList<JMethod> dependencyChain) {
+  private void printMethodDependency(ArrayList<JMethod> dependencyChain) {
     String curLine;
     for (int i = dependencyChain.size() - 1; i >= 0; i--) {
       JMethod curMethod = dependencyChain.get(i);
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorderImpl.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
similarity index 71%
rename from dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorderImpl.java
rename to dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
index d2ed828..30b9781 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorderImpl.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
@@ -13,17 +13,14 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package com.google.gwt.core.ext.soyc.impl;
 
 import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.soyc.SplitPointRecorder;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.util.HtmlTextOutput;
 import com.google.gwt.util.tools.Utility;
 
-import java.io.File;
-import java.io.FileOutputStream;
+import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Map;
@@ -32,38 +29,26 @@
 /**
  * Records split points to a file for SOYC reports.
  */
-public class SplitPointRecorderImpl implements SplitPointRecorder {
-
-  private FileOutputStream stream;
-  private OutputStreamWriter writer;
-  private PrintWriter pw;
-  private HtmlTextOutput htmlOut;
+public class SplitPointRecorder {
 
   /**
    * Used to record (runAsync) split points of a program.
    * 
    * @param jprogram
-   * @param workDir
-   * @param permutationId
+   * @param out
    * @param logger
-   * @return The file that the dependencies are recorded in
    */
-  public File recordSplitPoints(JProgram jprogram, File workDir,
-      int permutationId, TreeLogger logger) {
+  public static void recordSplitPoints(JProgram jprogram, OutputStream out,
+      TreeLogger logger) {
 
     logger = logger.branch(TreeLogger.INFO,
         "Creating Split Point Map file for SOYC");
 
-    File splitPointsFile = new File(workDir, "splitPoints"
-        + Integer.toString(permutationId) + ".xml.gz");
     try {
-      // No need to check mkdirs result because an IOException will occur anyway
-      splitPointsFile.getParentFile().mkdirs();
-      stream = new FileOutputStream(splitPointsFile, true);
-      writer = new OutputStreamWriter(new GZIPOutputStream(stream), "UTF-8");
-      pw = new PrintWriter(writer);
-      htmlOut = new HtmlTextOutput(pw, false);
-
+      OutputStreamWriter writer = new OutputStreamWriter(new GZIPOutputStream(
+          out), "UTF-8");
+      PrintWriter pw = new PrintWriter(writer);
+      HtmlTextOutput htmlOut = new HtmlTextOutput(pw, false);
       String curLine = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
       htmlOut.printRaw(curLine);
       htmlOut.newline();
@@ -109,7 +94,8 @@
     } catch (Throwable e) {
       logger.log(TreeLogger.ERROR, "Could not open dependency file.", e);
     }
+  }
 
-    return splitPointsFile;
+  private SplitPointRecorder() {
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorderImpl.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
similarity index 93%
rename from dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorderImpl.java
rename to dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
index 83f7491..c2dfafa 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorderImpl.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
@@ -13,7 +13,6 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package com.google.gwt.core.ext.soyc.impl;
 
 import com.google.gwt.core.ext.TreeLogger;
@@ -22,7 +21,6 @@
 import com.google.gwt.core.ext.soyc.Member;
 import com.google.gwt.core.ext.soyc.Range;
 import com.google.gwt.core.ext.soyc.Story;
-import com.google.gwt.core.ext.soyc.StoryRecorder;
 import com.google.gwt.core.ext.soyc.Story.Origin;
 import com.google.gwt.dev.jjs.Correlation;
 import com.google.gwt.dev.jjs.SourceInfo;
@@ -30,13 +28,11 @@
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.js.ast.JsFunction;
 import com.google.gwt.dev.util.HtmlTextOutput;
 import com.google.gwt.util.tools.Utility;
 
-import java.io.File;
-import java.io.FileOutputStream;
+import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -54,7 +50,7 @@
 /**
  * Records {@link Story}s to a file for SOYC.
  */
-public class StoryRecorderImpl implements StoryRecorder {
+public class StoryRecorder {
 
   /**
    * Associates a SourceInfo with a Range.
@@ -71,7 +67,16 @@
     }
   }
 
+  /**
+   * Used to record dependencies of a program.
+   */
+  public static void recordStories(TreeLogger logger, OutputStream out,
+      List<Map<Range, SourceInfo>> sourceInfoMaps, String[] js) {
+    new StoryRecorder().recordStoriesImpl(logger, out, sourceInfoMaps, js);
+  }
+
   private int curHighestFragment = 0;
+
   private HtmlTextOutput htmlOut;
   private String[] js;
 
@@ -94,34 +99,20 @@
 
   private Map<Story, Integer> storyIds = new HashMap<Story, Integer>();
 
-  private FileOutputStream stream;
-
   private OutputStreamWriter writer;
 
-  /**
-   * Used to record dependencies of a program.
-   * 
-   * @param jprogram
-   * @param workDir
-   * @param permutationId
-   * @param logger
-   * @return The file that the dependencies are recorded in
-   */
-  public File recordStories(JProgram jprogram, File workDir, int permutationId,
-      TreeLogger logger, List<Map<Range, SourceInfo>> sourceInfoMaps,
-      String[] js) {
+  private StoryRecorder() {
+  }
+
+  protected void recordStoriesImpl(TreeLogger logger, OutputStream out,
+      List<Map<Range, SourceInfo>> sourceInfoMaps, String[] js) {
 
     logger = logger.branch(TreeLogger.INFO, "Creating Stories file for SOYC");
 
     this.js = js;
 
-    File storiesFile = new File(workDir, "stories"
-        + Integer.toString(permutationId) + ".xml.gz");
     try {
-      // No need to check mkdirs result because an IOException will occur anyway 
-      storiesFile.getParentFile().mkdirs();
-      stream = new FileOutputStream(storiesFile, true);
-      writer = new OutputStreamWriter(new GZIPOutputStream(stream), "UTF-8");
+      writer = new OutputStreamWriter(new GZIPOutputStream(out), "UTF-8");
       pw = new PrintWriter(writer);
       htmlOut = new HtmlTextOutput(pw, false);
 
@@ -188,8 +179,6 @@
     } catch (Throwable e) {
       logger.log(TreeLogger.ERROR, "Could not open dependency file.", e);
     }
-
-    return storiesFile;
   }
 
   private void analyzeFragment(MemberFactory memberFactory,
@@ -466,7 +455,7 @@
           members.add(m);
         }
       }
-        
+
       SortedSet<Origin> origins = new TreeSet<Origin>();
       for (Correlation c : info.getAllCorrelations(Axis.ORIGIN)) {
         origins.add(new OriginImpl(c.getOrigin()));
diff --git a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
index df3f526..f208795 100644
--- a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
@@ -20,52 +20,16 @@
 import com.google.gwt.core.ext.TreeLogger;

 import com.google.gwt.core.ext.UnableToCompleteException;

 import com.google.gwt.core.ext.linker.ArtifactSet;

-import com.google.gwt.core.ext.linker.EmittedArtifact;

 import com.google.gwt.core.ext.linker.LinkerOrder;

 import com.google.gwt.core.ext.linker.LinkerOrder.Order;

 import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis;

 

-import java.io.File;

-import java.io.FileInputStream;

-import java.io.IOException;

-import java.io.InputStream;

-

 /**

  * Converts SOYC report files into emitted private artifacts.

  */

 @LinkerOrder(Order.POST)

 public class SoycReportLinker extends Linker {

 

-  private static class SoycArtifact extends EmittedArtifact {

-    private final File file;

-

-    public SoycArtifact(String partialPath, File file) {

-      super(SoycReportLinker.class, partialPath);

-      this.file = file;

-    }

-

-    @Override

-    public InputStream getContents(TreeLogger logger)

-        throws UnableToCompleteException {

-      try {

-        return new FileInputStream(file);

-      } catch (IOException e) {

-        logger.log(TreeLogger.ERROR, "Unable to open file", e);

-        throw new UnableToCompleteException();

-      }

-    }

-

-    @Override

-    public long getLastModified() {

-      return file.lastModified();

-    }

-

-    @Override

-    public boolean isPrivate() {

-      return true;

-    }

-  }

-

   @Override

   public String getDescription() {

     return "Emit SOYC artifacts";

@@ -76,14 +40,9 @@
       ArtifactSet artifacts) throws UnableToCompleteException {

     ArtifactSet results = new ArtifactSet(artifacts);

     for (StandardCompilationAnalysis soycFiles : artifacts.find(StandardCompilationAnalysis.class)) {

-      File depFile = soycFiles.getDepFile();

-      results.add(new SoycArtifact(depFile.getName(), depFile));

-

-      File storiesFile = soycFiles.getStoriesFile();

-      results.add(new SoycArtifact(storiesFile.getName(), storiesFile));

-

-      File splitPointsFile = soycFiles.getSplitPointsFile();

-      results.add(new SoycArtifact(splitPointsFile.getName(), splitPointsFile));

+      results.add(soycFiles.getDepFile());

+      results.add(soycFiles.getStoriesFile());

+      results.add(soycFiles.getSplitPointsFile());

     }

     return results;

   }

diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index 4e1ae47..9ca8436 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -16,15 +16,14 @@
 package com.google.gwt.dev.jjs;
 
 import com.google.gwt.dev.util.arg.OptionAggressivelyOptimize;
+import com.google.gwt.dev.util.arg.OptionCompilationStateRetained;
 import com.google.gwt.dev.util.arg.OptionDisableCastChecking;
 import com.google.gwt.dev.util.arg.OptionDisableClassMetadata;
 import com.google.gwt.dev.util.arg.OptionDraftCompile;
 import com.google.gwt.dev.util.arg.OptionEnableAssertions;
-import com.google.gwt.dev.util.arg.OptionCompilationStateRetained;
 import com.google.gwt.dev.util.arg.OptionRunAsyncEnabled;
 import com.google.gwt.dev.util.arg.OptionScriptStyle;
 import com.google.gwt.dev.util.arg.OptionSoycEnabled;
-import com.google.gwt.dev.util.arg.OptionWorkDir;
 
 /**
  * Controls options for the {@link JavaToJavaScriptCompiler}.
@@ -32,5 +31,5 @@
 public interface JJSOptions extends OptionAggressivelyOptimize,
     OptionDisableClassMetadata, OptionDisableCastChecking, OptionDraftCompile,
     OptionEnableAssertions, OptionRunAsyncEnabled, OptionScriptStyle,
-    OptionSoycEnabled, OptionCompilationStateRetained, OptionWorkDir {
+    OptionSoycEnabled, OptionCompilationStateRetained {
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
index 9c92c43..569e1ad 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.dev.jjs;
 
-import java.io.File;
 import java.io.Serializable;
 
 /**
@@ -32,7 +31,6 @@
   private JsOutputOption output = JsOutputOption.OBFUSCATED;
   private boolean runAsyncEnabled = true;
   private boolean soycEnabled = false;
-  private File workDir;
 
   public JJSOptionsImpl() {
   }
@@ -51,17 +49,12 @@
     setOutput(other.getOutput());
     setRunAsyncEnabled(other.isRunAsyncEnabled());
     setSoycEnabled(other.isSoycEnabled());
-    setWorkDir(other.getWorkDir());
   }
 
   public JsOutputOption getOutput() {
     return output;
   }
 
-  public File getWorkDir() {
-    return workDir;
-  }
-
   public boolean isAggressivelyOptimize() {
     return aggressivelyOptimize;
   }
@@ -129,8 +122,4 @@
   public void setSoycEnabled(boolean enabled) {
     soycEnabled = enabled;
   }
-
-  public void setWorkDir(File workDir) {
-    this.workDir = workDir;
-  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 65aeb56..4f60974 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -21,12 +21,11 @@
 import com.google.gwt.core.ext.linker.SymbolData;
 import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis;
 import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
+import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis.SoycArtifact;
 import com.google.gwt.core.ext.soyc.Range;
-import com.google.gwt.core.ext.soyc.SplitPointRecorder;
-import com.google.gwt.core.ext.soyc.StoryRecorder;
-import com.google.gwt.core.ext.soyc.impl.DependencyRecorderImpl;
-import com.google.gwt.core.ext.soyc.impl.SplitPointRecorderImpl;
-import com.google.gwt.core.ext.soyc.impl.StoryRecorderImpl;
+import com.google.gwt.core.ext.soyc.impl.DependencyRecorder;
+import com.google.gwt.core.ext.soyc.impl.SplitPointRecorder;
+import com.google.gwt.core.ext.soyc.impl.StoryRecorder;
 import com.google.gwt.dev.PermutationResult;
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.jdt.RebindPermutationOracle;
@@ -112,7 +111,6 @@
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -319,24 +317,31 @@
 
       PermutationResult toReturn = new PermutationResultImpl(js,
           makeSymbolMap(symbolTable));
+
       if (sourceInfoMaps != null) {
+        // Free up memory.
+        symbolTable = null;
 
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
         // get method dependencies
-        DependencyRecorderImpl dr = new DependencyRecorderImpl();
-        File depFile = dr.recordDependencies(jprogram, options.getWorkDir(),
-            permutationId, logger);
+        StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
+        SoycArtifact stories = new SoycArtifact("stories" + permutationId
+            + ".xml.gz", baos.toByteArray());
+        // Free up memory.
+        js = null;
 
-        StoryRecorder sr = new StoryRecorderImpl();
-        File storyFile = sr.recordStories(jprogram, options.getWorkDir(),
-            permutationId, logger, sourceInfoMaps, js);
+        baos.reset();
+        DependencyRecorder.recordDependencies(logger, baos, jprogram);
+        SoycArtifact dependencies = new SoycArtifact("dependencies"
+            + permutationId + ".xml.gz", baos.toByteArray());
 
-        SplitPointRecorder spr = new SplitPointRecorderImpl();
-        File splitPointsFile = spr.recordSplitPoints(jprogram,
-            options.getWorkDir(), permutationId, logger);
+        baos.reset();
+        SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
+        SoycArtifact splitPoints = new SoycArtifact("splitPoints"
+            + permutationId + ".xml.gz", baos.toByteArray());
 
         toReturn.getArtifacts().add(
-            new StandardCompilationAnalysis(logger, depFile, storyFile,
-                splitPointsFile));
+            new StandardCompilationAnalysis(dependencies, stories, splitPoints));
       }
       return toReturn;
     } catch (Throwable e) {