A faster implementation of SOYC that
reports whole-number size breakdowns in terms of
the program state after optimization.

Review by: kprobst


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5872 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 f4332b6..615f333 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,6 +17,10 @@
 
 import com.google.gwt.core.ext.Linker;
 
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
 /**
  * Represents analysis data for a CompilationResult.
  */
@@ -32,70 +36,77 @@
   public abstract EmittedArtifact getDepFile();
 
   /**
+   * @return a file with detailed story information
+   */
+  public abstract EmittedArtifact getDetailedStoriesFile();
+
+  /**
+   * @return a file of size maps
+   */
+  public abstract EmittedArtifact getSizeMapsFile();
+
+  /**
    * @return a file of split points
    */
   public abstract EmittedArtifact getSplitPointsFile();
 
-  /**
-   * @return a file of stories
-   */
-  public abstract EmittedArtifact getStoriesFile();
-
   @Override
   public final int hashCode() {
-
-    assert (getDepFile() != null);
-    assert (getStoriesFile() != null);
-    assert (getSplitPointsFile() != null);
-
-    return 17 * (37 + getDepFile().getPartialPath().hashCode())
-        + (37 + getStoriesFile().getPartialPath().hashCode())
-        + (37 + getSplitPointsFile().getPartialPath().hashCode());
+    int code = 37;
+    for (EmittedArtifact file : allFiles()) {
+      if (file == null) {
+        code = code * 17 + 37;
+      } else {
+        code = code * 17 + file.getPartialPath().hashCode();
+      }
+    }
+    return code;
   }
 
   @Override
   protected final int compareToComparableArtifact(CompilationAnalysis o) {
+    LinkedList<EmittedArtifact> myFiles = new LinkedList<EmittedArtifact>(
+        allFiles());
+    LinkedList<EmittedArtifact> otherFiles = new LinkedList<EmittedArtifact>(
+        o.allFiles());
+    assert (myFiles.size() == otherFiles.size());
 
-    if ((getDepFile() == null) && (o.getDepFile() == null)) {
-      return 0;
-    } else if ((getDepFile() == null) && (o.getDepFile() != null)) {
-      return 1;
-    } else if ((getDepFile() != null) && (o.getDepFile() == null)) {
-      return -1;
-    } 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().getPartialPath().compareTo(
-          o.getStoriesFile().getPartialPath()) == 0) {
-        if ((getSplitPointsFile() == null) && (o.getSplitPointsFile() == null)) {
-          return 0;
-        }
-        if ((getSplitPointsFile() == null) && (o.getSplitPointsFile() != null)) {
-          return 1;
-        } else if ((getSplitPointsFile() != null)
-            && (o.getSplitPointsFile() == null)) {
-          return -1;
-        } else {
-          return getSplitPointsFile().getPartialPath().compareTo(
-              o.getSplitPointsFile().getPartialPath());
-        }
-      } else {
-        return getStoriesFile().getPartialPath().compareTo(
-            o.getStoriesFile().getPartialPath());
+    while (!myFiles.isEmpty()) {
+      EmittedArtifact myFile = myFiles.removeFirst();
+      EmittedArtifact otherFile = otherFiles.removeFirst();
+      if (myFile == null && otherFile == null) {
+        continue;
       }
-    } else {
-      return getDepFile().getPartialPath().compareTo(
-          o.getDepFile().getPartialPath());
+      if (myFile == null && otherFile != null) {
+        return -1;
+      }
+      if (myFile != null && otherFile == null) {
+        return 1;
+      }
+      assert myFile != null;
+      assert otherFile != null;
+
+      int fileCompare = myFile.getPartialPath().compareTo(
+          otherFile.getPartialPath());
+      if (fileCompare != 0) {
+        return fileCompare;
+      }
     }
+
+    return 0;
   }
 
   @Override
   protected final Class<CompilationAnalysis> getComparableArtifactType() {
     return CompilationAnalysis.class;
   }
+
+  private List<EmittedArtifact> allFiles() {
+    List<EmittedArtifact> files = new ArrayList<EmittedArtifact>();
+    files.add(getSplitPointsFile());
+    files.add(getDepFile());
+    files.add(getSizeMapsFile());
+    files.add(getDetailedStoriesFile());
+    return files;
+  }
 }
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 022baef..b9a8969 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
@@ -43,24 +43,31 @@
   private SoycArtifact depFile;
 
   /**
+   * File containing detailed story information.
+   */
+  private SoycArtifact detailedStoriesFile;
+
+  /**
    * File containing split points.
    */
   private SoycArtifact splitPointsFile;
 
   /**
-   * File containing stories.
+   * File containing size maps.
    */
-  private SoycArtifact storiesFile;
+  private SoycArtifact sizeMapsFile;
 
   /**
    * Constructed by PermutationCompiler.
    */
   public StandardCompilationAnalysis(SoycArtifact dependencies,
-      SoycArtifact stories, SoycArtifact splitPoints) {
+      SoycArtifact sizeMaps, SoycArtifact splitPoints,
+      SoycArtifact detailedStories) {
     super(StandardLinkerContext.class);
     this.depFile = dependencies;
-    this.storiesFile = stories;
+    this.sizeMapsFile = sizeMaps;
     this.splitPointsFile = splitPoints;
+    this.detailedStoriesFile = detailedStories;
   }
 
   @Override
@@ -69,12 +76,17 @@
   }
 
   @Override
-  public SoycArtifact getSplitPointsFile() {
-    return splitPointsFile;
+  public SoycArtifact getDetailedStoriesFile() {
+    return detailedStoriesFile;
   }
 
   @Override
-  public SoycArtifact getStoriesFile() {
-    return storiesFile;
+  public SoycArtifact getSizeMapsFile() {
+    return sizeMapsFile;
+  }
+
+  @Override
+  public SoycArtifact getSplitPointsFile() {
+    return splitPointsFile;
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java
new file mode 100644
index 0000000..997d2b0
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java
@@ -0,0 +1,116 @@
+/*
+ * 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.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
+import com.google.gwt.dev.js.SizeBreakdown;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.util.Util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Records an array of {@link SizeBreakdown} to a gzipped XML file. That file is
+ * then read to produce a Story of Your Compile.
+ */
+public class SizeMapRecorder {
+  /**
+   * A human-accessible type and description of a program reference. These are
+   * produced by
+   * {@link SizeMapRecorder#typedProgramReference(JsName, JavaToJavaScriptMap)}
+   * and used by
+   * {@link SizeMapRecorder#recordMap(TreeLogger, OutputStream, SizeBreakdown[], JavaToJavaScriptMap)}
+   * .
+   */
+  private static class TypedProgramReference {
+    public final String type;
+    public final String description;
+
+    public TypedProgramReference(String type, String description) {
+      this.type = type;
+      this.description = description;
+    }
+  }
+
+  public static void recordMap(TreeLogger logger, OutputStream out,
+      SizeBreakdown[] sizeBreakdowns, JavaToJavaScriptMap jjsmap,
+      Map<JsName, String> obfuscateMap) throws IOException {
+    out = new GZIPOutputStream(out);
+    Writer writer = new OutputStreamWriter(out, Util.DEFAULT_ENCODING);
+
+    writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    writer.append("<sizemaps>\n");
+
+    for (int i = 0; i < sizeBreakdowns.length; i++) {
+      writer.append("<sizemap fragment=\"" + i + "\" " + "size=\""
+          + sizeBreakdowns[i].getSize() + "\">\n");
+      for (Entry<JsName, Integer> sizeMapEntry : sizeBreakdowns[i].getSizeMap().entrySet()) {
+        JsName name = sizeMapEntry.getKey();
+        int size = sizeMapEntry.getValue();
+        TypedProgramReference typedRef = typedProgramReference(name, jjsmap,
+            obfuscateMap);
+        writer.append("  <size " + "type=\"" + Util.escapeXml(typedRef.type)
+            + "\" " + "ref=\"" + Util.escapeXml(typedRef.description) + "\" "
+            + "size=\"" + size + "\"/>\n");
+      }
+      writer.append("</sizemap>\n");
+    }
+
+    writer.append("</sizemaps>");
+    writer.close();
+  }
+
+  private static TypedProgramReference typedProgramReference(JsName name,
+      JavaToJavaScriptMap jjsmap, Map<JsName, String> obfuscateMap) {
+    JMethod method = jjsmap.nameToMethod(name);
+    if (method != null) {
+      StringBuilder sb = new StringBuilder();
+      sb.append(method.getEnclosingType().getName());
+      sb.append("::");
+      sb.append(method.getName());
+      sb.append("(");
+      for (JType type : method.getOriginalParamTypes()) {
+        sb.append(type.getJsniSignatureName());
+      }
+      sb.append(")");
+      sb.append(method.getOriginalReturnType().getJsniSignatureName());
+      String desc = sb.toString();
+      return new TypedProgramReference("method", desc);
+    }
+
+    JReferenceType type = jjsmap.nameToType(name);
+    if (type != null) {
+      return new TypedProgramReference("type", type.getName());
+    }
+
+    String string = obfuscateMap.get(name);
+    if (string != null) {
+      return new TypedProgramReference("string", string);
+    }
+
+    return new TypedProgramReference("var", name.getShortIdent());
+  }
+}
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 2d93ab4..bb32174 100644
--- a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
@@ -43,8 +43,11 @@
       if (soycFiles.getDepFile() != null) {

         results.add(soycFiles.getDepFile());

       }

-      if (soycFiles.getStoriesFile() != null) {

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

+      if (soycFiles.getSizeMapsFile() != null) {

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

+      }

+      if (soycFiles.getDetailedStoriesFile() != null) {

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

       }

       results.add(soycFiles.getSplitPointsFile());

     }

diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 23bf485..e137fea 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -54,6 +54,7 @@
 import com.google.gwt.dev.util.arg.ArgHandlerMaxPermsPerPrecompile;
 import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
 import com.google.gwt.dev.util.arg.ArgHandlerSoyc;
+import com.google.gwt.dev.util.arg.ArgHandlerSoycDetailed;
 import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag;
 import com.google.gwt.dev.util.arg.OptionDisableUpdateCheck;
 import com.google.gwt.dev.util.arg.OptionDumpSignatures;
@@ -104,6 +105,7 @@
       registerHandler(new ArgHandlerDumpSignatures(options));
       registerHandler(new ArgHandlerMaxPermsPerPrecompile(options));
       registerHandler(new ArgHandlerSoyc(options));
+      registerHandler(new ArgHandlerSoycDetailed(options));
     }
 
     @Override
@@ -192,6 +194,10 @@
       return jjsOptions.isSoycEnabled();
     }
 
+    public boolean isSoycExtra() {
+      return jjsOptions.isSoycExtra();
+    }
+
     public boolean isUpdateCheckDisabled() {
       return disableUpdateCheck;
     }
@@ -256,6 +262,10 @@
       jjsOptions.setSoycEnabled(enabled);
     }
 
+    public void setSoycExtra(boolean soycExtra) {
+      jjsOptions.setSoycExtra(soycExtra);
+    }
+
     public void setValidateOnly(boolean validateOnly) {
       this.validateOnly = validateOnly;
     }
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 f9ff12c..28b7bbc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -25,6 +25,7 @@
 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.OptionSoycDetailed;
 
 /**
  * Controls options for the {@link JavaToJavaScriptCompiler}.
@@ -32,5 +33,5 @@
 public interface JJSOptions extends OptionAggressivelyOptimize,
     OptionDisableClassMetadata, OptionDisableCastChecking, OptionDraftCompile,
     OptionEnableAssertions, OptionRunAsyncEnabled, OptionScriptStyle,
-    OptionSoycEnabled, OptionCompilationStateRetained, OptionOptimizePrecompile {
+    OptionSoycEnabled, OptionSoycDetailed, OptionCompilationStateRetained, OptionOptimizePrecompile {
 }
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 80c49f5..a26d4a2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -32,6 +32,7 @@
   private JsOutputOption output = JsOutputOption.OBFUSCATED;
   private boolean runAsyncEnabled = true;
   private boolean soycEnabled = false;
+  private boolean soycExtra = false;
 
   public JJSOptionsImpl() {
   }
@@ -50,6 +51,7 @@
     setOutput(other.getOutput());
     setRunAsyncEnabled(other.isRunAsyncEnabled());
     setSoycEnabled(other.isSoycEnabled());
+    setSoycExtra(other.isSoycExtra());
   }
 
   public JsOutputOption getOutput() {
@@ -92,6 +94,10 @@
     return soycEnabled;
   }
 
+  public boolean isSoycExtra() {
+    return soycExtra;
+  }
+
   public void setAggressivelyOptimize(boolean aggressivelyOptimize) {
     this.aggressivelyOptimize = aggressivelyOptimize;
   }
@@ -131,4 +137,8 @@
   public void setSoycEnabled(boolean enabled) {
     soycEnabled = enabled;
   }
+
+  public void setSoycExtra(boolean enabled) {
+    soycExtra = enabled;
+  }
 }
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 52468eb..02b1199 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -26,6 +26,7 @@
 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.impl.DependencyRecorder;
+import com.google.gwt.core.ext.soyc.impl.SizeMapRecorder;
 import com.google.gwt.core.ext.soyc.impl.SplitPointRecorder;
 import com.google.gwt.core.ext.soyc.impl.StoryRecorder;
 import com.google.gwt.dev.cfg.ModuleDef;
@@ -88,13 +89,14 @@
 import com.google.gwt.dev.js.JsObfuscateNamer;
 import com.google.gwt.dev.js.JsPrettyNamer;
 import com.google.gwt.dev.js.JsReportGenerationVisitor;
-import com.google.gwt.dev.js.JsSourceGenerationVisitor;
+import com.google.gwt.dev.js.JsSourceGenerationVisitorWithSizeBreakdown;
 import com.google.gwt.dev.js.JsStackEmulator;
 import com.google.gwt.dev.js.JsStaticEval;
 import com.google.gwt.dev.js.JsStringInterner;
 import com.google.gwt.dev.js.JsSymbolResolver;
 import com.google.gwt.dev.js.JsUnusedFunctionRemover;
 import com.google.gwt.dev.js.JsVerboseNamer;
+import com.google.gwt.dev.js.SizeBreakdown;
 import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.util.AbstractTextOutput;
@@ -104,6 +106,7 @@
 import com.google.gwt.dev.util.PerfLogger;
 import com.google.gwt.dev.util.TextOutput;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.collect.Maps;
 
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.internal.compiler.CompilationResult;
@@ -291,9 +294,10 @@
       }
 
       // (10.5) Obfuscate
+      Map<JsName, String> obfuscateMap = Maps.create();
       switch (options.getOutput()) {
         case OBFUSCATED:
-          JsStringInterner.exec(jprogram, jsProgram);
+          obfuscateMap = JsStringInterner.exec(jprogram, jsProgram);
           JsObfuscateNamer.exec(jsProgram);
           break;
         case PRETTY:
@@ -301,7 +305,7 @@
           JsPrettyNamer.exec(jsProgram);
           break;
         case DETAILED:
-          JsStringInterner.exec(jprogram, jsProgram);
+          obfuscateMap = JsStringInterner.exec(jprogram, jsProgram);
           JsVerboseNamer.exec(jsProgram);
           break;
         default:
@@ -317,33 +321,19 @@
 
       // (12) Generate the final output text.
       String[] js = new String[jsProgram.getFragmentCount()];
-      List<Map<Range, SourceInfo>> sourceInfoMaps = options.isSoycEnabled()
-          ? new ArrayList<Map<Range, SourceInfo>>(jsProgram.getFragmentCount())
-          : null;
       StatementRanges[] ranges = new StatementRanges[js.length];
-      for (int i = 0; i < js.length; i++) {
-        DefaultTextOutput out = new DefaultTextOutput(
-            options.getOutput().shouldMinimize());
-        JsSourceGenerationVisitor v;
-        if (sourceInfoMaps != null) {
-          v = new JsReportGenerationVisitor(out);
-        } else {
-          v = new JsSourceGenerationVisitor(out);
-        }
-        v.accept(jsProgram.getFragmentBlock(i));
-        js[i] = out.toString();
-        if (sourceInfoMaps != null) {
-          sourceInfoMaps.add(((JsReportGenerationVisitor) v).getSourceInfoMap());
-        }
-        ranges[i] = v.getStatementRanges();
-      }
+      SizeBreakdown[] sizeBreakdowns = options.isSoycEnabled()
+          ? new SizeBreakdown[js.length] : null;
+      List<Map<Range, SourceInfo>> sourceInfoMaps = options.isSoycExtra()
+          ? new ArrayList<Map<Range, SourceInfo>>() : null;
+      generateJavaScriptCode(options, jsProgram, map, js, ranges,
+          sizeBreakdowns, sourceInfoMaps);
 
       PermutationResult toReturn = new PermutationResultImpl(js,
           makeSymbolMap(symbolTable), ranges, permutationId);
-
       toReturn.getArtifacts().add(
-          makeSoycArtifact(logger, permutationId, jprogram, js, sourceInfoMaps,
-              dependencies));
+          makeSoycArtifact(logger, permutationId, jprogram, js, sizeBreakdowns,
+              sourceInfoMaps, dependencies, map, obfuscateMap));
 
       System.out.println("Permutation took "
           + (System.currentTimeMillis() - permStart) + " ms");
@@ -422,7 +412,7 @@
     checkForErrors(logger, goldenCuds, false);
 
     PerfLogger.start("Build AST");
-    CorrelationFactory correlator = options.isSoycEnabled()
+    CorrelationFactory correlator = options.isSoycExtra()
         ? new RealCorrelationFactory() : new DummyCorrelationFactory();
     JProgram jprogram = new JProgram(correlator);
     JsProgram jsProgram = new JsProgram(correlator);
@@ -816,6 +806,46 @@
     return null;
   }
 
+  /**
+   * Generate JavaScript code from the given JavaScript ASTs. Also produces
+   * information about that transformation.
+   * 
+   * @param options The options this compiler instance is running with
+   * @param jsProgram The AST to convert to source code
+   * @param jjsMap A map between the JavaScript AST and the Java AST it came
+   *          from
+   * @param js An array to hold the output JavaScript
+   * @param ranges An array to hold the statement ranges for that JavaScript
+   * @param sizeBreakdowns An array to hold the size breakdowns for that
+   *          JavaScript
+   * @param sourceInfoMaps An array to hold the source info maps for that
+   *          JavaScript
+   */
+  private static void generateJavaScriptCode(JJSOptions options,
+      JsProgram jsProgram, JavaToJavaScriptMap jjsMap, String[] js,
+      StatementRanges[] ranges, SizeBreakdown[] sizeBreakdowns,
+      List<Map<Range, SourceInfo>> sourceInfoMaps) {
+    for (int i = 0; i < js.length; i++) {
+      DefaultTextOutput out = new DefaultTextOutput(
+          options.getOutput().shouldMinimize());
+      JsSourceGenerationVisitorWithSizeBreakdown v;
+      if (sourceInfoMaps != null) {
+        v = new JsReportGenerationVisitor(out, jjsMap);
+      } else {
+        v = new JsSourceGenerationVisitorWithSizeBreakdown(out, jjsMap);
+      }
+      v.accept(jsProgram.getFragmentBlock(i));
+      js[i] = out.toString();
+      if (sizeBreakdowns != null) {
+        sizeBreakdowns[i] = v.getSizeBreakdown();
+      }
+      if (sourceInfoMaps != null) {
+        sourceInfoMaps.add(((JsReportGenerationVisitor) v).getSourceInfoMap());
+      }
+      ranges[i] = v.getStatementRanges();
+    }
+  }
+
   private static UnableToCompleteException logAndTranslateException(
       TreeLogger logger, Throwable e) {
     if (e instanceof UnableToCompleteException) {
@@ -861,10 +891,13 @@
 
   private static StandardCompilationAnalysis makeSoycArtifact(
       TreeLogger logger, int permutationId, JProgram jprogram, String[] js,
-      List<Map<Range, SourceInfo>> sourceInfoMaps, SoycArtifact dependencies) {
+      SizeBreakdown[] sizeBreakdowns,
+      List<Map<Range, SourceInfo>> sourceInfoMaps, SoycArtifact dependencies,
+      JavaToJavaScriptMap jjsmap, Map<JsName, String> obfuscateMap)
+      throws IOException {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
-    PerfLogger.start("Computing SOYC output");
+    PerfLogger.start("Recording SOYC output");
 
     PerfLogger.start("Record split points");
     SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
@@ -872,20 +905,32 @@
         + ".xml.gz", baos.toByteArray());
     PerfLogger.end();
 
-    SoycArtifact stories = null;
+    SoycArtifact sizeMaps = null;
+    SoycArtifact detailedStories = null;
+
+    if (sizeBreakdowns != null) {
+      PerfLogger.start("Record size map");
+      baos.reset();
+      SizeMapRecorder.recordMap(logger, baos, sizeBreakdowns, jjsmap,
+          obfuscateMap);
+      sizeMaps = new SoycArtifact("stories" + permutationId + ".xml.gz",
+          baos.toByteArray());
+      PerfLogger.end();
+    }
 
     if (sourceInfoMaps != null) {
-      PerfLogger.start("Record stories");
+      PerfLogger.start("Record detailed stories");
       baos.reset();
       StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
-      stories = new SoycArtifact("stories" + permutationId + ".xml.gz",
-          baos.toByteArray());
+      detailedStories = new SoycArtifact("detailedStories" + permutationId
+          + ".xml.gz", baos.toByteArray());
       PerfLogger.end();
     }
 
     PerfLogger.end();
 
-    return new StandardCompilationAnalysis(dependencies, stories, splitPoints);
+    return new StandardCompilationAnalysis(dependencies, sizeMaps, splitPoints,
+        detailedStories);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
index 864be36..5ac8a58 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -161,6 +161,29 @@
     }
   }
 
+  /**
+   * Return the Java method corresponding to <code>stat</code>, or
+   * <code>null</code> if there isn't one. It recognizes JavaScript of the form
+   * <code>function foo(...) { ...}</code>, where <code>foo</code> is the name
+   * of the JavaScript translation of a Java method.
+   */
+  public static JMethod methodFor(JsStatement stat, JavaToJavaScriptMap map) {
+    if (stat instanceof JsExprStmt) {
+      JsExpression exp = ((JsExprStmt) stat).getExpression();
+      if (exp instanceof JsFunction) {
+        JsFunction func = (JsFunction) exp;
+        if (func.getName() != null) {
+          JMethod method = map.nameToMethod(func.getName());
+          if (method != null) {
+            return method;
+          }
+        }
+      }
+    }
+
+    return map.vtableInitToMethod(stat);
+  }
+
   private Set<JsName> entryMethodNames;
 
   private final JProgram jprogram;
@@ -363,9 +386,6 @@
     }
 
     JMethod meth = methodFor(stat);
-    if (meth == null) {
-      meth = map.vtableInitToMethod(stat);
-    }
 
     if (meth != null) {
       /*
@@ -411,16 +431,7 @@
    * of the JavaScript translation of a Java method.
    */
   private JMethod methodFor(JsStatement stat) {
-    if (stat instanceof JsExprStmt) {
-      JsExpression exp = ((JsExprStmt) stat).getExpression();
-      if (exp instanceof JsFunction) {
-        JsFunction func = (JsFunction) exp;
-        if (func.getName() != null) {
-          return map.nameToMethod(func.getName());
-        }
-      }
-    }
-    return null;
+    return methodFor(stat, map);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 58b0ae3..4acb98f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -1884,6 +1884,8 @@
    */
   private Set<JMethod> crossClassTargets = new HashSet<JMethod>();
 
+  private Map<String, JsFunction> indexedFunctions = Maps.create();
+
   /**
    * Contains JsNames for all interface methods. A special scope is needed so
    * that independent classes will obfuscate their interface implementation
@@ -1891,8 +1893,6 @@
    */
   private final JsScope interfaceScope;
 
-  private Map<String, JsFunction> indexedFunctions = Maps.create();
-
   private final JsProgram jsProgram;
 
   /**
@@ -2143,6 +2143,10 @@
         return nameToMethodMap.get(name);
       }
 
+      public JReferenceType nameToType(JsName name) {
+        return constructorNameToTypeMap.get(name);
+      }
+
       public JReferenceType typeForStatement(JsStatement stat) {
         return typeForStatMap.get(stat);
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
index 7a96d82..3a232b1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
@@ -49,6 +49,12 @@
   JMethod nameToMethod(JsName name);
 
   /**
+   * If <code>name</code> is the name of a constructor function corresponding to
+   * a Java type, then return that type. Otherwise, return <code>null</code>.
+   */
+  JReferenceType nameToType(JsName name);
+
+  /**
    * If <code>stat</code> is used to set up the definition of some class, return
    * that class. Otherwise, return null.
    */
diff --git a/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
index b5c7180..acfda17 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
@@ -18,6 +18,7 @@
 import com.google.gwt.core.ext.soyc.Range;
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
 import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.util.TextOutput;
 
@@ -30,12 +31,12 @@
  * A variation on the standard source generation visitor that records the
  * locations of SourceInfo objects in the output.
  */
-public class JsReportGenerationVisitor extends JsSourceGenerationVisitor {
+public class JsReportGenerationVisitor extends JsSourceGenerationVisitorWithSizeBreakdown {
   private final Map<Range, SourceInfo> sourceInfoMap = new HashMap<Range, SourceInfo>();
   private final TextOutput out;
 
-  public JsReportGenerationVisitor(TextOutput out) {
-    super(out);
+  public JsReportGenerationVisitor(TextOutput out, JavaToJavaScriptMap map) {
+    super(out, map);
     this.out = out;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
new file mode 100644
index 0000000..466ec4a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
@@ -0,0 +1,139 @@
+/*
+ * 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.dev.js;
+
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
+import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsVisitable;
+import com.google.gwt.dev.js.ast.JsVars.JsVar;
+import com.google.gwt.dev.util.TextOutput;
+import com.google.gwt.dev.util.collect.HashMap;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A version of {@link JsSourceGenerationVisitor} that records a
+ * {@link SizeBreakdown} as it goes.
+ */
+public class JsSourceGenerationVisitorWithSizeBreakdown extends
+    JsSourceGenerationVisitor {
+
+  private final JavaToJavaScriptMap map;
+  private JsName nameToBillTo;
+  private TextOutput out;
+  private final Map<JsName, Integer> sizeMap = new HashMap<JsName, Integer>();
+
+  public JsSourceGenerationVisitorWithSizeBreakdown(TextOutput out,
+      JavaToJavaScriptMap javaToJavaScriptMap) {
+    super(out);
+    this.out = out;
+    this.map = javaToJavaScriptMap;
+  }
+
+  public SizeBreakdown getSizeBreakdown() {
+    return new SizeBreakdown(out.getPosition(), sizeMap);
+  }
+  
+  @Override
+  public boolean visit(JsBlock x, JsContext<JsStatement> ctx) {
+    printJsBlock(x, false, true);
+    return false;
+  }
+
+  @Override
+  public boolean visit(JsProgram x, JsContext<JsProgram> ctx) {
+    // Descend naturally.
+    return true;
+  }
+
+  @Override
+  public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+    // Descend naturally.
+    return true;
+  }
+
+  @Override
+  protected <T extends JsVisitable<T>> T doAccept(T node) {
+    JsName newName = nameToBillTo(node);
+    if (newName == null) {
+      return super.doAccept(node);
+    } else {
+      JsName oldName = nameToBillTo;
+      nameToBillTo = newName;
+      int start = out.getPosition();
+      T retValue = super.doAccept(node);
+      billChars(nameToBillTo, out.getPosition() - start);
+      nameToBillTo = oldName;
+      return retValue;
+    }
+  }
+
+  @Override
+  protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
+    for (T t : collection) {
+      doAccept(t);
+    }
+  }
+
+  @Override
+  protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
+      List<T> collection) {
+    for (T t : collection) {
+      doAccept(t);
+    }
+  }
+
+  private void billChars(JsName nameToBillTo, int chars) {
+    Integer oldSize = sizeMap.get(nameToBillTo);
+    if (oldSize == null) {
+      oldSize = 0;
+    }
+    sizeMap.put(nameToBillTo, oldSize + chars);
+  }
+
+  private JsName nameToBillTo(JsVisitable<?> node) {
+    if (node instanceof JsStatement) {
+      JsStatement stat = (JsStatement) node;
+      JReferenceType type = map.typeForStatement(stat);
+      if (type != null) {
+        return map.nameForType(type);
+      }
+
+      JMethod method = FragmentExtractor.methodFor(stat, map);
+      if (method != null) {
+        return map.nameForMethod(method);
+      }
+    }
+
+    if (node instanceof JsVar) {
+      if (nameToBillTo == null) {
+        return ((JsVar) node).getName();
+      }
+    }
+
+    return null;
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
index 415737a..126bd52 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
@@ -36,11 +36,13 @@
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.SortedSet;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.Map.Entry;
 
 /**
  * Interns all String literals in a JsProgram. Each unique String will be
@@ -191,14 +193,14 @@
     }
   }
 
+  public static final String PREFIX = "$intern_";
+
   private static final Comparator<JsStringLiteral> LITERAL_COMPARATOR = new Comparator<JsStringLiteral>() {
     public int compare(JsStringLiteral o1, JsStringLiteral o2) {
       return o1.getValue().compareTo(o2.getValue());
     }
   };
 
-  public static final String PREFIX = "$intern_";
-
   /**
    * Apply interning of String literals to a JsProgram. The symbol names for the
    * interned strings will be defined within the program's top scope and the
@@ -208,8 +210,9 @@
    * @param jprogram the JProgram that has fragment dependency data for
    *          <code>program</code>
    * @param program the JsProgram
+   * @return a map describing the interning that occurred
    */
-  public static void exec(JProgram jprogram, JsProgram program) {
+  public static Map<JsName, String> exec(JProgram jprogram, JsProgram program) {
     StringVisitor v = new StringVisitor(jprogram, program.getScope());
     v.accept(program);
 
@@ -227,6 +230,8 @@
       createVars(program, program.getFragmentBlock(entry.getKey()),
           entry.getValue(), v.toCreate);
     }
+
+    return reverse(v.toCreate);
   }
 
   /**
@@ -267,6 +272,16 @@
     }
   }
 
+  private static Map<JsName, String> reverse(
+      SortedMap<JsStringLiteral, JsName> toCreate) {
+    Map<JsName, String> reversed = new LinkedHashMap<JsName, String>(
+        toCreate.size());
+    for (Entry<JsStringLiteral, JsName> entry : toCreate.entrySet()) {
+      reversed.put(entry.getValue(), entry.getKey().getValue());
+    }
+    return reversed;
+  }
+
   /**
    * Utility class.
    */
diff --git a/dev/core/src/com/google/gwt/dev/js/SizeBreakdown.java b/dev/core/src/com/google/gwt/dev/js/SizeBreakdown.java
new file mode 100644
index 0000000..29aaf0b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/SizeBreakdown.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dev.js;
+
+import com.google.gwt.dev.js.ast.JsName;
+
+import java.util.Map;
+
+/**
+ * A size breakdown of a single JavaScript code fragment.
+ */
+public class SizeBreakdown {
+  private final int size;
+  private final Map<JsName, Integer> sizeMap;
+
+  public SizeBreakdown(int size, Map<JsName, Integer> sizeMap) {
+    this.size = size;
+    this.sizeMap = sizeMap;
+  }
+
+  public int getSize() {
+    return size;
+  }
+
+  public Map<JsName, Integer> getSizeMap() {
+    return sizeMap;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java
new file mode 100644
index 0000000..d8b78c9
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dev.util.arg;
+
+import com.google.gwt.util.tools.ArgHandlerFlag;
+
+/**
+ * An ArgHandler that enables detailed Story Of Your Compile data collection.
+ */
+public class ArgHandlerSoycDetailed extends ArgHandlerFlag {
+  private final OptionSoycDetailed options;
+
+  public ArgHandlerSoycDetailed(OptionSoycDetailed options) {
+    this.options = options;
+  }
+
+  @Override
+  public String getPurpose() {
+    return "Emit extra, detailed SOYC information at the expense of compile time";
+  }
+
+  @Override
+  public String getTag() {
+    return "-XsoycDetailed";
+  }
+
+  @Override
+  public boolean isUndocumented() {
+    return true;
+  }
+
+  @Override
+  public boolean setFlag() {
+    options.setSoycExtra(true);
+    return true;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionSoycDetailed.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionSoycDetailed.java
new file mode 100644
index 0000000..6ab0217
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionSoycDetailed.java
@@ -0,0 +1,33 @@
+/*
+ * 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.dev.util.arg;
+
+/**
+ * Option to request extra SOYC output at the expense of more compile time.
+ */
+public interface OptionSoycDetailed {
+
+  /**
+   * Returns true if the compiler should record and emit extra SOYC information.
+   */
+  boolean isSoycExtra();
+
+  /**
+   * Sets whether or not the compiler should record and emit extra SOYC
+   * information.
+   */
+  void setSoycExtra(boolean soycExtra);
+}
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java b/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java
index b05fead..3b9b451 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java
@@ -25,20 +25,15 @@
 public class CodeCollection {
 
   public TreeSet<String> classes = new TreeSet<String>();
-  public String codeType = "";
   public float cumPartialSize = 0f;
   public int cumSize = 0;
   public TreeSet<String> stories = new TreeSet<String>();
 
-  public CodeCollection(String type) {
-    codeType = type;
-  }
-
   public float getCumPartialSize(SizeBreakdown breakdown) {
     cumPartialSize = 0f;
     for (String className : classes) {
-      if (breakdown.classToPartialSize.containsKey(className)) {
-        cumPartialSize += breakdown.classToPartialSize.get(className);
+      if (breakdown.classToSize.containsKey(className)) {
+        cumPartialSize += breakdown.classToSize.get(className);
       }
     }
     return cumPartialSize;
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java b/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
index 0465d4c..3f18afa 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
@@ -30,29 +30,19 @@
 public class GlobalInformation {
   private static final SizeBreakdown[] EMPTY_SIZE_BREAKDOWN = new SizeBreakdown[0];
 
-  public static String backupLocation = "(Source location not known)";
-  
   public static Boolean displayDependencies = false;
   public static Boolean displaySplitPoints = false;
 
   public static HashMap<String, String> classToPackage = new HashMap<String, String>();
-  public static HashMap<String, HashSet<String>> classToWhatItDependsOn = new HashMap<String, HashSet<String>>();
-  public static HashMap<Integer, Float> fragmentToPartialSize = new HashMap<Integer, Float>();
-  public static HashMap<Integer, HashSet<String>> fragmentToStories = new HashMap<Integer, HashSet<String>>();
-
-  public static int numBytesDoubleCounted = 0;
-  public static int numFragments = 0;
-  public static int numSplitPoints = 0;
   public static Map<String, TreeSet<String>> packageToClasses = new TreeMap<String, TreeSet<String>>();
 
+  public static HashMap<String, HashSet<String>> classToWhatItDependsOn = new HashMap<String, HashSet<String>>();
+
+  public static int numSplitPoints = 0;
+
   public static HashMap<Integer, String> splitPointToLocation = new HashMap<Integer, String>();
   public static ArrayList<Integer> splitPointInitialLoadSequence = new ArrayList<Integer>();
 
-  public static HashMap<String, HashSet<String>> storiesToCorrClasses = new HashMap<String, HashSet<String>>();
-  public static HashMap<String, HashSet<String>> storiesToCorrClassesAndMethods = new HashMap<String, HashSet<String>>();
-
-  public static HashMap<String, String> storiesToLitType = new HashMap<String, String>();
-
   public static Settings settings = new Settings();
 
   public static SizeBreakdown initialCodeBreakdown = new SizeBreakdown(
@@ -82,13 +72,6 @@
     }
   }
 
-  public static void computePartialPackageSizes() {
-    for (SizeBreakdown breakdown : allSizeBreakdowns()) {
-      computePartialPackageSizes(breakdown.packageToPartialSize,
-          breakdown.classToPartialSize);
-    }
-  }
-
   public static SizeBreakdown splitPointCodeBreakdown(int sp) {
     assert sp >= 1 && sp <= numSplitPoints;
     if (!exclusiveCodeBreakdowns.containsKey(sp)) {
@@ -98,6 +81,9 @@
     return exclusiveCodeBreakdowns.get(sp);
   }
 
+  /**
+   * TODO(spoon) move this to the SizeBreakdown class.
+   */
   private static void computePackageSizes(Map<String, Integer> packageToSize,
       Map<String, Integer> classToSize) {
     packageToSize.clear();
@@ -112,23 +98,4 @@
       }
     }
   }
-
-  private static void computePartialPackageSizes(
-      Map<String, Float> packageToPartialSize,
-      Map<String, Float> classToPartialSize) {
-    float cumPartialSizeFromPackages = 0f;
-
-    packageToPartialSize.clear();
-    for (String packageName : packageToClasses.keySet()) {
-      packageToPartialSize.put(packageName, 0f);
-      for (String className : packageToClasses.get(packageName)) {
-        if (classToPartialSize.containsKey(className)) {
-          float curSize = classToPartialSize.get(className);
-          cumPartialSizeFromPackages += curSize;
-          float newSize = curSize + packageToPartialSize.get(packageName);
-          packageToPartialSize.put(packageName, newSize);
-        }
-      }
-    }
-  }
 }
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/LiteralsCollection.java b/tools/soyc-vis/src/com/google/gwt/soyc/LiteralsCollection.java
index 36a984f..18f2747 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/LiteralsCollection.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/LiteralsCollection.java
@@ -16,41 +16,13 @@
 
 package com.google.gwt.soyc;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.HashSet;
-import java.util.TreeMap;
+import java.util.Set;
+import java.util.TreeSet;
 
 /**
  * A collection of literals.
  */
 public class LiteralsCollection {
-  public int cumSize = 0;
-  public int cumStringSize = 0;
-  public String literalType = "";
-  public Map<String, HashSet<String>> literalToLocations = new TreeMap<String, HashSet<String>>();
-  public Map<String, HashSet<String>> storyToLocations = new HashMap<String, HashSet<String>>();
-  public TreeMap<String, String> stringLiteralToType = new TreeMap<String, String>();
-  public Map<String, Integer> stringTypeToSize = new HashMap<String, Integer>();
-  public Map<String, Integer> stringTypeToCount = new HashMap<String, Integer>();
-
-  public LiteralsCollection(String type) {
-    literalType = type;
-  }
-
-  /**
-   * Utility method.
-   */
-  public void printAllStrings() {
-    int iSum = 0;
-    System.out.println("--- now printing strings ---");
-    for (String st : stringLiteralToType.keySet()) {
-      int numBytes = st.getBytes().length;
-      iSum += numBytes;
-      System.out.println(st + "[" + numBytes + "]");
-    }
-    System.out.println("sum: " + iSum);
-    System.out.println("--- done printing strings ---");
-  }
-
+  public int size = 0;
+  public final Set<String> literals = new TreeSet<String>();
 }
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
index 41caf9b..2116795 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -43,12 +43,30 @@
  * A utility to make the top level HTTML file for one permutation.
  */
 public class MakeTopLevelHtmlForPerm {
+  /**
+   * A class that specifies the URL to which a class should be linked in a size
+   * breakdown. It is always either a dependencies page, an intermediate status
+   * page that itself links to dependencies pages, or <code>null</code>. If it's
+   * <code>null</code>, then no link should be created for the class.
+   */
+  public interface DependencyLinker {
+    String dependencyLinkForClass(String className);
+  }
+
+  /**
+   * A {@link DependencyLinker} for exclusive fragments. It doesn't link to any
+   * dependencies.
+   */
   public class DependencyLinkerForExclusiveFragment implements DependencyLinker {
     public String dependencyLinkForClass(String className) {
       return null;
     }
   }
 
+  /**
+   * A {@link DependencyLinker} for the initially downloaded code. It links into
+   * the initial dependencies.
+   */
   public class DependencyLinkerForInitialCode implements DependencyLinker {
     public String dependencyLinkForClass(String className) {
       String packageName = GlobalInformation.classToPackage.get(className);
@@ -57,22 +75,27 @@
     }
   }
 
+  /**
+   * A {@link DependencyLinker} for the leftovers fragment. It links a class to
+   * the leftovers status page for that class.
+   * 
+   */
   public class DependencyLinkerForLeftoversFragment implements DependencyLinker {
     public String dependencyLinkForClass(String className) {
       return leftoversStatusFileName(className);
     }
   }
 
+  /**
+   * A dependency linker for the size breakdown of the entire program. It links
+   * each class to a general split status page describing that class.
+   */
   public class DependencyLinkerForTotalBreakdown implements DependencyLinker {
     public String dependencyLinkForClass(String className) {
       return splitStatusFileName(className);
     }
   }
 
-  interface DependencyLinker {
-    String dependencyLinkForClass(String className);
-  }
-
   /**
    * By a convention shared with the compiler, the initial download is fragment
    * number 0.
@@ -279,7 +302,7 @@
     outFile.println("<body>");
 
     int yOffset = 0;
-    for (Float size : sortedCodeTypes.keySet()) {
+    for (float size : sortedCodeTypes.keySet()) {
 
       String codeType = sortedCodeTypes.get(size);
       String drillDownFileName = breakdown.getId() + "_" + codeType
@@ -306,7 +329,7 @@
 
       int yOffsetText = yOffset + 8;
       outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-          + "px; left:5px;\">%.1f</div>\n", size);
+          + "px; left:5px;\">%d</div>\n", (int) size);
       outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
           + "px; left:70px;\">%.1f", perc);
       outFile.println("%</div>\n");
@@ -379,41 +402,26 @@
     addHeaderWithBreakdownContext(breakdown, outFile);
 
     outFile.println("</center>");
-    outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
+    outFile.println("  <div style=\"width:100%;  float:left; padding-top: 10px;\">");
     outFile.println("<b>Package breakdown</b>");
     outFile.println("    </div>");
-    outFile.println("  <div style=\"width:48%;  float:right; padding-top: 10px; \">");
-    outFile.println("<b>Code type breakdown</b>");
-    outFile.println("    </div>");
 
-    outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
+    outFile.println("  <div style=\"width:100%;  float:left; padding-top: 10px;\">");
     outFile.println("<div style=\"width: 110px; float: left; font-size:16px;\">Size</div>");
     outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">Package Name</div>");
     outFile.println("    </div>");
 
-    outFile.println("  <div style=\"width:48%;  float:right; padding-top: 10px;\">");
-    outFile.println("<div style=\"width: 110px; float: left; font-size:16px;\">Size</div>");
-    outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">Code Type</div>");
-    outFile.println("    </div>");
-
     String packageBreakdownFileName = makePackageHtml(breakdown);
-    outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:left;\">");
+    outFile.println("<div style=\"height:35%; width:100%; margin:0 auto; background-color:white; float:left;\">");
     outFile.println("<iframe src=\"" + packageBreakdownFileName
         + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("  </div>");
 
-    String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown,
-        nameToCodeColl);
-    outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:right;\">");
-    outFile.println("<iframe src=\"" + codeTypeBreakdownFileName
-        + "\" width=100% height=100% scrolling=auto></iframe>");
-    outFile.println("  </div>");
-
     outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
     outFile.println("<b>Literals breakdown</b>");
     outFile.println("    </div>");
     outFile.println("  <div style=\"width:48%;  float:right; padding-top: 10px;  \">");
-    outFile.println("<b>String literals breakdown</b>");
+    outFile.println("<b>Code type breakdown</b>");
     outFile.println("    </div>");
 
     outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
@@ -423,7 +431,7 @@
 
     outFile.println("  <div style=\"width:48%;  float:right; padding-top: 10px; \">");
     outFile.println("<div style=\"width: 110px; float: left; font-size:16px;\">Size</div>");
-    outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">String Literal Type</div>");
+    outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">Code Type</div>");
     outFile.println("    </div>");
 
     String literalsBreakdownFileName = makeLiteralsHtml(breakdown,
@@ -433,10 +441,10 @@
         + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("</div>");
 
-    String stringLiteralsBreakdownFileName = makeStringLiteralsHtml(breakdown,
-        nameToLitColl);
+    String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown,
+        nameToCodeColl);
     outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:right;\">");
-    outFile.println("<iframe src=\"" + stringLiteralsBreakdownFileName
+    outFile.println("<iframe src=\"" + codeTypeBreakdownFileName
         + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("  </div>");
 
@@ -459,11 +467,11 @@
           Collections.reverseOrder());
 
       for (String className : nameToCodeColl.get(codeType).classes) {
-        if (breakdown.classToPartialSize.containsKey(className)) {
+        if (breakdown.classToSize.containsKey(className)) {
 
           float curSize = 0f;
-          if (breakdown.classToPartialSize.containsKey(className)) {
-            curSize = breakdown.classToPartialSize.get(className);
+          if (breakdown.classToSize.containsKey(className)) {
+            curSize = breakdown.classToSize.get(className);
           }
 
           if (curSize != 0f) {
@@ -502,7 +510,7 @@
       outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 120px; left:70px; position:absolute; background-color:white\"");
 
       int yOffset = 0;
-      for (Float size : sortedClasses.keySet()) {
+      for (float size : sortedClasses.keySet()) {
 
         String className = sortedClasses.get(size);
 
@@ -526,7 +534,7 @@
 
         int yOffsetText = yOffset + 8;
         outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-            + "px; left:5px;\">%.1f</div>\n", size);
+            + "px; left:5px;\">%d</div>\n", (int) size);
         outFile.println("<div class=\"barlabel\" style=\"top:" + yOffsetText
             + "px; left:70px;\">" + className + "</div>");
 
@@ -584,73 +592,29 @@
       outFile.println("<center>");
       outFile.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
 
-      for (String literal : nameToLitColl.get(literalType).literalToLocations.keySet()) {
-
-        if ((literalType.compareTo("string") != 0)
-            || (nameToLitColl.get("string").stringLiteralToType.get(literal).compareTo(
-                "otherStrings") != 0)) {
-
-          if (literal.trim().compareTo("") == 0) {
-            literal = "[whitespace only string]";
+      for (String literal : nameToLitColl.get(literalType).literals) {
+        String newLiteral = "";
+        if (literal.length() > 80) {
+          int i;
+          for (i = 80; i < literal.length(); i = i + 80) {
+            String part1 = literal.substring(i - 80, i);
+            newLiteral = newLiteral + part1 + " ";
           }
-
-          String newLiteral = "";
-          if (literal.length() > 80) {
-            int i;
-            for (i = 80; i < literal.length(); i = i + 80) {
-              String part1 = literal.substring(i - 80, i);
-              newLiteral = newLiteral + part1 + " ";
-            }
-            if (i - 80 > 0) {
-              newLiteral = newLiteral + literal.substring(i - 80);
-            }
-          } else {
-            newLiteral = literal;
+          if (i - 80 > 0) {
+            newLiteral = newLiteral + literal.substring(i - 80);
           }
-
-          String escliteral = escapeXml(newLiteral);
-
-          outFile.println("<tr>");
-          outFile.println("<td width=\"40%\">" + escliteral + "</td>");
-
-          int ct = 0;
-          if ((nameToLitColl.containsKey(literalType))
-              && (nameToLitColl.get(literalType).literalToLocations.containsKey(literal))) {
-            for (String location : nameToLitColl.get(literalType).literalToLocations.get(literal)) {
-
-              if (ct > 0) {
-                outFile.println("<tr>");
-                outFile.println("<td width=\"40%\"> </td>");
-              }
-
-              String newLocation = "";
-              if (location.length() > 80) {
-                int i;
-                for (i = 80; i < location.length(); i = i + 80) {
-                  String part1 = location.substring(i - 80, i);
-                  newLocation = newLocation + part1 + " ";
-                }
-                if (i - 80 > 0) {
-                  newLocation = newLocation + location.substring(i - 80);
-                }
-              } else {
-                newLocation = location;
-              }
-
-              outFile.println("<td width=\"40%\">" + newLocation + "</td>");
-
-              if (ct > 0) {
-                outFile.println("</tr>");
-              }
-              ct++;
-            }
-          } else {
-            System.err.println("either literalType " + literalType
-                + " not known, or no location for literal " + literal);
-          }
-
-          outFile.println("</tr>");
+        } else {
+          newLiteral = literal;
         }
+
+        if (newLiteral.trim().length() == 0) {
+          newLiteral = "[whitespace only string]";
+        }
+        String escliteral = escapeXml(newLiteral);
+
+        outFile.println("<tr>");
+        outFile.println("<td>" + escliteral + "</td>");
+        outFile.println("</tr>");
       }
 
       outFile.println("</table>");
@@ -677,10 +641,10 @@
       for (String className : GlobalInformation.packageToClasses.get(packageName)) {
 
         float curSize = 0f;
-        if (!breakdown.classToPartialSize.containsKey(className)) {
+        if (!breakdown.classToSize.containsKey(className)) {
           // This class not present in this code collection
         } else {
-          curSize = breakdown.classToPartialSize.get(className);
+          curSize = breakdown.classToSize.get(className);
         }
 
         int depCount = 0;
@@ -722,7 +686,7 @@
       outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 120px; left:70px; position:absolute; background-color:white\"");
 
       int yOffset = 0;
-      for (Float size : sortedClasses.keySet()) {
+      for (float size : sortedClasses.keySet()) {
 
         String className = sortedClasses.get(size);
 
@@ -774,7 +738,7 @@
 
         int yOffsetText = yOffset + 8;
         outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-            + "px; left:5px;\">%.1f</div>\n", size);
+            + "px; left:5px;\">%d</div>\n", (int) size);
         if (GlobalInformation.displayDependencies == true) {
           String dependencyLink = depLinker.dependencyLinkForClass(className);
           outFile.print("<div class=\"barlabel\" style=\"top:" + yOffsetText
@@ -811,121 +775,6 @@
     }
   }
 
-  public void makeStringLiteralsClassesTableHtmls(SizeBreakdown breakdown)
-      throws IOException {
-    Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
-
-    for (String literalType : nameToLitColl.get("string").stringTypeToSize.keySet()) {
-      String outFileName = literalType + "Strings.html";
-      final PrintWriter outFile = new PrintWriter(getOutFile(breakdown.getId()
-          + "_" + outFileName));
-
-      String title = "Literals of type \"" + literalType + "\"";
-      String header = headerLineForBreakdown(breakdown);
-
-      addStandardHtmlProlog(outFile, title, header);
-
-      if (literalType.compareTo("otherStrings") == 0) {
-        outFile.println("<center>");
-        outFile.println("(JavaScript variable names are not displayed.)");
-        outFile.println("</center>");
-      }
-
-      else {
-        outFile.println("<center>");
-        outFile.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
-
-        for (String literal : nameToLitColl.get("string").stringLiteralToType.keySet()) {
-
-          if (nameToLitColl.get("string").stringLiteralToType.get(literal).compareTo(
-              literalType) == 0) {
-
-            if (literal.trim().compareTo("") == 0) {
-              literal = "[whitespace only string]";
-            }
-
-            String newLiteral = "";
-            if (literal.length() > 80) {
-              int i;
-              for (i = 80; i < literal.length(); i = i + 80) {
-                String part1 = literal.substring(i - 80, i);
-                newLiteral = newLiteral + part1 + " ";
-              }
-              if (i - 80 > 0) {
-                newLiteral = newLiteral + literal.substring(i - 80);
-              }
-            } else {
-              newLiteral = literal;
-            }
-
-            String escliteral = escapeXml(newLiteral);
-
-            outFile.println("<tr>");
-            outFile.println("<td width=\"40%\">" + escliteral + "</td>");
-
-            int ct = 0;
-
-            if (nameToLitColl.get("string").literalToLocations.containsKey(literal)) {
-
-              for (String location : nameToLitColl.get("string").literalToLocations.get(literal)) {
-
-                if ((location.indexOf("Line 0") == -1)
-                    && (location.compareTo(GlobalInformation.backupLocation) != 0)) { // i.e.,
-                  // if
-                  // we
-                  // actually
-                  // know
-                  // the
-                  // location
-                  if (ct > 0) {
-                    outFile.println("<tr>");
-                    outFile.println("<td width=\"40%\"> </td>");
-                  }
-
-                  String newLocation = "";
-                  if (location.length() > 80) {
-                    int i;
-                    for (i = 80; i < location.length(); i = i + 80) {
-                      String part1 = location.substring(i - 80, i);
-                      newLocation = newLocation + part1 + " ";
-                    }
-                    if (i - 80 > 0) {
-                      newLocation = newLocation + location.substring(i - 80);
-                    }
-                  } else {
-                    newLocation = location;
-                  }
-
-                  outFile.println("<td width=\"40%\"> " + newLocation
-                      + " </td>");
-
-                  if (ct > 0) {
-                    outFile.println("</tr>");
-                  }
-                  ct++;
-                }
-              }
-              if (ct == 0) {
-                outFile.println("<td width=\"40%\"> "
-                    + GlobalInformation.backupLocation + " </td>");
-              }
-            } else {
-              System.err.println("no location given for string literal: "
-                  + literal);
-            }
-            outFile.println("</tr>");
-          }
-        }
-
-        outFile.println("</table>");
-        outFile.println("<center>");
-      }
-
-      addStandardHtmlEnding(outFile);
-      outFile.close();
-    }
-  }
-
   public void makeTopLevelShell() throws IOException {
     PrintWriter outFile = new PrintWriter(
         getOutFile("SoycDashboard-index.html"));
@@ -947,7 +796,7 @@
     outFile.println("<center>");
     outFile.println("<h3>Story of Your Compile Dashboard</h3>");
     outFile.println("<hr>");
-    if (GlobalInformation.fragmentToStories.size() > 1) {
+    if (GlobalInformation.splitPointToLocation.size() > 1) {
       outFile.println("<b>Initial download size: <span style=\"color:maroon\">"
           + GlobalInformation.initialCodeBreakdown.sizeAllCode
           + "</span></span></b>");
@@ -971,7 +820,7 @@
     int outerHeight = 25 * numRows;
     outFile.println("<div style=\"width:100%; margin:20px 0 20px 0; background-color:white;position:relative;height:"
         + outerHeight + "\">");
-    float maxSize = GlobalInformation.totalCodeBreakdown.sizeAllCode;
+    int maxSize = GlobalInformation.totalCodeBreakdown.sizeAllCode;
 
     int yOffset = 0;
     for (int i = FRAGMENT_NUMBER_TOTAL_PROGRAM; i <= numSplitPoints + 1; i++) {
@@ -994,13 +843,8 @@
       String drillDownFileName = shellFileName(breakdown);
       String splitPointDescription = breakdown.getDescription();
 
-      float size;
-      if (i >= 0) {
-        size = GlobalInformation.fragmentToPartialSize.get(i);
-      } else {
-        size = GlobalInformation.totalCodeBreakdown.sizeAllCode;
-      }
-      float ratio = (size / maxSize) * 79;
+      int size = breakdown.sizeAllCode;
+      float ratio = (1.0F * size / maxSize) * 79;
       if (ratio < 3) {
         ratio = 3;
       }
@@ -1019,7 +863,7 @@
 
       int yOffsetText = yOffset + 8;
       outFile.printf("<div class=\"barlabel\" style=\"top: " + yOffsetText
-          + "px; left:5px;\">%.1f</div>\n", size);
+          + "px; left:5px;\">%d</div>\n", size);
       outFile.println("<div class=\"barlabel\" style=\"top: " + yOffsetText
           + "px; left:120px;\"><a href=\"" + drillDownFileName
           + "\" target=\"_top\">" + splitPointDescription + "</a></div>");
@@ -1181,7 +1025,7 @@
         Collections.reverseOrder());
 
     for (String literal : nameToLitColl.keySet()) {
-      float curSize = nameToLitColl.get(literal).cumSize;
+      float curSize = nameToLitColl.get(literal).size;
       sumSize += curSize;
 
       if (curSize != 0f) {
@@ -1205,7 +1049,7 @@
     outFile.println("<body>");
 
     int yOffset = 0;
-    for (Float size : sortedLitTypes.keySet()) {
+    for (float size : sortedLitTypes.keySet()) {
 
       String literal = sortedLitTypes.get(size);
       String drillDownFileName = breakdown.getId() + "_" + literal
@@ -1232,7 +1076,7 @@
 
       int yOffsetText = yOffset + 8;
       outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-          + "px; left:5px;\">%.1f</div>\n", size);
+          + "px; left:5px;\">%d</div>\n", (int) size);
       outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
           + "px; left:70px;\">%.1f", perc);
       outFile.println("%</div>\n");
@@ -1252,8 +1096,8 @@
   private String makePackageHtml(SizeBreakdown breakdown)
       throws FileNotFoundException {
     String outFileName = breakdown.getId() + "_" + "packageBreakdown.html";
-    Map<String, Float> packageToPartialSize = breakdown.packageToPartialSize;
-    TreeMap<Float, String> sortedPackages = new TreeMap<Float, String>(
+    Map<String, Integer> packageToPartialSize = breakdown.packageToSize;
+    TreeMap<Integer, String> sortedPackages = new TreeMap<Integer, String>(
         Collections.reverseOrder());
     float maxSize = 0f;
     float sumSize = 0f;
@@ -1278,7 +1122,7 @@
     outFile.println("<body>");
 
     int yOffset = 0;
-    for (Float size : sortedPackages.keySet()) {
+    for (int size : sortedPackages.keySet()) {
       String packageName = sortedPackages.get(size);
       String drillDownFileName = classesInPackageFileName(breakdown,
           packageName);
@@ -1305,7 +1149,7 @@
 
       int yOffsetText = yOffset + 8;
       outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-          + "px; left:5px;\">%.1f</div>\n", size);
+          + "px; left:5px;\">%d</div>\n", size);
       outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
           + "px; left:70px;\">%.1f", perc);
       outFile.println("%</div>\n");
@@ -1352,89 +1196,6 @@
     out.close();
   }
 
-  private String makeStringLiteralsHtml(SizeBreakdown breakdown,
-      Map<String, LiteralsCollection> nameToLitColl) throws IOException {
-    String outFileName = breakdown.getId() + "_stringLiteralsBreakdown.html";
-    final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
-
-    outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
-    outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
-    outFile.println("<html>");
-    outFile.println("<head>");
-    outFile.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
-    outFile.println("<link rel=\"stylesheet\" href=\"roundedCorners.css\" media=\"screen\">");
-    outFile.println("</head>");
-    outFile.println("<body>");
-
-    if (nameToLitColl.get("string").stringTypeToSize.size() > 0) {
-
-      float maxSize = 0f;
-      float sumSize = 0f;
-      TreeMap<Float, String> sortedStLitTypes = new TreeMap<Float, String>(
-          Collections.reverseOrder());
-
-      for (String stringLiteral : nameToLitColl.get("string").stringTypeToSize.keySet()) {
-        float curSize = nameToLitColl.get("string").stringTypeToSize.get(stringLiteral);
-        sumSize += curSize;
-
-        if (curSize != 0f) {
-          sortedStLitTypes.put(curSize, stringLiteral);
-
-          if (curSize > maxSize) {
-            maxSize = curSize;
-          }
-        }
-      }
-
-      int yOffset = 0;
-      for (Float size : sortedStLitTypes.keySet()) {
-
-        String stringLiteral = sortedStLitTypes.get(size);
-        String drillDownFileName = breakdown.getId() + "_" + stringLiteral
-            + "Strings.html";
-
-        float ratio = (size / maxSize) * 79;
-        float perc = (size / sumSize) * 100;
-
-        if (ratio < 3) {
-          ratio = 3;
-        }
-
-        outFile.println("<div id=\"box\" style=\"width:" + ratio + "%; top: "
-            + yOffset + "px; left: 110px;\">");
-        outFile.println("<div id=\"lb\">");
-        outFile.println("<div id=\"rb\">");
-        outFile.println("<div id=\"bb\"><div id=\"blc\"><div id=\"brc\">");
-        outFile.println("<div id=\"tb\"><div id=\"tlc\"><div id=\"trc\">");
-        outFile.println("<div id=\"content\">");
-        outFile.println("</div>");
-        outFile.println("</div></div></div></div>");
-        outFile.println("</div></div></div></div>");
-        outFile.println("</div>");
-
-        int yOffsetText = yOffset + 8;
-        outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-            + "px; left:5px;\">%.1f</div>\n", size);
-        outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText
-            + "px; left:70px;\">%.1f", perc);
-        outFile.println("%</div>\n");
-        outFile.println("<div class=\"barlabel\" style=\"top:" + yOffsetText
-            + "px; left:110px;\"><a href=\"" + drillDownFileName
-            + "\" target=\"_top\">" + stringLiteral + "</a></div>");
-
-        yOffset = yOffset + 25;
-      }
-    } else {
-      outFile.println("No string literals found for this application.");
-    }
-
-    outFile.println("</body>");
-    outFile.println("</html>");
-    outFile.close();
-
-    return outFileName;
-  }
-
   /**
    * Find which split points include code belonging to <code>className</code>.
    */
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java b/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java
index e260dd4..86b00b4 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java
@@ -15,11 +15,8 @@
  */
 package com.google.gwt.soyc;
 
-import com.google.gwt.dev.util.collect.HashSet;
-
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 import java.util.TreeMap;
 
 /**
@@ -28,38 +25,34 @@
 public class SizeBreakdown {
   private static void initializeLiteralsCollection(
       Map<String, LiteralsCollection> nameToLitColl) {
-    nameToLitColl.put("long", new LiteralsCollection("long"));
-    nameToLitColl.put("null", new LiteralsCollection("null"));
-    nameToLitColl.put("class", new LiteralsCollection("class"));
-    nameToLitColl.put("int", new LiteralsCollection("int"));
-    nameToLitColl.put("string", new LiteralsCollection("string"));
-    nameToLitColl.put("number", new LiteralsCollection("number"));
-    nameToLitColl.put("boolean", new LiteralsCollection("boolean"));
-    nameToLitColl.put("double", new LiteralsCollection("double"));
-    nameToLitColl.put("char", new LiteralsCollection("char"));
-    nameToLitColl.put("undefined", new LiteralsCollection("undefined"));
-    nameToLitColl.put("float", new LiteralsCollection("float"));
+    nameToLitColl.put("long", new LiteralsCollection());
+    nameToLitColl.put("null", new LiteralsCollection());
+    nameToLitColl.put("class", new LiteralsCollection());
+    nameToLitColl.put("int", new LiteralsCollection());
+    nameToLitColl.put("string", new LiteralsCollection());
+    nameToLitColl.put("number", new LiteralsCollection());
+    nameToLitColl.put("boolean", new LiteralsCollection());
+    nameToLitColl.put("double", new LiteralsCollection());
+    nameToLitColl.put("char", new LiteralsCollection());
+    nameToLitColl.put("undefined", new LiteralsCollection());
+    nameToLitColl.put("float", new LiteralsCollection());
   }
 
   private static void initializeNameToCodeCollection(
       HashMap<String, CodeCollection> nameToCodeColl) {
-    nameToCodeColl.put("allOther", new CodeCollection("allOther"));
-    nameToCodeColl.put("widget", new CodeCollection("widget"));
-    nameToCodeColl.put("rpcUser", new CodeCollection("rpcUser"));
-    nameToCodeColl.put("rpcGen", new CodeCollection("rpcGen"));
-    nameToCodeColl.put("rpcGwt", new CodeCollection("rpcGwt"));
-    nameToCodeColl.put("gwtLang", new CodeCollection("long"));
-    nameToCodeColl.put("jre", new CodeCollection("jre"));
+    nameToCodeColl.put("allOther", new CodeCollection());
+    nameToCodeColl.put("widget", new CodeCollection());
+    nameToCodeColl.put("rpcUser", new CodeCollection());
+    nameToCodeColl.put("rpcGen", new CodeCollection());
+    nameToCodeColl.put("rpcGwt", new CodeCollection());
+    nameToCodeColl.put("gwtLang", new CodeCollection());
+    nameToCodeColl.put("jre", new CodeCollection());
   }
 
-  public Map<String, Float> classToPartialSize = new HashMap<String, Float>();
   public Map<String, Integer> classToSize = new HashMap<String, Integer>();
-  public Map<String, LiteralsCollection> nameToLitColl = new TreeMap<String, LiteralsCollection>();
-  public int nonAttributedBytes = 0;
-  public Set<String> nonAttributedStories = new HashSet<String>();;
-  public Map<String, Float> packageToPartialSize = new HashMap<String, Float>();
-  public Map<String, Integer> packageToSize = new HashMap<String, Integer>();
   public HashMap<String, CodeCollection> nameToCodeColl = new HashMap<String, CodeCollection>();
+  public Map<String, LiteralsCollection> nameToLitColl = new TreeMap<String, LiteralsCollection>();
+  public Map<String, Integer> packageToSize = new HashMap<String, Integer>();
 
   public int sizeAllCode;
 
@@ -87,7 +80,7 @@
   public String getId() {
     return id;
   }
-  
+
   @Override
   public String toString() {
     return getId();
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java b/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
index 95308a3..d4db8c9 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
@@ -31,7 +31,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
@@ -48,9 +47,17 @@
  */
 public class SoycDashboard {
   private static class FormatException extends RuntimeException {
+    public FormatException() {
+      super();
+    }
+
     public FormatException(String message) {
       super(message);
     }
+
+    public FormatException(Throwable cause) {
+      super(cause);
+    }
   }
 
   public static void main(String[] args) {
@@ -113,7 +120,7 @@
        */
 
       // make the parser handler
-      DefaultHandler handler = parseXMLDocument();
+      DefaultHandler handler = parseXMLDocumentSizeMap();
 
       // start parsing
       SAXParserFactory factoryMain = SAXParserFactory.newInstance();
@@ -133,7 +140,6 @@
 
       // now we need to aggregate numbers
       GlobalInformation.computePackageSizes();
-      GlobalInformation.computePartialPackageSizes();
 
       // clean up the RPC categories
       for (SizeBreakdown breakdown : GlobalInformation.allSizeBreakdowns()) {
@@ -172,6 +178,22 @@
     }
   }
 
+  private static Collection<SizeBreakdown> breakdownsForFragment(
+      Integer fragment) {
+    List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>();
+    breakdowns.add(GlobalInformation.totalCodeBreakdown);
+    if (fragment == 0) {
+      breakdowns.add(GlobalInformation.initialCodeBreakdown);
+    }
+    if (fragment == (GlobalInformation.numSplitPoints + 1)) {
+      breakdowns.add(GlobalInformation.leftoversBreakdown);
+    }
+    if (fragment >= 1 && fragment <= GlobalInformation.numSplitPoints) {
+      breakdowns.add(GlobalInformation.splitPointCodeBreakdown(fragment));
+    }
+    return breakdowns;
+  }
+
   private static DependencyLinker chooseDependencyLinker(
       MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, SizeBreakdown breakdown) {
     if (breakdown == GlobalInformation.totalCodeBreakdown) {
@@ -228,520 +250,10 @@
     makeTopLevelHtmlForPerm.makePackageClassesHtmls(breakdown, depLinker);
     makeTopLevelHtmlForPerm.makeCodeTypeClassesHtmls(breakdown);
     makeTopLevelHtmlForPerm.makeLiteralsClassesTableHtmls(breakdown);
-    makeTopLevelHtmlForPerm.makeStringLiteralsClassesTableHtmls(breakdown);
     makeTopLevelHtmlForPerm.makeBreakdownShell(breakdown);
     makeTopLevelHtmlForPerm.makeTopLevelShell();
   }
 
-  private static DefaultHandler parseXMLDocument() {
-    DefaultHandler handler = new DefaultHandler() {
-      String curClassId;
-      Integer curFragment;
-      String curLineNumber;
-      String curLocation;
-      HashSet<String> curRelevantCodeTypes = new HashSet<String>();
-      HashSet<String> curRelevantLitTypes = new HashSet<String>();
-      String curStoryId;
-      String curStoryLiteralType;
-      String curStoryRef;
-      boolean specialCodeType = false;
-      StringBuilder valueBuilder = new StringBuilder();
-
-      /**
-       * This method collects a block of the value of the current XML node that
-       * the SAX parser parses. It simply adds to the the previous blocks, so
-       * that we can collect the entire value block.
-       */
-      @Override
-      public void characters(char ch[], int start, int length) {
-        valueBuilder.append(ch, start, length);
-      }
-
-      /**
-       * This method marks the end of an XML element that the SAX parser parses.
-       * It has access to the full value of the node and uses it to add
-       * information to the relevant literal or code collections.
-       * 
-       * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
-       *      java.lang.String, java.lang.String)
-       */
-      @Override
-      public void endElement(String nsUri, String strippedName, String qName) {
-
-        if (strippedName.compareTo("storyref") == 0) {
-          String value = valueBuilder.toString();
-
-          int numBytes = currentStorySize();
-          if (curStoryRef != null) {
-            if (!GlobalInformation.fragmentToPartialSize.containsKey(curFragment)) {
-              GlobalInformation.fragmentToPartialSize.put(curFragment,
-                  (float) numBytes);
-            } else {
-              float newSize = GlobalInformation.fragmentToPartialSize.get(curFragment)
-                  + numBytes;
-              GlobalInformation.fragmentToPartialSize.put(curFragment, newSize);
-            }
-
-            for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-              breakdown.sizeAllCode += numBytes;
-            }
-
-            // add this size to the classes associated with it
-            if (GlobalInformation.storiesToCorrClasses.containsKey(curStoryRef)) {
-
-              if ((GlobalInformation.storiesToLitType.containsKey(curStoryRef))
-                  && (GlobalInformation.storiesToCorrClasses.get(curStoryRef).size() > 0)) {
-                GlobalInformation.numBytesDoubleCounted += numBytes;
-              }
-
-              for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-                accountForCurrentStory(breakdown.nameToCodeColl, breakdown);
-              }
-            }
-
-            for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-              updateLitTypes(breakdown.nameToLitColl, value, numBytes);
-            }
-          }
-        }
-      }
-
-      /**
-       * This method deals with the beginning of the XML element. It analyzes
-       * the XML node and adds its information to the relevant literal or code
-       * collection for later analysis.
-       * 
-       * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
-       *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
-       */
-      @Override
-      public void startElement(String nsUri, String strippedName,
-          String tagName, Attributes attributes) {
-        valueBuilder.delete(0, valueBuilder.length());
-
-        if (strippedName.compareTo("story") == 0) {
-          parseStory(attributes);
-        } else if (strippedName.compareTo("of") == 0) {
-          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-            parseOverrides(breakdown.nameToCodeColl, attributes);
-          }
-        } else if (strippedName.compareTo("by") == 0) {
-          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-            parseCorrelations(breakdown.nameToCodeColl, attributes);
-          }
-        } else if (strippedName.compareTo("origin") == 0) {
-          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-            parseOrigins(breakdown.nameToLitColl, attributes);
-          }
-        } else if (strippedName.compareTo("js") == 0) {
-          if (attributes.getValue("fragment") != null) {
-            curFragment = Integer.parseInt(attributes.getValue("fragment"));
-          } else {
-            curFragment = -2;
-          }
-        } else if (strippedName.compareTo("storyref") == 0) {
-          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-            parseJs(breakdown.nameToLitColl, breakdown.nameToCodeColl,
-                attributes, curFragment);
-          }
-        }
-      }
-
-      private void accountForCurrentStory(
-          final HashMap<String, CodeCollection> nameToCodeColl,
-          SizeBreakdown breakdown) {
-        int storySize = currentStorySize();
-        if ((!GlobalInformation.storiesToLitType.containsKey(curStoryRef))
-            && (!GlobalInformation.storiesToCorrClasses.containsKey(curStoryRef))) {
-          breakdown.nonAttributedStories.add(curStoryRef);
-          breakdown.nonAttributedBytes += storySize;
-        }
-
-        // go through all the classes for this story
-        for (String className : GlobalInformation.storiesToCorrClasses.get(curStoryRef)) {
-          // get the corresponding package
-
-          String packageName = "";
-
-          if (!GlobalInformation.classToPackage.containsKey(className)) {
-            // derive the package name from the class
-            packageName = className;
-            packageName = packageName.replaceAll("\\.[A-Z].*", "");
-            GlobalInformation.classToPackage.put(className, packageName);
-          } else {
-            packageName = GlobalInformation.classToPackage.get(className);
-          }
-          parseClass(nameToCodeColl, className, packageName);
-
-          if (!GlobalInformation.packageToClasses.containsKey(packageName)) {
-            TreeSet<String> insertSet = new TreeSet<String>();
-            insertSet.add(className);
-            GlobalInformation.packageToClasses.put(packageName, insertSet);
-          } else {
-            GlobalInformation.packageToClasses.get(packageName).add(className);
-          }
-
-          if (breakdown.classToSize.containsKey(className)) {
-            int newSize = breakdown.classToSize.get(className) + storySize;
-            breakdown.classToSize.put(className, newSize);
-          } else {
-            breakdown.classToSize.put(className, storySize);
-          }
-
-          if (breakdown.classToPartialSize.containsKey(className)) {
-            float newSize = breakdown.classToPartialSize.get(className)
-                + currentStoryPartialSize();
-            breakdown.classToPartialSize.put(className, newSize);
-          } else {
-            breakdown.classToPartialSize.put(className,
-                currentStoryPartialSize());
-          }
-        }
-      }
-
-      private Collection<SizeBreakdown> breakdownsForCurFragment() {
-        List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>();
-        breakdowns.add(GlobalInformation.totalCodeBreakdown);
-        if (curFragment == 0) {
-          breakdowns.add(GlobalInformation.initialCodeBreakdown);
-        }
-        if (curFragment == (GlobalInformation.numSplitPoints + 1)) {
-          breakdowns.add(GlobalInformation.leftoversBreakdown);
-        }
-        if (curFragment >= 1 && curFragment <= GlobalInformation.numSplitPoints) {
-          breakdowns.add(GlobalInformation.splitPointCodeBreakdown(curFragment));
-        }
-        return breakdowns;
-      }
-
-      private float currentStoryPartialSize() {
-        return (float) currentStorySize()
-            / (float) GlobalInformation.storiesToCorrClasses.get(curStoryRef).size();
-      }
-
-      private int currentStorySize() {
-        return valueBuilder.toString().getBytes().length;
-      }
-
-      /*
-       * parses the "class" portion of the XML file
-       */
-      private void parseClass(
-          final HashMap<String, CodeCollection> nameToCodeColl,
-          String curClassId, String curPackage) {
-        // if (attributes.getValue("id") != null) {
-        // curClassId = attributes.getValue("id");
-
-        // GlobalInformation.classToPackage.put(curClassId, curPackage);
-
-        if (curPackage.startsWith("java")) {
-          nameToCodeColl.get("jre").classes.add(curClassId);
-        } else if (curPackage.startsWith("com.google.gwt.lang")) {
-          nameToCodeColl.get("gwtLang").classes.add(curClassId);
-        }
-        if (curClassId.contains("_CustomFieldSerializer")) {
-          nameToCodeColl.get("rpcUser").classes.add(curClassId);
-        } else if (curClassId.endsWith("_FieldSerializer")
-            || curClassId.endsWith("_Proxy")
-            || curClassId.endsWith("_TypeSerializer")) {
-          nameToCodeColl.get("rpcGen").classes.add(curClassId);
-        }
-        // }
-      }
-
-      /*
-       * parses the "correlations" portion of the XML file
-       */
-      private void parseCorrelations(
-          final HashMap<String, CodeCollection> nameToCodeColl,
-          Attributes attributes) {
-
-        if (attributes.getValue("idref") != null) {
-
-          String corrClassOrMethod = attributes.getValue("idref");
-          String corrClass = attributes.getValue("idref");
-
-          if (corrClass.contains(":")) {
-            corrClass = corrClass.replaceAll(":.*", "");
-          }
-
-          if (!GlobalInformation.storiesToCorrClassesAndMethods.containsKey(curStoryId)) {
-            HashSet<String> insertSet = new HashSet<String>();
-            insertSet.add(corrClassOrMethod);
-            GlobalInformation.storiesToCorrClassesAndMethods.put(curStoryId,
-                insertSet);
-          } else {
-            GlobalInformation.storiesToCorrClassesAndMethods.get(curStoryId).add(
-                corrClassOrMethod);
-          }
-
-          if (!GlobalInformation.storiesToCorrClasses.containsKey(curStoryId)) {
-            HashSet<String> insertSet = new HashSet<String>();
-            insertSet.add(corrClass);
-            GlobalInformation.storiesToCorrClasses.put(curStoryId, insertSet);
-          } else {
-            GlobalInformation.storiesToCorrClasses.get(curStoryId).add(
-                corrClass);
-          }
-
-          for (String codeType : nameToCodeColl.keySet()) {
-            if (nameToCodeColl.get(codeType).classes.contains(corrClass)) {
-              nameToCodeColl.get(codeType).stories.add(curStoryId);
-            }
-          }
-        }
-      }
-
-      /*
-       * parses the "JS" portion of the XML file
-       */
-      private void parseJs(final Map<String, LiteralsCollection> nameToLitColl,
-          final HashMap<String, CodeCollection> nameToCodeColl,
-          Attributes attributes, Integer curFragment) {
-        curRelevantLitTypes.clear();
-        curRelevantCodeTypes.clear();
-
-        if (attributes.getValue("idref") != null) {
-
-          curStoryRef = attributes.getValue("idref");
-
-          if (curFragment != -1) {
-            // add this to the stories for this fragment
-            if (!GlobalInformation.fragmentToStories.containsKey(curFragment)) {
-              HashSet<String> insertSet = new HashSet<String>();
-              insertSet.add(curStoryRef);
-              GlobalInformation.fragmentToStories.put(curFragment, insertSet);
-            } else {
-              GlobalInformation.fragmentToStories.get(curFragment).add(
-                  curStoryRef);
-            }
-          }
-
-          for (String litType : nameToLitColl.keySet()) {
-            if (nameToLitColl.get(litType).storyToLocations.containsKey(curStoryRef)) {
-              curRelevantLitTypes.add(litType);
-            }
-          }
-
-          specialCodeType = false;
-          for (String codeType : nameToCodeColl.keySet()) {
-            if (nameToCodeColl.get(codeType).stories.contains(curStoryRef)) {
-              curRelevantCodeTypes.add(codeType);
-              specialCodeType = true;
-            }
-          }
-          if (specialCodeType == false) {
-
-            nameToCodeColl.get("allOther").stories.add(curStoryRef);
-            curRelevantCodeTypes.add("allOther");
-          }
-        }
-      }
-
-      /*
-       * parses the "origins" portion of the XML file
-       */
-      private void parseOrigins(
-          final Map<String, LiteralsCollection> nameToLitColl,
-          Attributes attributes) {
-        if ((curStoryLiteralType.compareTo("") != 0)
-            && (attributes.getValue("lineNumber") != null)
-            && (attributes.getValue("location") != null)) {
-          curLineNumber = attributes.getValue("lineNumber");
-          curLocation = attributes.getValue("location");
-          String curOrigin = curLocation + ": Line " + curLineNumber;
-
-          if (!nameToLitColl.get(curStoryLiteralType).storyToLocations.containsKey(curStoryId)) {
-            HashSet<String> insertSet = new HashSet<String>();
-            insertSet.add(curOrigin);
-            nameToLitColl.get(curStoryLiteralType).storyToLocations.put(
-                curStoryId, insertSet);
-          } else {
-            nameToLitColl.get(curStoryLiteralType).storyToLocations.get(
-                curStoryId).add(curOrigin);
-          }
-        }
-      }
-
-      /*
-       * parses the "overrides" portion of the XML file
-       */
-      private void parseOverrides(
-          final HashMap<String, CodeCollection> nameToCodeColl,
-          Attributes attributes) {
-        if (attributes.getValue("idref") != null) {
-          String overriddenClass = attributes.getValue("idref");
-
-          // we either generalize to classes, or the
-          // numbers are messed up...
-          if (overriddenClass.contains(":")) {
-            overriddenClass = overriddenClass.replaceAll(":.*", "");
-          }
-
-          if (overriddenClass.compareTo("com.google.gwt.user.client.ui.UIObject") == 0) {
-            nameToCodeColl.get("widget").classes.add(curClassId);
-          } else if (overriddenClass.contains("java.io.Serializable")
-              || overriddenClass.contains("IsSerializable")) {
-            nameToCodeColl.get("rpcUser").classes.add(curClassId);
-          } else if (overriddenClass.contains("com.google.gwt.user.client.rpc.core.java")) {
-            nameToCodeColl.get("rpcGwt").classes.add(curClassId);
-          }
-        }
-      }
-
-      /*
-       * parses the "story" portion of the XML file
-       */
-      private void parseStory(Attributes attributes) {
-        if (attributes.getValue("id") != null) {
-          curStoryId = attributes.getValue("id");
-          if (attributes.getValue("literal") != null) {
-            curStoryLiteralType = attributes.getValue("literal");
-            GlobalInformation.storiesToLitType.put(curStoryId,
-                curStoryLiteralType);
-            for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
-              if (!breakdown.nameToLitColl.get(curStoryLiteralType).storyToLocations.containsKey(curStoryId)) {
-                HashSet<String> insertSet = new HashSet<String>();
-                breakdown.nameToLitColl.get(curStoryLiteralType).storyToLocations.put(
-                    curStoryId, insertSet);
-              }
-            }
-          } else {
-            curStoryLiteralType = "";
-          }
-        }
-      }
-
-      /*
-       * This method assigns strings to the appropriate category
-       */
-      private void updateLitTypes(
-          final Map<String, LiteralsCollection> nameToLitColl, String value,
-          int numBytes) {
-
-        int iNumCounted = 0;
-
-        for (String relLitType : curRelevantLitTypes) {
-          iNumCounted++;
-
-          // then give string literals special treatment
-          if (relLitType.compareTo("string") == 0) {
-
-            // note that this will double-count (i.e., it will count a string
-            // twice if it's in the output twice), as it should.
-            nameToLitColl.get("string").cumStringSize += numBytes;
-            nameToLitColl.get(relLitType).cumSize += numBytes;
-
-            // get the origins
-            HashSet<String> originSet = nameToLitColl.get("string").storyToLocations.get(curStoryRef);
-
-            // find the most appropriate string literal category
-            String mostAppropriateCategory = "";
-            String mostAppropriateLocation = "";
-            String backupLocation = "";
-            for (String origin : originSet) {
-
-              if ((origin.contains("ClassLiteralHolder"))
-                  && (mostAppropriateCategory.compareTo("") == 0)) {
-                mostAppropriateCategory = "compiler";
-                mostAppropriateLocation = origin;
-              } else if ((origin.startsWith("transient source for"))
-                  && (origin.contains("_TypeSerializer"))
-                  && (mostAppropriateCategory.compareTo("") == 0)) {
-                mostAppropriateCategory = "transient";
-                mostAppropriateLocation = origin;
-              } else if ((origin.contains("InlineResourceBundleGenerator"))
-                  && (mostAppropriateCategory.compareTo("") == 0)) {
-                mostAppropriateCategory = "inlinedTextRes";
-                mostAppropriateLocation = origin;
-              }
-              if (origin.compareTo("com.google.gwt.dev.js.ast.JsProgram: Line 0") != 0) {
-                backupLocation = origin;
-              }
-            }
-
-            if (backupLocation.compareTo("") == 0) {
-              backupLocation = GlobalInformation.backupLocation;
-            }
-            if ((((value.startsWith("'")) && (value.endsWith("'"))) || ((value.startsWith("\"")) && (value.endsWith("\""))))
-                && (mostAppropriateCategory.compareTo("") == 0)) {
-              mostAppropriateCategory = "user";
-              mostAppropriateLocation = backupLocation;
-            } else if (mostAppropriateCategory.compareTo("") == 0) {
-              mostAppropriateCategory = "otherStrings";
-              mostAppropriateLocation = backupLocation;
-            }
-
-            if (!nameToLitColl.get("string").stringLiteralToType.containsKey(value)) {
-              nameToLitColl.get("string").stringLiteralToType.put(value,
-                  mostAppropriateCategory);
-              if (!nameToLitColl.get("string").stringTypeToCount.containsKey(mostAppropriateCategory)) {
-                nameToLitColl.get("string").stringTypeToCount.put(
-                    mostAppropriateCategory, 1);
-              } else {
-                int iNewCount = nameToLitColl.get("string").stringTypeToCount.get(mostAppropriateCategory) + 1;
-                nameToLitColl.get("string").stringTypeToCount.put(
-                    mostAppropriateCategory, iNewCount);
-              }
-
-              int iNewSize = numBytes;
-              if (nameToLitColl.get("string").stringTypeToSize.containsKey(mostAppropriateCategory)) {
-                iNewSize += nameToLitColl.get("string").stringTypeToSize.get(mostAppropriateCategory);
-              }
-              nameToLitColl.get("string").stringTypeToSize.put(
-                  mostAppropriateCategory, iNewSize);
-
-              if (nameToLitColl.get("string").storyToLocations.containsKey(curStoryRef)) {
-                HashSet<String> insertSet = new HashSet<String>();
-                insertSet.add(mostAppropriateLocation);
-                nameToLitColl.get(relLitType).literalToLocations.put(value,
-                    insertSet);
-              }
-            }
-          } else {
-            // note that this will double-count (i.e., it will count a literal
-            // twice if it's in the output twice), as it should.
-            nameToLitColl.get(relLitType).cumSize += numBytes;
-
-            if (nameToLitColl.get(relLitType).storyToLocations.containsKey(curStoryRef)) {
-              if (nameToLitColl.get(relLitType).literalToLocations.containsKey(value)) {
-                nameToLitColl.get(relLitType).literalToLocations.get(value).addAll(
-                    nameToLitColl.get(relLitType).storyToLocations.get(curStoryRef));
-              } else {
-                HashSet<String> insertSet = nameToLitColl.get(relLitType).storyToLocations.get(curStoryRef);
-                nameToLitColl.get(relLitType).literalToLocations.put(value,
-                    insertSet);
-              }
-            }
-          }
-        }
-      }
-
-      /*
-       * parses the "depends on" portion of the XML file
-       */
-      /*
-       * private void parseDependsOn( final HashMap<String, CodeCollection>
-       * nameToCodeColl, Attributes attributes) { if
-       * (curFunctionId.compareTo("") == 0) { if (attributes.getValue("idref")
-       * != null) { String curDepClassId = attributes.getValue("idref");
-       * 
-       * if (curDepClassId.contains(":")) { // strip everything after the :: (to
-       * get to class, even if it's a // method) curDepClassId =
-       * curDepClassId.replaceAll(":.*", ""); }
-       * 
-       * if (curDepClassId.contains(".")) { if
-       * (!GlobalInformation.classToWhatItDependsOn.containsKey(curClassId)) {
-       * HashSet<String> insertSet = new HashSet<String>();
-       * insertSet.add(curDepClassId);
-       * GlobalInformation.classToWhatItDependsOn.put(curClassId, insertSet); }
-       * else { GlobalInformation.classToWhatItDependsOn.get(curClassId).add(
-       * curDepClassId); } } } } }
-       */
-    };
-    return handler;
-  }
-
   private static DefaultHandler parseXMLDocumentDependencies(
       final Map<String, Map<String, String>> allDependencies) {
     DefaultHandler handler = new DefaultHandler() {
@@ -799,6 +311,143 @@
     return handler;
   }
 
+  private static DefaultHandler parseXMLDocumentSizeMap() {
+    return new DefaultHandler() {
+      int fragment = -1;
+
+      @Override
+      public void endElement(String uri, String localName, String qName) {
+        if (localName.compareTo("sizemap") == 0) {
+          fragment = -1;
+        }
+      }
+
+      @Override
+      public void startElement(String uri, String localName, String qName,
+          Attributes attributes) {
+        if (localName.compareTo("sizemap") == 0) {
+          // starting a new size map
+          String fragString = attributes.getValue("fragment");
+          if (fragString == null) {
+            throw new FormatException();
+          }
+          try {
+            fragment = Integer.valueOf(fragString);
+          } catch (NumberFormatException e) {
+            throw new FormatException(e);
+          }
+          String sizeString = attributes.getValue("size");
+          if (sizeString == null) {
+            throw new FormatException();
+          }
+          int size;
+          try {
+            size = Integer.valueOf(sizeString);
+          } catch (NumberFormatException e) {
+            throw new FormatException(e);
+          }
+          for (SizeBreakdown breakdown : breakdownsForFragment(fragment)) {
+            breakdown.sizeAllCode += size;
+          }
+        } else if (localName.compareTo("size") == 0) {
+          String type = attributes.getValue("type");
+          if (type == null) {
+            throw new FormatException();
+          }
+          String ref = attributes.getValue("ref");
+          if (ref == null) {
+            throw new FormatException();
+          }
+          String sizeString = attributes.getValue("size");
+          if (sizeString == null) {
+            throw new FormatException();
+          }
+          int size;
+          try {
+            size = Integer.valueOf(sizeString);
+          } catch (NumberFormatException e) {
+            throw new FormatException(e);
+          }
+          recordSize(type, ref, size);
+        }
+      }
+
+      private void accountForSize(SizeBreakdown breakdown, String refType,
+          String ref, int size) {
+        if (refType.equals("string")) {
+          LiteralsCollection stringLiterals = breakdown.nameToLitColl.get("string");
+          stringLiterals.size += size;
+          stringLiterals.literals.add(ref);
+        } else if (refType.equals("var")) {
+          // Nothing to record, because no breakdown is provided for random
+          // variables
+        } else {
+          if (!refType.equals("type") && !refType.equals("method")) {
+            throw new FormatException();
+          }
+          String className = ref;
+          if (className.contains("::")) {
+            /*
+             * It's a method reference. Discard the method part.
+             */
+            int idx = className.indexOf(':');
+            className = className.substring(0, idx);
+          }
+
+          // derive the package name from the class
+          String packageName;
+          if (!GlobalInformation.classToPackage.containsKey(className)) {
+            packageName = className;
+            packageName = packageName.replaceAll("\\.[A-Z].*", "");
+            GlobalInformation.classToPackage.put(className, packageName);
+          } else {
+            packageName = GlobalInformation.classToPackage.get(className);
+          }
+          if (!GlobalInformation.packageToClasses.containsKey(packageName)) {
+            TreeSet<String> insertSet = new TreeSet<String>();
+            insertSet.add(className);
+            GlobalInformation.packageToClasses.put(packageName, insertSet);
+          } else {
+            GlobalInformation.packageToClasses.get(packageName).add(className);
+          }
+
+          recordClassCategories(breakdown.nameToCodeColl, className,
+              packageName);
+
+          if (breakdown.classToSize.containsKey(className)) {
+            int newSize = breakdown.classToSize.get(className) + size;
+            breakdown.classToSize.put(className, newSize);
+          } else {
+            breakdown.classToSize.put(className, size);
+          }
+        }
+      }
+
+      private void recordClassCategories(
+          final HashMap<String, CodeCollection> nameToCodeColl,
+          String className, String packageName) {
+        if (packageName.startsWith("java")) {
+          nameToCodeColl.get("jre").classes.add(className);
+        } else if (packageName.startsWith("com.google.gwt.lang")) {
+          nameToCodeColl.get("gwtLang").classes.add(className);
+        }
+        if (className.contains("_CustomFieldSerializer")) {
+          nameToCodeColl.get("rpcUser").classes.add(className);
+        } else if (className.endsWith("_FieldSerializer")
+            || className.endsWith("_Proxy")
+            || className.endsWith("_TypeSerializer")) {
+          nameToCodeColl.get("rpcGen").classes.add(className);
+        }
+      }
+
+      private void recordSize(String refType, String ref, int size) {
+        for (SizeBreakdown breakdown : breakdownsForFragment(fragment)) {
+          accountForSize(breakdown, refType, ref, size);
+        }
+      }
+    };
+  }
+
   private static DefaultHandler parseXMLDocumentSplitPoints() {
 
     DefaultHandler handler = new DefaultHandler() {