Decouple JsProgram; this allows the JS AST to serialize in pieces.

1) A rethinking of how JsScope should work.  Most notably, the JsRootScope is a singleton with special serialization.  I also refactored bits and pieces of the JsScope hierarchy to get rid of fields and code that don't make sense for particular subclasses.  And I broke serialization circular references (that pull in the whole world) by not having scopes serialize their children.

2) I got JsProgram out of the business of federating literals and singletons. This is much more like how the Java AST works now.

http://gwt-code-reviews.appspot.com/1342801/show


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9683 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
index 9decb5a..3888f9d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
@@ -114,11 +114,6 @@
     }
 
     @Override
-    public JsProgram program() {
-      return func.getScope().getProgram();
-    }
-
-    @Override
     public String toString() {
       return func.toString();
     }
@@ -251,8 +246,8 @@
     int jsLine = info.getStartLine()
         + countLines(indexes, info.getStartPos(), absoluteJsStartPos);
 
-    SourceInfo jsInfo = SourceOrigin.create(jsStartPos, jsEndPos, jsLine,
-        info.getFileName());
+    SourceInfo jsInfo = jsProgram.createSourceInfo(jsStartPos, jsEndPos,
+        jsLine, info.getFileName());
     try {
       List<JsStatement> result = JsParser.parse(jsInfo, jsProgram.getScope(),
           sr);
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java b/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java
index fd0c87b..616ff10 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniMethod.java
@@ -16,7 +16,6 @@
 package com.google.gwt.dev.javac;
 
 import com.google.gwt.dev.js.ast.JsFunction;
-import com.google.gwt.dev.js.ast.JsProgram;
 
 /**
  * Represents a single JsniMethod in a compiled class file.
@@ -53,9 +52,4 @@
    * The parameter names.
    */
   public abstract String[] paramNames();
-
-  /**
-   * Gets the JsProgram in which this method is located.
-   */
-  public abstract JsProgram program();
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java b/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java
index be0fbfb..b280c37 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java
@@ -40,6 +40,16 @@
    * A dummy factory that always returns <code>null</code>.
    */
   public static final class DummyCorrelationFactory extends CorrelationFactory {
+    public static final CorrelationFactory INSTANCE = new DummyCorrelationFactory();
+
+    private DummyCorrelationFactory() {
+    }
+
+    @Override
+    public Correlation by(JDeclaredType type) {
+      return null;
+    }
+
     @Override
     public Correlation by(JField field) {
       return null;
@@ -51,11 +61,6 @@
     }
 
     @Override
-    public Correlation by(JDeclaredType type) {
-      return null;
-    }
-
-    @Override
     public Correlation by(JsFunction function) {
       return null;
     }
@@ -99,6 +104,8 @@
      * public-API consumers of the Correlation.
      */
 
+    public static final CorrelationFactory INSTANCE = new RealCorrelationFactory();
+
     /**
      * Correlations based on Literals are all the same, so we'll just cook up a
      * Map to make {@link #by(Literal)} fast.
@@ -108,8 +115,8 @@
 
     static {
       for (Literal l : Literal.values()) {
-        LITERAL_CORRELATIONS.put(l, new Correlation(Axis.LITERAL,
-            l.getDescription(), l));
+        LITERAL_CORRELATIONS.put(l,
+            new Correlation(Axis.LITERAL, l.getDescription(), l));
       }
     }
 
@@ -132,6 +139,19 @@
     private final Map<Object, Correlation> canonicalMap = Collections.synchronizedMap(new ReferenceMap(
         ReferenceMap.WEAK, ReferenceMap.WEAK));
 
+    private RealCorrelationFactory() {
+    }
+
+    @Override
+    public Correlation by(JDeclaredType type) {
+      Correlation toReturn = canonicalMap.get(type);
+      if (toReturn == null) {
+        toReturn = new Correlation(Axis.CLASS, type.getName(), type);
+        canonicalMap.put(type, toReturn);
+      }
+      return toReturn;
+    }
+
     @Override
     public Correlation by(JField field) {
       Correlation toReturn = canonicalMap.get(field);
@@ -155,16 +175,6 @@
     }
 
     @Override
-    public Correlation by(JDeclaredType type) {
-      Correlation toReturn = canonicalMap.get(type);
-      if (toReturn == null) {
-        toReturn = new Correlation(Axis.CLASS, type.getName(), type);
-        canonicalMap.put(type, toReturn);
-      }
-      return toReturn;
-    }
-
-    @Override
     public Correlation by(JsFunction function) {
       Correlation toReturn = canonicalMap.get(function);
       if (toReturn == null) {
@@ -221,12 +231,12 @@
     }
   }
 
+  public abstract Correlation by(JDeclaredType type);
+
   public abstract Correlation by(JField field);
 
   public abstract Correlation by(JMethod method);
 
-  public abstract Correlation by(JDeclaredType type);
-
   public abstract Correlation by(JsFunction function);
 
   public abstract Correlation by(JsName name);
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 a50d4a1..13fce81 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -541,7 +541,7 @@
     checkForErrors(logger, goldenCuds, false);
 
     CorrelationFactory correlator = options.isSoycExtra()
-        ? new RealCorrelationFactory() : new DummyCorrelationFactory();
+        ? RealCorrelationFactory.INSTANCE : DummyCorrelationFactory.INSTANCE;
     JProgram jprogram = new JProgram(correlator);
     JsProgram jsProgram = new JsProgram(correlator);
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
index a34a2b0..ccaf1c4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
@@ -49,6 +49,11 @@
    */
   List<Correlation> getAllCorrelations(Axis axis);
 
+  /**
+   * Returns the correlation factory that created this node.
+   */
+  CorrelationFactory getCorrelationFactory();
+  
   int getEndPos();
 
   String getFileName();
@@ -64,11 +69,11 @@
    * has been set.
    */
   Set<Correlation> getPrimaryCorrelations();
-  
+
   /**
    * Returns the first Correlations added along each Axis on which a Correlation
-   * has been set.  Some entries may be null and should be ignored.  The
-   * returned array must not be modified.
+   * has been set. Some entries may be null and should be ignored. The returned
+   * array must not be modified.
    */
   Correlation[] getPrimaryCorrelationsArray();
 
@@ -93,6 +98,13 @@
   SourceInfo makeChild(Class<?> caller, String description, SourceInfo... merge);
 
   /**
+   * Create a child node of the same type as this node, but with a new Origin.
+   * If data accumulation is enabled, the derived node will inherit its
+   * Correlations from this node.
+   */
+  SourceInfo makeChild(SourceOrigin origin);
+
+  /**
    * Add additional ancestor SourceInfos. These SourceInfo objects indicate that
    * a merge-type operation took place or that the additional ancestors have a
    * containment relationship with the SourceInfo.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java b/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java
index ab985f1..9176f89 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs;
 
 import com.google.gwt.dev.jjs.Correlation.Axis;
+import com.google.gwt.dev.jjs.CorrelationFactory.RealCorrelationFactory;
 
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -36,7 +37,7 @@
    * Micro-opt for {@link #makeChild(Class, String)}.
    */
   private static final SourceInfo[] EMPTY_SOURCEINFO_ARRAY = new SourceInfo[0];
-  
+
   private static final int numCorrelationAxes = Axis.values().length;
 
   private static int numCorrelationAxes() {
@@ -66,18 +67,17 @@
     primaryCorrelations = new Correlation[numCorrelationAxes()];
   }
 
+  private SourceInfoCorrelation(SourceInfoCorrelation parent,
+      SourceOrigin origin) {
+    this.origin = origin;
+    this.allCorrelations = new ArrayList<Correlation>(parent.allCorrelations);
+    primaryCorrelations = parent.primaryCorrelations.clone();
+  }
+
   private SourceInfoCorrelation(SourceInfoCorrelation parent, String caller,
       SourceInfo... additionalAncestors) {
-    assert parent != null;
+    this(parent, parent.origin);
     assert caller != null;
-    this.origin = parent.origin;
-
-    this.allCorrelations = new ArrayList<Correlation>(parent.allCorrelations);
-    primaryCorrelations = new Correlation[numCorrelationAxes()];
-    for (int i = 0; i < numCorrelationAxes(); i++) {
-      primaryCorrelations[i] = parent.primaryCorrelations[i];
-    }
-
     merge(additionalAncestors);
   }
 
@@ -134,6 +134,11 @@
     return toReturn;
   }
 
+  @Override
+  public CorrelationFactory getCorrelationFactory() {
+    return RealCorrelationFactory.INSTANCE;
+  }
+
   public int getEndPos() {
     return getOrigin().getEndPos();
   }
@@ -167,7 +172,7 @@
     }
     return toReturn;
   }
-  
+
   public Correlation[] getPrimaryCorrelationsArray() {
     return primaryCorrelations;
   }
@@ -202,6 +207,11 @@
     return new SourceInfoCorrelation(this, callerName, merge);
   }
 
+  @Override
+  public SourceInfo makeChild(SourceOrigin origin) {
+    return new SourceInfoCorrelation(this, origin);
+  }
+
   /**
    * Add additional ancestor SourceInfos. These SourceInfo objects indicate that
    * a merge-type operation took place or that the additional ancestors have a
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
index 5e666d7..555c503 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs;
 
 import com.google.gwt.dev.jjs.Correlation.Axis;
+import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
 import com.google.gwt.dev.util.StringInterner;
 
 import java.util.Collections;
@@ -56,7 +57,7 @@
     public int getStartPos() {
       return startPos;
     }
-    
+
     // super.equals and hashCode call getStartPos() and getEndPos(),
     // so there is no need to implement them in this subclass
   }
@@ -148,6 +149,11 @@
     return Collections.emptyList();
   }
 
+  @Override
+  public CorrelationFactory getCorrelationFactory() {
+    return DummyCorrelationFactory.INSTANCE;
+  }
+
   public int getEndPos() {
     return -1;
   }
@@ -167,7 +173,7 @@
   public Set<Correlation> getPrimaryCorrelations() {
     return Collections.emptySet();
   }
-  
+
   public Correlation[] getPrimaryCorrelationsArray() {
     return new Correlation[0];
   }
@@ -195,6 +201,11 @@
     return this;
   }
 
+  @Override
+  public SourceInfo makeChild(SourceOrigin origin) {
+    return origin;
+  }
+
   public void merge(SourceInfo... sourceInfos) {
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index a4ca996..020c6d6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -15,11 +15,12 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.Correlation.Literal;
 import com.google.gwt.dev.jjs.CorrelationFactory;
+import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.SourceOrigin;
-import com.google.gwt.dev.jjs.Correlation.Literal;
 import com.google.gwt.dev.jjs.ast.JField.Disposition;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
 import com.google.gwt.dev.jjs.ast.js.JsonObject;
@@ -107,7 +108,7 @@
   private static final int IS_NULL = 0;
 
   private static final Map<String, JPrimitiveType> primitiveTypes = new HashMap<String, JPrimitiveType>();
-  
+
   @Deprecated
   private static final Map<String, JPrimitiveType> primitiveTypesDeprecated = new HashMap<String, JPrimitiveType>();
 
@@ -398,7 +399,7 @@
   private JClassType typeString;
 
   public JProgram() {
-    this(new CorrelationFactory.DummyCorrelationFactory());
+    this(DummyCorrelationFactory.INSTANCE);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index 42bc89c..e2221ba 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 Google Inc.
- *
+ * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * 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
@@ -110,7 +110,7 @@
    * corresponding JNode for each created method and variable. 4) Maps all
    * synthetic arguments and fields for nested and local classes. 5) Slurps in
    * JSNI code for native methods as an opaque string.
-   *
+   * 
    * Note that methods and fields are not added to their classes here, that
    * isn't done until {@link GenerateJavaAST}.
    */
@@ -292,13 +292,10 @@
 
     private SourceInfo makeSourceInfo(AbstractMethodDeclaration methodDecl,
         HasSourceInfo enclosing) {
-      CompilationResult compResult = methodDecl.compilationResult;
-      int[] indexes = compResult.lineSeparatorPositions;
-      String fileName = String.valueOf(compResult.fileName);
-      int startLine = Util.getLineNumber(methodDecl.sourceStart, indexes, 0,
-          indexes.length - 1);
+      int startLine = Util.getLineNumber(methodDecl.sourceStart,
+          currentSeparatorPositions, 0, currentSeparatorPositions.length - 1);
       SourceInfo toReturn = program.createSourceInfo(methodDecl.sourceStart,
-          methodDecl.bodyEnd, startLine, fileName);
+          methodDecl.bodyEnd, startLine, currentFileName);
 
       // The SourceInfo will inherit Correlations from its enclosing object
       if (enclosing != null) {
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 88c152e..502aa35 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
@@ -538,7 +538,7 @@
        * An empty JsVars seems possibly surprising; return a true empty
        * statement instead.
        */
-      return jsprogram.getEmptyStmt();
+      return new JsEmpty(stat.getSourceInfo());
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index 105d0f2..4c04e90 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -2995,8 +2995,7 @@
           JLiteral initializer = field.getConstInitializer();
           JType type = initializer.getType();
           if (type instanceof JPrimitiveType || program.isJavaLangString(type)) {
-            GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals(
-                jsProgram);
+            GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals();
             generator.accept(initializer);
             JsExpression result = generator.peek();
             assert (result != null);
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 c878a3b..84de28d 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
@@ -105,6 +105,7 @@
 import com.google.gwt.dev.js.ast.JsContinue;
 import com.google.gwt.dev.js.ast.JsDefault;
 import com.google.gwt.dev.js.ast.JsDoWhile;
+import com.google.gwt.dev.js.ast.JsEmpty;
 import com.google.gwt.dev.js.ast.JsExprStmt;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFor;
@@ -118,6 +119,7 @@
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsNew;
 import com.google.gwt.dev.js.ast.JsNode;
+import com.google.gwt.dev.js.ast.JsNormalScope;
 import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsParameter;
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
@@ -125,6 +127,7 @@
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsPropertyInitializer;
 import com.google.gwt.dev.js.ast.JsReturn;
+import com.google.gwt.dev.js.ast.JsRootScope;
 import com.google.gwt.dev.js.ast.JsScope;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsSwitch;
@@ -295,7 +298,7 @@
         if (parentScope == objectScope) {
           parentScope = interfaceScope;
         }
-        myScope = new JsScope(parentScope, "class " + x.getShortName());
+        myScope = new JsNormalScope(parentScope, "class " + x.getShortName());
       }
       classScopes.put(x, myScope);
 
@@ -532,10 +535,6 @@
       arrayLength.setObfuscatable(false);
     }
 
-    public GenerateJavaScriptVisitor() {
-      super(jsProgram);
-    }
-
     @Override
     public void endVisit(JAbsentArrayDimension x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
@@ -594,7 +593,7 @@
       Iterator<JsStatement> iterator = stmts.iterator();
       while (iterator.hasNext()) {
         JsStatement stmt = iterator.next();
-        if (stmt == jsProgram.getEmptyStmt()) {
+        if (stmt instanceof JsEmpty) {
           iterator.remove();
         }
       }
@@ -755,7 +754,7 @@
       if (x.getBody() != null) {
         stmt.setBody((JsStatement) pop()); // body
       } else {
-        stmt.setBody(jsProgram.getEmptyStmt());
+        stmt.setBody(new JsEmpty(x.getSourceInfo()));
       }
       stmt.setCondition((JsExpression) pop()); // testExpr
       push(stmt);
@@ -843,7 +842,7 @@
       if (x.getBody() != null) {
         jsFor.setBody((JsStatement) pop());
       } else {
-        jsFor.setBody(jsProgram.getEmptyStmt());
+        jsFor.setBody(new JsEmpty(x.getSourceInfo()));
       }
 
       // increments
@@ -892,7 +891,7 @@
       if (x.getThenStmt() != null) {
         stmt.setThenStmt((JsStatement) pop()); // thenStmt
       } else {
-        stmt.setThenStmt(jsProgram.getEmptyStmt());
+        stmt.setThenStmt(new JsEmpty(x.getSourceInfo()));
       }
 
       stmt.setIfExpr((JsExpression) pop()); // ifExpr
@@ -1092,7 +1091,8 @@
       }
       if (cur == null) {
         // the multi-expression was empty; use undefined
-        cur = jsProgram.getUndefinedLiteral();
+        cur = new JsNameRef(x.getSourceInfo(),
+            JsRootScope.INSTANCE.getUndefined());
       }
       push(cur);
     }
@@ -1284,7 +1284,7 @@
       if (x.getBody() != null) {
         stmt.setBody((JsStatement) pop()); // body
       } else {
-        stmt.setBody(jsProgram.getEmptyStmt());
+        stmt.setBody(new JsEmpty(x.getSourceInfo()));
       }
       stmt.setCondition((JsExpression) pop()); // testExpr
       push(stmt);
@@ -1593,8 +1593,7 @@
       SourceInfo sourceInfo = program.createSourceInfoSynthetic(
           GenerateJavaScriptAST.class, "gwtOnLoad");
 
-      JsName entryName = topScope.findExistingName("$entry");
-      entryName.setObfuscatable(true);
+      JsName entryName = topScope.declareName("$entry");
       JsVar entryVar = new JsVar(sourceInfo, entryName);
       JsInvocation registerEntryCall = new JsInvocation(sourceInfo);
       JsFunction registerEntryFunction = indexedFunctions.get("Impl.registerEntry");
@@ -1746,8 +1745,8 @@
          * primitive with a modified prototype.
          */
         JsNameRef rhs = prototype.makeRef(sourceInfo);
-        rhs.setQualifier(jsProgram.getRootScope().declareName("String").makeRef(
-            sourceInfo));
+        rhs.setQualifier(JsRootScope.INSTANCE.findExistingUnobfuscatableName(
+            "String").makeRef(sourceInfo));
         JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
             rhs);
         JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
@@ -2114,7 +2113,7 @@
     this.jsProgram = jsProgram;
     topScope = jsProgram.getScope();
     objectScope = jsProgram.getObjectScope();
-    interfaceScope = new JsScope(objectScope, "Interfaces");
+    interfaceScope = new JsNormalScope(objectScope, "Interfaces");
     this.output = output;
     this.symbolTable = symbolTable;
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
index e9c55c2..fb50d79 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
@@ -26,11 +26,14 @@
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.js.ast.JsBooleanLiteral;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsNullLiteral;
+import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.js.ast.JsObjectLiteral;
-import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsPropertyInitializer;
+import com.google.gwt.dev.js.ast.JsStringLiteral;
 import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.lang.LongLib;
 
@@ -44,36 +47,31 @@
  */
 public class GenerateJavaScriptLiterals extends JVisitor {
 
-  private final JsProgram program;
   private final Stack<JsVisitable> nodeStack = new Stack<JsVisitable>();
 
-  public GenerateJavaScriptLiterals(JsProgram program) {
-    this.program = program;
-  }
-
   @Override
   public final void endVisit(JBooleanLiteral x, Context ctx) {
-    push(x.getValue() ? program.getTrueLiteral() : program.getFalseLiteral());
+    push(JsBooleanLiteral.get(x.getValue()));
   }
 
   @Override
   public final void endVisit(JCharLiteral x, Context ctx) {
-    push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
+    push(new JsNumberLiteral(x.getSourceInfo(), x.getValue()));
   }
 
   @Override
   public final void endVisit(JDoubleLiteral x, Context ctx) {
-    push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
+    push(new JsNumberLiteral(x.getSourceInfo(), x.getValue()));
   }
 
   @Override
   public final void endVisit(JFloatLiteral x, Context ctx) {
-    push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
+    push(new JsNumberLiteral(x.getSourceInfo(), x.getValue()));
   }
 
   @Override
   public final void endVisit(JIntLiteral x, Context ctx) {
-    push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
+    push(new JsNumberLiteral(x.getSourceInfo(), x.getValue()));
   }
 
   @Override
@@ -85,9 +83,9 @@
     JsExpression label0 = new JsNameRef(sourceInfo, "l");
     JsExpression label1 = new JsNameRef(sourceInfo, "m");
     JsExpression label2 = new JsNameRef(sourceInfo, "h");
-    JsExpression value0 = program.getNumberLiteral(sourceInfo, intArray[0]);
-    JsExpression value1 = program.getNumberLiteral(sourceInfo, intArray[1]);
-    JsExpression value2 = program.getNumberLiteral(sourceInfo, intArray[2]);
+    JsExpression value0 = new JsNumberLiteral(sourceInfo, intArray[0]);
+    JsExpression value1 = new JsNumberLiteral(sourceInfo, intArray[1]);
+    JsExpression value2 = new JsNumberLiteral(sourceInfo, intArray[2]);
     inits.add(new JsPropertyInitializer(sourceInfo, label0, value0));
     inits.add(new JsPropertyInitializer(sourceInfo, label1, value1));
     inits.add(new JsPropertyInitializer(sourceInfo, label2, value2));
@@ -96,12 +94,12 @@
 
   @Override
   public final void endVisit(JNullLiteral x, Context ctx) {
-    push(program.getNullLiteral());
+    push(JsNullLiteral.INSTANCE);
   }
 
   @Override
   public final void endVisit(JStringLiteral x, Context ctx) {
-    push(program.getStringLiteral(x.getSourceInfo(), x.getValue()));
+    push(new JsStringLiteral(x.getSourceInfo(), x.getValue()));
   }
 
   @SuppressWarnings("unchecked")
diff --git a/dev/core/src/com/google/gwt/dev/js/JsInliner.java b/dev/core/src/com/google/gwt/dev/js/JsInliner.java
index 5b56c97..aa97813 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsInliner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsInliner.java
@@ -26,9 +26,11 @@
 import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsBooleanLiteral;
 import com.google.gwt.dev.js.ast.JsCase;
+import com.google.gwt.dev.js.ast.JsCatchScope;
 import com.google.gwt.dev.js.ast.JsConditional;
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsDefault;
+import com.google.gwt.dev.js.ast.JsEmpty;
 import com.google.gwt.dev.js.ast.JsExprStmt;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFor;
@@ -48,9 +50,9 @@
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
 import com.google.gwt.dev.js.ast.JsPrefixOperation;
 import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsProgramFragment;
 import com.google.gwt.dev.js.ast.JsRegExp;
 import com.google.gwt.dev.js.ast.JsReturn;
+import com.google.gwt.dev.js.ast.JsRootScope;
 import com.google.gwt.dev.js.ast.JsScope;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsStringLiteral;
@@ -89,11 +91,9 @@
    */
   private static class AffectedBySideEffectsVisitor extends JsVisitor {
     private boolean affectedBySideEffects;
-    private final JsProgram program;
     private final JsScope safeScope;
 
-    public AffectedBySideEffectsVisitor(JsProgram program, JsScope safeScope) {
-      this.program = program;
+    public AffectedBySideEffectsVisitor(JsScope safeScope) {
       this.safeScope = safeScope;
     }
 
@@ -125,7 +125,7 @@
     public void endVisit(JsNameRef x, JsContext ctx) {
       if (x.getQualifier() == null && x.getName() != null) {
         // Special case the undefined literal.
-        if (x.getName() == program.getUndefinedLiteral().getName()) {
+        if (x.getName() == JsRootScope.INSTANCE.getUndefined()) {
           return;
         }
         // Locals in a safe scope are unaffected.
@@ -423,7 +423,7 @@
             return false;
           } else {
             // The return value from an XO function is never used
-            ctx.replaceMe(program.getNullLiteral());
+            ctx.replaceMe(JsNullLiteral.INSTANCE);
             return false;
           }
 
@@ -489,7 +489,7 @@
         if (ctx.canRemove()) {
           ctx.removeMe();
         } else {
-          ctx.replaceMe(program.getEmptyStmt());
+          ctx.replaceMe(new JsEmpty(x.getSourceInfo()));
         }
         return false;
 
@@ -604,8 +604,11 @@
    * invocations occur.
    */
   private static class EvaluationOrderVisitor extends JsVisitor {
-    public static final JsName THIS_NAME = (new JsScope("fake scope") {
-    }).declareName("this");
+    /**
+     * A dummy name to represent 'this' refs.
+     */
+    public static final JsName THIS_NAME = new JsCatchScope(
+        JsRootScope.INSTANCE, "this").getAllNames().next();
 
     private boolean maintainsOrder = true;
     private final List<JsName> toEvaluate;
@@ -777,7 +780,6 @@
     private final Stack<JsFunction> functionStack = new Stack<JsFunction>();
     private final InvocationCountingVisitor invocationCountingVisitor = new InvocationCountingVisitor();
     private final Stack<List<JsName>> newLocalVariableStack = new Stack<List<JsName>>();
-    private final JsProgram program;
 
     /**
      * A map containing the next integer to try as an identifier suffix for a
@@ -791,7 +793,6 @@
     private JsFunction programFunction;
 
     public InliningVisitor(JsProgram program) {
-      this.program = program;
       invocationCountingVisitor.accept(program);
     }
 
@@ -856,7 +857,7 @@
         if (ctx.canRemove()) {
           ctx.removeMe();
         } else {
-          ctx.replaceMe(program.getEmptyStmt());
+          ctx.replaceMe(new JsEmpty(x.getSourceInfo()));
         }
 
       } else if (x.getExpression() != statements.get(0).getExpression()) {
@@ -969,7 +970,7 @@
     }
 
     @Override
-    public void endVisit(JsProgramFragment x, JsContext ctx) {
+    public void endVisit(JsProgram x, JsContext ctx) {
       if (!functionStack.pop().equals(programFunction)) {
         throw new InternalCompilerException("Unexpected function popped");
       }
@@ -1003,9 +1004,8 @@
      * top-level of the program.
      */
     @Override
-    public boolean visit(JsProgramFragment x, JsContext ctx) {
-      programFunction = new JsFunction(program.getSourceInfo(),
-          program.getScope());
+    public boolean visit(JsProgram x, JsContext ctx) {
+      programFunction = new JsFunction(x.getSourceInfo(), x.getScope());
       programFunction.setBody(new JsBlock(x.getSourceInfo()));
       functionStack.push(programFunction);
       newLocalVariableStack.push(new ArrayList<JsName>());
@@ -1097,8 +1097,7 @@
          * distinct objects, it would not be possible to substitute different
          * JsNameRefs at different call sites.
          */
-        JsExpression h = hoistedExpression(program, statement,
-            localVariableNames);
+        JsExpression h = hoistedExpression(statement, localVariableNames);
         if (h == null) {
           return x;
         }
@@ -1117,7 +1116,8 @@
        * JsExprStmt.
        */
       if (!sawReturnStatement) {
-        hoisted.add(program.getUndefinedLiteral());
+        hoisted.add(new JsNameRef(x.getSourceInfo(),
+            JsRootScope.INSTANCE.getUndefined()));
       }
 
       assert (hoisted.size() > 0);
@@ -1141,7 +1141,7 @@
       }
 
       // Confirm that the expression conforms to the desired heuristics
-      if (!isInlinable(program, callerFunction, invokedFunction, thisExpr,
+      if (!isInlinable(callerFunction, invokedFunction, thisExpr,
           x.getArguments(), op)) {
         return x;
       }
@@ -1618,8 +1618,8 @@
    * the generated output. Increasing this number will allow larger sections of
    * code to be inlined, but at a cost of larger JS output.
    */
-  private static final double MAX_COMPLEXITY_INCREASE = 
-      Double.parseDouble(System.getProperty("gwt.jsinlinerRatio", "5.0"));
+  private static final double MAX_COMPLEXITY_INCREASE = Double.parseDouble(System.getProperty(
+      "gwt.jsinlinerRatio", "5.0"));
 
   /**
    * Static entry point used by JavaToJavaScriptCompiler.
@@ -1637,8 +1637,8 @@
    * The context parameter provides a scope in which local (and therefore
    * immutable) variables are defined.
    */
-  private static boolean affectedBySideEffects(JsProgram program,
-      List<JsExpression> list, JsFunction context) {
+  private static boolean affectedBySideEffects(List<JsExpression> list,
+      JsFunction context) {
     /*
      * If the caller contains no nested functions, none of its locals can
      * possibly be affected by side effects.
@@ -1647,8 +1647,7 @@
     if (context != null && !containsNestedFunctions(context)) {
       safeScope = context.getScope();
     }
-    AffectedBySideEffectsVisitor v = new AffectedBySideEffectsVisitor(program,
-        safeScope);
+    AffectedBySideEffectsVisitor v = new AffectedBySideEffectsVisitor(safeScope);
     v.acceptList(list);
     return v.affectedBySideEffects();
   }
@@ -1762,8 +1761,8 @@
    * @return a JsExpression representing all expressions that would have been
    *         evaluated by the statement
    */
-  private static JsExpression hoistedExpression(JsProgram program,
-      JsStatement statement, List<JsName> localVariableNames) {
+  private static JsExpression hoistedExpression(JsStatement statement,
+      List<JsName> localVariableNames) {
     JsExpression expression;
     if (statement instanceof JsExprStmt) {
       // Extract the expression
@@ -1775,7 +1774,8 @@
       JsReturn ret = (JsReturn) statement;
       expression = ret.getExpr();
       if (expression == null) {
-        expression = program.getUndefinedLiteral();
+        expression = new JsNameRef(ret.getSourceInfo(),
+            JsRootScope.INSTANCE.getUndefined());
       }
 
     } else if (statement instanceof JsVars) {
@@ -1783,7 +1783,7 @@
       JsVars vars = (JsVars) statement;
 
       // Rely on comma expression cleanup to remove this later.
-      expression = program.getNullLiteral();
+      expression = JsNullLiteral.INSTANCE;
 
       for (JsVar var : vars) {
         // Record the locally-defined variable
@@ -1854,9 +1854,8 @@
   /**
    * Determine if a statement can be inlined into a call site.
    */
-  private static boolean isInlinable(JsProgram program, JsFunction caller,
-      JsFunction callee, JsExpression thisExpr, List<JsExpression> arguments,
-      JsNode toInline) {
+  private static boolean isInlinable(JsFunction caller, JsFunction callee,
+      JsExpression thisExpr, List<JsExpression> arguments, JsNode toInline) {
 
     /*
      * This will happen with varargs-style JavaScript functions that rely on the
@@ -1913,21 +1912,21 @@
      * effects. This will determine how aggressively the parameters may be
      * reordered.
      */
-    if (isVolatile(program, evalArgs, caller)) {
+    if (isVolatile(evalArgs, caller)) {
       /*
        * Determine the order in which the parameters must be evaluated. This
        * will vary between call sites, based on whether or not the invocation's
        * arguments can be repeated without ill effect.
        */
       List<JsName> requiredOrder = new ArrayList<JsName>();
-      if (thisExpr != null && isVolatile(program, thisExpr, callee)) {
+      if (thisExpr != null && isVolatile(thisExpr, callee)) {
         requiredOrder.add(EvaluationOrderVisitor.THIS_NAME);
       }
       for (int i = 0; i < arguments.size(); i++) {
         JsExpression e = arguments.get(i);
         JsParameter p = callee.getParameters().get(i);
 
-        if (isVolatile(program, e, callee)) {
+        if (isVolatile(e, callee)) {
           requiredOrder.add(p.getName());
         }
       }
@@ -1974,9 +1973,8 @@
    * affected by side effects when evaluated within a particular function
    * context.
    */
-  private static boolean isVolatile(JsProgram program, JsExpression e,
-      JsFunction context) {
-    return isVolatile(program, Collections.singletonList(e), context);
+  private static boolean isVolatile(JsExpression e, JsFunction context) {
+    return isVolatile(Collections.singletonList(e), context);
   }
 
   /**
@@ -1984,10 +1982,8 @@
    * affected by side effects when evaluated within a particular function
    * context.
    */
-  private static boolean isVolatile(JsProgram program, List<JsExpression> list,
-      JsFunction context) {
-    return hasSideEffects(list)
-        || affectedBySideEffects(program, list, context);
+  private static boolean isVolatile(List<JsExpression> list, JsFunction context) {
+    return hasSideEffects(list) || affectedBySideEffects(list, context);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/js/JsParser.java b/dev/core/src/com/google/gwt/dev/js/JsParser.java
index f1bdb2d..d3d81de 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsParser.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsParser.java
@@ -15,7 +15,9 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.Correlation.Literal;
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
 import com.google.gwt.dev.js.ast.JsArrayAccess;
 import com.google.gwt.dev.js.ast.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -27,8 +29,10 @@
 import com.google.gwt.dev.js.ast.JsCatch;
 import com.google.gwt.dev.js.ast.JsConditional;
 import com.google.gwt.dev.js.ast.JsContinue;
+import com.google.gwt.dev.js.ast.JsDebugger;
 import com.google.gwt.dev.js.ast.JsDefault;
 import com.google.gwt.dev.js.ast.JsDoWhile;
+import com.google.gwt.dev.js.ast.JsEmpty;
 import com.google.gwt.dev.js.ast.JsExprStmt;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFor;
@@ -41,14 +45,16 @@
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsNew;
 import com.google.gwt.dev.js.ast.JsNode;
+import com.google.gwt.dev.js.ast.JsNullLiteral;
+import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsParameter;
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
 import com.google.gwt.dev.js.ast.JsPrefixOperation;
-import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsPropertyInitializer;
 import com.google.gwt.dev.js.ast.JsRegExp;
 import com.google.gwt.dev.js.ast.JsReturn;
+import com.google.gwt.dev.js.ast.JsRootScope;
 import com.google.gwt.dev.js.ast.JsScope;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsStringLiteral;
@@ -80,7 +86,7 @@
 
   public static List<JsStatement> parse(SourceInfo rootSourceInfo,
       JsScope scope, Reader r) throws IOException, JsParserException {
-    return new JsParser(scope.getProgram()).parseImpl(rootSourceInfo, scope, r);
+    return new JsParser().parseImpl(rootSourceInfo, scope, r);
   }
 
   public static void parseInto(SourceInfo rootSourceInfo, JsScope scope,
@@ -90,13 +96,10 @@
     parentStmts.addAll(childStmts);
   }
 
-  private JsProgram program;
-
   private final Stack<JsScope> scopeStack = new Stack<JsScope>();
   private final Stack<SourceInfo> sourceInfoStack = new Stack<SourceInfo>();
 
-  private JsParser(JsProgram program) {
-    this.program = program;
+  private JsParser() {
   }
 
   List<JsStatement> parseImpl(final SourceInfo rootSourceInfo, JsScope scope,
@@ -155,9 +158,20 @@
       // Rhino only reports line numbers for statement nodes, not expressions
       return parent;
     }
-    SourceInfo toReturn = program.createSourceInfo(lineno, parent.getFileName());
-    toReturn.copyMissingCorrelationsFrom(parent);
-    return toReturn;
+    return parent.makeChild(SourceOrigin.create(lineno, parent.getFileName()));
+  }
+
+  /**
+   * Force a distinct child to be created, so correlations can be added.
+   */
+  private SourceInfo makeSourceInfoDistinct(Node node) {
+    SourceInfo parent = sourceInfoStack.peek();
+    int lineno = node.getLineno();
+    if (lineno == -1) {
+      // Rhino only reports line numbers for statement nodes, not expressions
+      lineno = parent.getStartLine();
+    }
+    return parent.makeChild(SourceOrigin.create(lineno, parent.getFileName()));
   }
 
   private JsNode map(Node node) throws JsParserException {
@@ -170,7 +184,7 @@
       }
 
       case TokenStream.DEBUGGER:
-        return mapDebuggerStatement();
+        return mapDebuggerStatement(node);
 
       case TokenStream.VOID:
         // VOID = nothing was parsed for this node
@@ -236,10 +250,11 @@
       case TokenStream.HOOK:
         return mapConditional(node);
 
-      case TokenStream.STRING:
-        return program.getStringLiteral(
-            sourceInfoStack.peek().makeChild(JsParser.class,
-                "JS String literal"), node.getString());
+      case TokenStream.STRING: {
+        SourceInfo info = makeSourceInfoDistinct(node);
+        info.addCorrelation(info.getCorrelationFactory().by(Literal.JS_STRING));
+        return new JsStringLiteral(info, node.getString());
+      }
 
       case TokenStream.NUMBER:
         return mapNumber(node);
@@ -484,10 +499,10 @@
     }
   }
 
-  private JsStatement mapDebuggerStatement() {
+  private JsStatement mapDebuggerStatement(Node node) {
     // Calls an optional method to invoke the debugger.
     //
-    return program.getDebuggerStmt();
+    return new JsDebugger(makeSourceInfo(node));
   }
 
   private JsExpression mapDeleteProp(Node node) throws JsParserException {
@@ -597,6 +612,7 @@
     Node fromIncr = fromTest.getNext();
     Node fromBody = fromIncr.getNext();
 
+    SourceInfo info = makeSourceInfo(forNode);
     if (fromBody == null) {
       // This could be a "for...in" structure.
       // We could based on the different child layout.
@@ -612,7 +628,7 @@
         Node fromIterVarName = fromIter.getFirstChild();
         String fromName = fromIterVarName.getString();
         JsName toName = getScope().declareName(fromName);
-        toForIn = new JsForIn(makeSourceInfo(forNode), toName);
+        toForIn = new JsForIn(info, toName);
         Node fromIterInit = fromIterVarName.getFirstChild();
         if (fromIterInit != null) {
           // That has an initializer expression (useful only for side effects).
@@ -622,7 +638,7 @@
       } else {
         // An unnamed iterator var.
         //
-        toForIn = new JsForIn(makeSourceInfo(forNode));
+        toForIn = new JsForIn(info);
         toForIn.setIterExpr(mapExpression(fromIter));
       }
       toForIn.setObjExpr(mapExpression(fromObjExpr));
@@ -633,14 +649,14 @@
       if (bodyStmt != null) {
         toForIn.setBody(bodyStmt);
       } else {
-        toForIn.setBody(program.getEmptyStmt());
+        toForIn.setBody(new JsEmpty(info));
       }
 
       return toForIn;
     } else {
       // Regular ol' for loop.
       //
-      JsFor toFor = new JsFor(makeSourceInfo(forNode));
+      JsFor toFor = new JsFor(info);
 
       // The first item is either an expression or a JsVars.
       JsNode initThingy = map(fromInit);
@@ -659,7 +675,7 @@
       if (bodyStmt != null) {
         toFor.setBody(bodyStmt);
       } else {
-        toFor.setBody(program.getEmptyStmt());
+        toFor.setBody(new JsEmpty(info));
       }
       return toFor;
     }
@@ -821,7 +837,8 @@
   }
 
   private JsExpression mapNumber(Node numberNode) {
-    return program.getNumberLiteral(numberNode.getDouble());
+    return new JsNumberLiteral(makeSourceInfo(numberNode),
+        numberNode.getDouble());
   }
 
   private JsExpression mapObjectLit(Node objLitNode) throws JsParserException {
@@ -887,16 +904,20 @@
         return new JsThisRef(makeSourceInfo(node));
 
       case TokenStream.TRUE:
-        return program.getTrueLiteral();
+        return JsBooleanLiteral.TRUE;
 
       case TokenStream.FALSE:
-        return program.getFalseLiteral();
+        return JsBooleanLiteral.FALSE;
 
       case TokenStream.NULL:
-        return program.getNullLiteral();
+        return JsNullLiteral.INSTANCE;
 
-      case TokenStream.UNDEFINED:
-        return program.getUndefinedLiteral();
+      case TokenStream.UNDEFINED: {
+        SourceInfo info = makeSourceInfoDistinct(node);
+        info.addCorrelation(info.getCorrelationFactory().by(
+            Literal.JS_UNDEFINED));
+        return new JsNameRef(info, JsRootScope.INSTANCE.getUndefined());
+      }
 
       default:
         throw createParserException("Unknown primary: " + node.getIntDatum(),
@@ -1017,7 +1038,7 @@
     } else {
       // When map() returns null, we return an empty statement.
       //
-      return program.getEmptyStmt();
+      return new JsEmpty(makeSourceInfo(nodeStmt));
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
index 26bcfbc..35633c3 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
@@ -27,6 +27,7 @@
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
 import com.google.gwt.dev.js.ast.JsBinaryOperator;
 import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsBooleanLiteral;
 import com.google.gwt.dev.js.ast.JsCatch;
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsExprStmt;
@@ -39,10 +40,13 @@
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsNew;
 import com.google.gwt.dev.js.ast.JsNode;
+import com.google.gwt.dev.js.ast.JsNullLiteral;
+import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
 import com.google.gwt.dev.js.ast.JsPrefixOperation;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsReturn;
+import com.google.gwt.dev.js.ast.JsRootScope;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsStringLiteral;
 import com.google.gwt.dev.js.ast.JsThrow;
@@ -50,9 +54,9 @@
 import com.google.gwt.dev.js.ast.JsUnaryOperation;
 import com.google.gwt.dev.js.ast.JsUnaryOperator;
 import com.google.gwt.dev.js.ast.JsVars;
+import com.google.gwt.dev.js.ast.JsVars.JsVar;
 import com.google.gwt.dev.js.ast.JsVisitor;
 import com.google.gwt.dev.js.ast.JsWhile;
-import com.google.gwt.dev.js.ast.JsVars.JsVar;
 import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.dev.util.collect.Maps;
 
@@ -278,7 +282,7 @@
         // There is a finally block, so we need to set the early-exit flag
         JsBinaryOperation asg = new JsBinaryOperation(x.getSourceInfo(),
             JsBinaryOperator.ASG, earlyExitRef(outerFinallyBlock),
-            program.getBooleanLiteral(true));
+            JsBooleanLiteral.get(true));
         if (x.getExpr() == null) {
           if (ctx.canInsert()) {
             // exitingEarly = true; return;
@@ -507,7 +511,7 @@
      */
     private JsExpression pop(SourceInfo info) {
       JsBinaryOperation sub = new JsBinaryOperation(info, JsBinaryOperator.SUB,
-          stackIndexRef(info), program.getNumberLiteral(1));
+          stackIndexRef(info), new JsNumberLiteral(info, 1));
       JsBinaryOperation op = new JsBinaryOperation(info, JsBinaryOperator.ASG,
           stackDepth.makeRef(info), sub);
       return op;
@@ -525,7 +529,7 @@
       JsExpression currentFunctionRef;
       if (currentFunction.getName() == null) {
         // Anonymous
-        currentFunctionRef = program.getNullLiteral();
+        currentFunctionRef = JsNullLiteral.INSTANCE;
       } else {
         currentFunctionRef = currentFunction.getName().makeRef(info);
       }
@@ -737,11 +741,11 @@
           "Synthetic location data");
 
       // ($locations[stackIndex] = fileName + lineNumber, x)
-      JsExpression location = program.getStringLiteral(info,
+      JsExpression location = new JsStringLiteral(info,
           String.valueOf(lastLine = info.getStartLine()));
       if (recordFileNames) {
         // 'fileName:' + lineNumber
-        JsStringLiteral stringLit = program.getStringLiteral(info,
+        JsStringLiteral stringLit = new JsStringLiteral(info,
             baseName(lastFile = info.getFileName()) + ":");
         location = new JsBinaryOperation(info, JsBinaryOperator.ADD, stringLit,
             location);
@@ -770,13 +774,10 @@
    * with references to our locally-defined, obfuscatable names.
    */
   private class ReplaceUnobfuscatableNames extends JsModVisitor {
-    private final JsName rootLineNumbers = program.getRootScope().findExistingUnobfuscatableName(
-        "$location");
     // See JsRootScope for the definition of these names
-    private final JsName rootStack = program.getRootScope().findExistingUnobfuscatableName(
-        "$stack");
-    private final JsName rootStackDepth = program.getRootScope().findExistingUnobfuscatableName(
-        "$stackDepth");
+    private final JsName rootLineNumbers = JsRootScope.INSTANCE.findExistingUnobfuscatableName("$location");
+    private final JsName rootStack = JsRootScope.INSTANCE.findExistingUnobfuscatableName("$stack");
+    private final JsName rootStackDepth = JsRootScope.INSTANCE.findExistingUnobfuscatableName("$stackDepth");
 
     @Override
     public void endVisit(JsNameRef x, JsContext ctx) {
@@ -896,7 +897,7 @@
     JsVar stackVar = new JsVar(info, stack);
     stackVar.setInitExpr(new JsArrayLiteral(info));
     JsVar stackDepthVar = new JsVar(info, stackDepth);
-    stackDepthVar.setInitExpr(program.getNumberLiteral(info, -1));
+    stackDepthVar.setInitExpr(new JsNumberLiteral(info, (-1)));
     JsVar lineNumbersVar = new JsVar(info, lineNumbers);
     lineNumbersVar.setInitExpr(new JsArrayLiteral(info));
 
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
index aaa9db5..f44b733 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
@@ -28,6 +28,7 @@
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsContinue;
 import com.google.gwt.dev.js.ast.JsDoWhile;
+import com.google.gwt.dev.js.ast.JsEmpty;
 import com.google.gwt.dev.js.ast.JsExprStmt;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFor;
@@ -291,7 +292,7 @@
         if (ctx.canRemove()) {
           ctx.removeMe();
         } else {
-          ctx.replaceMe(program.getEmptyStmt());
+          ctx.replaceMe(new JsEmpty(x.getSourceInfo()));
         }
       }
     }
@@ -623,7 +624,7 @@
         // "undefined" is not a JsValueLiteral, so the only way
         // the result can be true is if exp is itself a JsNullLiteral
         boolean result = exp instanceof JsNullLiteral;
-        return program.getBooleanLiteral(result);
+        return JsBooleanLiteral.get(result);
       }
 
       // no simplification made
@@ -640,7 +641,7 @@
         // "undefined" is not a JsValueLiteral, so the only way
         // the result can be false is if exp is itself a JsNullLiteral
         boolean result = !(exp instanceof JsNullLiteral);
-        return program.getBooleanLiteral(result);
+        return JsBooleanLiteral.get(result);
       }
 
       // no simplification made
@@ -653,18 +654,19 @@
     private void trySimplifyAdd(JsExpression original, JsExpression arg1,
         JsExpression arg2, JsContext ctx) {
       if (arg1 instanceof JsValueLiteral && arg2 instanceof JsValueLiteral) {
+        SourceInfo info = original.getSourceInfo();
         // case: number + number
         if (arg1 instanceof JsNumberLiteral && arg2 instanceof JsNumberLiteral) {
           double value = ((JsNumberLiteral) arg1).getValue()
               + ((JsNumberLiteral) arg2).getValue();
-          ctx.replaceMe(program.getNumberLiteral(value));
+          ctx.replaceMe(new JsNumberLiteral(info, value));
         } else {
           // cases: number + string or string + number
           StringBuilder result = new StringBuilder();
           if (appendLiteral(result, (JsValueLiteral) arg1)
               && appendLiteral(result, (JsValueLiteral) arg2)) {
-            ctx.replaceMe(program.getStringLiteral(original.getSourceInfo(),
-                result.toString()));
+            info.merge(arg1.getSourceInfo(), arg2.getSourceInfo());
+            ctx.replaceMe(new JsStringLiteral(info, result.toString()));
           }
         }
       }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSymbolResolver.java b/dev/core/src/com/google/gwt/dev/js/JsSymbolResolver.java
index 1f0c050..3ae0308 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSymbolResolver.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSymbolResolver.java
@@ -37,7 +37,7 @@
         name = getScope().findExistingName(ident);
         if (name == null) {
           // No clue what this is; create a new unobfuscatable name
-          name = program.getRootScope().declareName(ident);
+          name = program.getScope().declareName(ident);
           name.setObfuscatable(false);
         }
       } else {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsBooleanLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsBooleanLiteral.java
index e254270..24fc9c1 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsBooleanLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsBooleanLiteral.java
@@ -16,16 +16,26 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
 
 /**
  * Represents a JavaScript literal boolean expression.
  */
 public final class JsBooleanLiteral extends JsValueLiteral {
 
+  public static final JsBooleanLiteral FALSE = new JsBooleanLiteral(
+      SourceOrigin.UNKNOWN, false);
+
+  public static final JsBooleanLiteral TRUE = new JsBooleanLiteral(
+      SourceOrigin.UNKNOWN, true);
+
+  public static JsBooleanLiteral get(boolean value) {
+    return value ? TRUE : FALSE;
+  }
+
   private final boolean value;
 
-  // Should be interned by JsProgram
-  JsBooleanLiteral(SourceInfo sourceInfo, boolean value) {
+  private JsBooleanLiteral(SourceInfo sourceInfo, boolean value) {
     super(sourceInfo);
     this.value = value;
   }
@@ -56,4 +66,8 @@
     v.visit(this, ctx);
     v.endVisit(this, ctx);
   }
+
+  private Object readResolve() {
+    return get(value);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsCatchScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsCatchScope.java
index 1791fc5..542d398 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsCatchScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsCatchScope.java
@@ -15,14 +15,14 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import java.util.Collections;
 import java.util.Iterator;
-import java.util.NoSuchElementException;
 
 /**
  * A special scope used only for catch blocks. It only holds a single symbol:
  * the catch argument's name.
  */
-public class JsCatchScope extends JsScope {
+public class JsCatchScope extends JsNestingScope {
 
   private final JsName name;
 
@@ -32,45 +32,14 @@
   }
 
   @Override
-  public JsName declareName(String ident) {
-    // Declare into parent scope!
-    return getParent().declareName(ident);
-  }
-
-  @Override
-  public JsName declareName(String ident, String shortIdent) {
-    // Declare into parent scope!
-    return getParent().declareName(ident, shortIdent);
-  }
-
-  @Override
   public Iterator<JsName> getAllNames() {
-    return new Iterator<JsName>() {
-      private boolean didIterate = false;
-
-      public boolean hasNext() {
-        return !didIterate;
-      }
-
-      public JsName next() {
-        if (didIterate) {
-          throw new NoSuchElementException();
-        }
-        didIterate = true;
-        return name;
-      }
-
-      public void remove() {
-        throw new UnsupportedOperationException();
-      }
-
-    };
+    return Collections.singleton(name).iterator();
   }
 
   @Override
   protected JsName doCreateName(String ident, String shortIdent) {
-    throw new UnsupportedOperationException(
-        "Cannot create a name in a catch scope");
+    // Declare into parent scope!
+    return getParent().declareName(ident, shortIdent);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsEmpty.java b/dev/core/src/com/google/gwt/dev/js/ast/JsEmpty.java
index 00a5b6f..4727008 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsEmpty.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsEmpty.java
@@ -22,8 +22,7 @@
  */
 public class JsEmpty extends JsStatement {
 
-  // Interned by JsProgram
-  JsEmpty(SourceInfo sourceInfo) {
+  public JsEmpty(SourceInfo sourceInfo) {
     super(sourceInfo);
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java b/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java
index 959470c..74f211f 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java
@@ -68,7 +68,7 @@
     setName(name);
     String scopeName = (name == null) ? "<anonymous>" : name.getIdent();
     scopeName = "function " + scopeName;
-    this.scope = new JsScope(parent, scopeName);
+    this.scope = new JsNormalScope(parent, scopeName);
   }
 
   public JsBlock getBody() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
index 58a7a8c..4bde74d 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
@@ -89,10 +89,7 @@
 
   @Override
   public boolean isDefinitelyNull() {
-    if (name != null) {
-      return (name.getEnclosing().getProgram().getUndefinedLiteral().getName() == name);
-    }
-    return false;
+    return name == JsRootScope.INSTANCE.getUndefined();
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNestingScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNestingScope.java
new file mode 100644
index 0000000..36b83e7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNestingScope.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 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.ast;
+
+import com.google.gwt.dev.util.collect.Lists;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * A normal scope that has a parent and children.
+ */
+public abstract class JsNestingScope extends JsScope implements Serializable {
+
+  /**
+   * Transient because children will add themselves to the parent after
+   * deserialization.
+   */
+  private transient List<JsScope> children = Lists.create();
+
+  private final JsScope parent;
+
+  /**
+   * Create a scope with parent.
+   */
+  public JsNestingScope(JsScope parent, String description) {
+    super(description);
+    assert (parent != null);
+    this.parent = parent;
+    parent.addChild(this);
+  }
+
+  /**
+   * Returns a list of this scope's child scopes.
+   */
+  @Override
+  public final List<JsScope> getChildren() {
+    return children;
+  }
+
+  /**
+   * Returns the parent scope of this scope, or <code>null</code> if this is the
+   * root scope.
+   */
+  @Override
+  public final JsScope getParent() {
+    return parent;
+  }
+
+  @Override
+  protected final void addChild(JsScope child) {
+    children = Lists.add(children, child);
+  }
+
+  protected Object readResolve() {
+    children = Lists.create();
+    parent.addChild(this);
+    return this;
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNormalScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNormalScope.java
new file mode 100644
index 0000000..ce4dcf6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNormalScope.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 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.ast;
+
+import com.google.gwt.dev.util.StringInterner;
+import com.google.gwt.dev.util.collect.Maps;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A normal scope that has a parent and children.
+ */
+public class JsNormalScope extends JsNestingScope implements Serializable {
+
+  private Map<String, JsName> names = Collections.emptyMap();
+
+  /**
+   * Create a scope with parent.
+   */
+  public JsNormalScope(JsScope parent, String description) {
+    super(parent, description);
+  }
+
+  /**
+   * Returns an iterator for all the names defined by this scope.
+   */
+  @Override
+  public Iterator<JsName> getAllNames() {
+    return names.values().iterator();
+  }
+
+  /**
+   * Creates a new name in this scope.
+   */
+  @Override
+  protected JsName doCreateName(String ident, String shortIdent) {
+    ident = StringInterner.get().intern(ident);
+    shortIdent = StringInterner.get().intern(shortIdent);
+    JsName name = new JsName(this, ident, shortIdent);
+    names = Maps.putOrdered(names, ident, name);
+    return name;
+  }
+
+  /**
+   * Attempts to find the name object for the specified ident, searching in this
+   * scope only.
+   * 
+   * @return <code>null</code> if the identifier has no associated name
+   */
+  @Override
+  protected JsName findExistingNameNoRecurse(String ident) {
+    return names.get(ident);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNullLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNullLiteral.java
index dba3966..2774635 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNullLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNullLiteral.java
@@ -16,14 +16,17 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
 
 /**
  * A JavaScript null literal.
  */
 public final class JsNullLiteral extends JsValueLiteral {
 
-  // Should only be instantiated in JsProgram
-  JsNullLiteral(SourceInfo sourceInfo) {
+  public static final JsNullLiteral INSTANCE = new JsNullLiteral(
+      SourceOrigin.UNKNOWN);
+
+  private JsNullLiteral(SourceInfo sourceInfo) {
     super(sourceInfo);
   }
 
@@ -49,4 +52,12 @@
     v.visit(this, ctx);
     v.endVisit(this, ctx);
   }
+
+  /**
+   * Note, if this ever becomes not-a-singleton, we'll need to check the
+   * SourceInfo == SourceOrigin.UNKNOWN.
+   */
+  private Object readResolve() {
+    return INSTANCE;
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNumberLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNumberLiteral.java
index 90026ac..df3508c 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNumberLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNumberLiteral.java
@@ -24,8 +24,7 @@
 
   private final double value;
 
-  // Should be interned by JsProgram
-  JsNumberLiteral(SourceInfo sourceInfo, double value) {
+  public JsNumberLiteral(SourceInfo sourceInfo, double value) {
     super(sourceInfo);
     this.value = value;
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
index 9ab9e27..a05967b 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
@@ -16,10 +16,9 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.CorrelationFactory;
+import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.SourceOrigin;
-import com.google.gwt.dev.jjs.Correlation.Axis;
-import com.google.gwt.dev.jjs.Correlation.Literal;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -31,39 +30,16 @@
 
   private final CorrelationFactory correlator;
 
-  private final JsStatement debuggerStmt;
-
-  private final JsEmpty emptyStmt;
-
-  private final JsBooleanLiteral falseLiteral;
-
   private JsProgramFragment[] fragments;
 
-  /**
-   * The root intrinsic source info.
-   */
-  private final SourceInfo intrinsic;
-
   private final Map<String, JsFunction> indexedFunctions = new HashMap<String, JsFunction>();
 
-  private final JsNullLiteral nullLiteral;
-
-  private final Map<Double, JsNumberLiteral> numberLiteralMap = new HashMap<Double, JsNumberLiteral>();
-
   private final JsScope objectScope;
 
-  private final JsRootScope rootScope;
-
-  private final Map<String, JsStringLiteral> stringLiteralMap = new HashMap<String, JsStringLiteral>();
-
-  private final SourceInfo stringPoolSourceInfo;
-
   private final JsScope topScope;
 
-  private final JsBooleanLiteral trueLiteral;
-
   public JsProgram() {
-    this(new CorrelationFactory.DummyCorrelationFactory());
+    this(DummyCorrelationFactory.INSTANCE);
   }
 
   /**
@@ -79,26 +55,16 @@
         JsProgram.class.getName())));
 
     this.correlator = correlator;
-    intrinsic = createSourceInfo(0, getClass().getName());
 
-    rootScope = new JsRootScope(this);
-    topScope = new JsScope(rootScope, "Global");
-    objectScope = new JsScope(rootScope, "Object");
+    topScope = new JsNormalScope(JsRootScope.INSTANCE, "Global");
+    objectScope = new JsNormalScope(JsRootScope.INSTANCE, "Object");
     setFragmentCount(1);
+  }
 
-    debuggerStmt = new JsDebugger(createLiteralSourceInfo("debugger statement"));
-    emptyStmt = new JsEmpty(createLiteralSourceInfo("Empty statement"));
-    falseLiteral = new JsBooleanLiteral(createLiteralSourceInfo(
-        "false literal", Literal.JS_BOOLEAN), false);
-    nullLiteral = new JsNullLiteral(createLiteralSourceInfo("null literal",
-        Literal.JS_NULL));
-    trueLiteral = new JsBooleanLiteral(createLiteralSourceInfo("true literal",
-        Literal.JS_BOOLEAN), true);
-
-    trueLiteral.getSourceInfo().addCorrelation(
-        correlator.by(Literal.JS_BOOLEAN));
-    stringPoolSourceInfo = createLiteralSourceInfo("String pool",
-        Literal.JS_STRING);
+  public SourceInfo createSourceInfo(int startPos, int endPos, int startLine,
+      String fileName) {
+    return correlator.makeSourceInfo(SourceOrigin.create(startPos, endPos,
+        startLine, fileName));
   }
 
   public SourceInfo createSourceInfo(int lineNumber, String location) {
@@ -110,31 +76,6 @@
     return createSourceInfo(0, caller.getName()).makeChild(caller, description);
   }
 
-  public JsBooleanLiteral getBooleanLiteral(boolean truth) {
-    if (truth) {
-      return getTrueLiteral();
-    }
-    return getFalseLiteral();
-  }
-
-  /**
-   * Gets the {@link JsStatement} to use whenever parsed source include a
-   * <code>debugger</code> statement.
-   * 
-   * @see #setDebuggerStmt(JsStatement)
-   */
-  public JsStatement getDebuggerStmt() {
-    return debuggerStmt;
-  }
-
-  public JsEmpty getEmptyStmt() {
-    return emptyStmt;
-  }
-
-  public JsBooleanLiteral getFalseLiteral() {
-    return falseLiteral;
-  }
-
   public JsBlock getFragmentBlock(int fragment) {
     if (fragment < 0 || fragment >= fragments.length) {
       throw new IllegalArgumentException("Invalid fragment: " + fragment);
@@ -157,56 +98,11 @@
     return indexedFunctions.get(name);
   }
 
-  public JsNullLiteral getNullLiteral() {
-    return nullLiteral;
-  }
-
-  public JsNumberLiteral getNumberLiteral(double value) {
-    return getNumberLiteral(null, value);
-  }
-
-  public JsNumberLiteral getNumberLiteral(SourceInfo info, double value) {
-    /*
-     * This method only canonicalizes number literals when we don't have an
-     * incoming SourceInfo so that we can distinguish int-0 from double-0 in the
-     * analysis.
-     */
-    if (info == null) {
-      JsNumberLiteral lit = numberLiteralMap.get(value);
-      if (lit == null) {
-        info = createSourceInfoSynthetic(JsProgram.class, "Number literal "
-            + value);
-        info.addCorrelation(correlator.by(Literal.JS_NUMBER));
-        lit = new JsNumberLiteral(info, value);
-        numberLiteralMap.put(value, lit);
-      }
-
-      return lit;
-    } else {
-      // Only add a JS_NUMBER if no literal correlation present: e.g. Java int
-      if (info.getPrimaryCorrelation(Axis.LITERAL) == null) {
-        // Don't mutate incoming SourceInfo
-        info = info.makeChild(JsProgram.class, "Number literal " + value);
-        info.addCorrelation(correlator.by(Literal.JS_NUMBER));
-      }
-      return new JsNumberLiteral(info, value);
-    }
-  }
-
   public JsScope getObjectScope() {
     return objectScope;
   }
 
   /**
-   * Gets the quasi-mythical root scope. This is not the same as the top scope;
-   * all unresolvable identifiers wind up here, because they are considered
-   * external to the program.
-   */
-  public JsRootScope getRootScope() {
-    return rootScope;
-  }
-
-  /**
    * Gets the top level scope. This is the scope of all the statements in the
    * main program.
    */
@@ -214,31 +110,6 @@
     return topScope;
   }
 
-  /**
-   * Creates or retrieves a JsStringLiteral from an interned object pool.
-   */
-  public JsStringLiteral getStringLiteral(SourceInfo sourceInfo, String value) {
-    JsStringLiteral lit = stringLiteralMap.get(value);
-    if (lit == null) {
-      lit = new JsStringLiteral(stringPoolSourceInfo.makeChild(JsProgram.class,
-          "String literal: " + value), value);
-      stringLiteralMap.put(value, lit);
-    }
-    lit.getSourceInfo().merge(sourceInfo);
-    return lit;
-  }
-
-  public JsBooleanLiteral getTrueLiteral() {
-    return trueLiteral;
-  }
-
-  public JsNameRef getUndefinedLiteral() {
-    SourceInfo info = createSourceInfoSynthetic(JsProgram.class,
-        "undefined reference");
-    info.addCorrelation(correlator.by(Literal.JS_UNDEFINED));
-    return rootScope.findExistingName("undefined").makeRef(info);
-  }
-
   public void setFragmentCount(int fragments) {
     this.fragments = new JsProgramFragment[fragments];
     for (int i = 0; i < fragments; i++) {
@@ -260,14 +131,4 @@
     }
     v.endVisit(this, ctx);
   }
-
-  private SourceInfo createLiteralSourceInfo(String description) {
-    return intrinsic.makeChild(getClass(), description);
-  }
-
-  private SourceInfo createLiteralSourceInfo(String description, Literal literal) {
-    SourceInfo child = createLiteralSourceInfo(description);
-    child.addCorrelation(correlator.by(literal));
-    return child;
-  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRootName.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRootName.java
new file mode 100644
index 0000000..d32582a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRootName.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 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.ast;
+
+import java.io.Serializable;
+
+/**
+ * A well-known name in the root scope.
+ */
+public class JsRootName extends JsName {
+
+  private static class SerializedForm implements Serializable {
+    private final String ident;
+
+    public SerializedForm(String ident) {
+      this.ident = ident;
+    }
+
+    private Object readResolve() {
+      return JsRootScope.INSTANCE.findExistingName(ident);
+    }
+  }
+
+  JsRootName(JsRootScope rootScope, String ident) {
+    super(rootScope, ident, ident);
+    super.setObfuscatable(false);
+  }
+
+  @Override
+  public void setObfuscatable(boolean isObfuscatable) {
+    throw new UnsupportedOperationException("Root names are immutable");
+  }
+
+  @Override
+  public void setShortIdent(String shortIdent) {
+    throw new UnsupportedOperationException("Root names are immutable");
+  }
+
+  @Override
+  public void setStaticRef(JsNode node) {
+    throw new UnsupportedOperationException("Root names are immutable");
+  }
+
+  private Object writeReplace() {
+    return new SerializedForm(getIdent());
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
index 325f62e..6f1dfc1 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
@@ -15,336 +15,383 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.util.collect.Lists;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
- * The root scope is the parent of every scope. All identifiers in this scope
- * are not obfuscatable.
+ * The root scope is the parent of every scope, it contains a list of browser
+ * built-in identifiers that we should recognize and never obfuscate into.
  */
 public final class JsRootScope extends JsScope {
+  /*
+   * NOTE: the startup sequence for this class is a bit tricky.
+   */
 
-  private final JsProgram program;
+  private static class SerializedForm implements Serializable {
+    private Object readResolve() {
+      return JsRootScope.INSTANCE;
+    }
+  }
 
-  public JsRootScope(JsProgram program) {
+  public static final JsRootScope INSTANCE;
+
+  private static final String[] COMMON_BUILTINS = new String[]{
+      // 15.1.1 Value Properties of the Global Object
+      "NaN",
+      "Infinity",
+      "undefined",
+
+      // 15.1.2 Function Properties of the Global Object
+      "eval",
+      "parseInt",
+      "parseFloat",
+      "isNan",
+      "isFinite",
+
+      // 15.1.3 URI Handling Function Properties
+      "decodeURI",
+      "decodeURIComponent",
+      "encodeURI",
+      "encodeURIComponent",
+
+      // 15.1.4 Constructor Properties of the Global Object
+      "Object",
+      "Function",
+      "Array",
+      "String",
+      "Boolean",
+      "Number",
+      "Date",
+      "RegExp",
+      "Error",
+      "EvalError",
+      "RangeError",
+      "ReferenceError",
+      "SyntaxError",
+      "TypeError",
+      "URIError",
+
+      // 15.1.5 Other Properties of the Global Object
+      "Math",
+
+      // 10.1.6 Activation Object
+      "arguments",
+
+      // B.2 Additional Properties (non-normative)
+      "escape",
+      "unescape",
+
+      // Window props (https://developer.mozilla.org/en/DOM/window)
+      "applicationCache",
+      "closed",
+      "Components",
+      "content",
+      "controllers",
+      "crypto",
+      "defaultStatus",
+      "dialogArguments",
+      "directories",
+      "document",
+      "frameElement",
+      "frames",
+      "fullScreen",
+      "globalStorage",
+      "history",
+      "innerHeight",
+      "innerWidth",
+      "length",
+      "location",
+      "locationbar",
+      "localStorage",
+      "menubar",
+      "mozInnerScreenX",
+      "mozInnerScreenY",
+      "mozScreenPixelsPerCssPixel",
+      "name",
+      "navigator",
+      "opener",
+      "outerHeight",
+      "outerWidth",
+      "pageXOffset",
+      "pageYOffset",
+      "parent",
+      "personalbar",
+      "pkcs11",
+      "returnValue",
+      "screen",
+      "scrollbars",
+      "scrollMaxX",
+      "scrollMaxY",
+      "self",
+      "sessionStorage",
+      "sidebar",
+      "status",
+      "statusbar",
+      "toolbar",
+      "top",
+      "window",
+
+      // Window methods (https://developer.mozilla.org/en/DOM/window)
+      "alert",
+      "addEventListener",
+      "atob",
+      "back",
+      "blur",
+      "btoa",
+      "captureEvents",
+      "clearInterval",
+      "clearTimeout",
+      "close",
+      "confirm",
+      "disableExternalCapture",
+      "dispatchEvent",
+      "dump",
+      "enableExternalCapture",
+      "escape",
+      "find",
+      "focus",
+      "forward",
+      "GeckoActiveXObject",
+      "getAttention",
+      "getAttentionWithCycleCount",
+      "getComputedStyle",
+      "getSelection",
+      "home",
+      "maximize",
+      "minimize",
+      "moveBy",
+      "moveTo",
+      "open",
+      "openDialog",
+      "postMessage",
+      "print",
+      "prompt",
+      "QueryInterface",
+      "releaseEvents",
+      "removeEventListener",
+      "resizeBy",
+      "resizeTo",
+      "restore",
+      "routeEvent",
+      "scroll",
+      "scrollBy",
+      "scrollByLines",
+      "scrollByPages",
+      "scrollTo",
+      "setInterval",
+      "setResizeable",
+      "setTimeout",
+      "showModalDialog",
+      "sizeToContent",
+      "stop",
+      "uuescape",
+      "updateCommands",
+      "XPCNativeWrapper",
+      "XPCSafeJSOjbectWrapper",
+
+      // Mozilla Window event handlers, same cite
+      "onabort",
+      "onbeforeunload",
+      "onchange",
+      "onclick",
+      "onclose",
+      "oncontextmenu",
+      "ondragdrop",
+      "onerror",
+      "onfocus",
+      "onhashchange",
+      "onkeydown",
+      "onkeypress",
+      "onkeyup",
+      "onload",
+      "onmousedown",
+      "onmousemove",
+      "onmouseout",
+      "onmouseover",
+      "onmouseup",
+      "onmozorientation",
+      "onpaint",
+      "onreset",
+      "onresize",
+      "onscroll",
+      "onselect",
+      "onsubmit",
+      "onunload",
+
+      // Safari Web Content Guide
+      // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf
+      // WebKit Window member data, from WebKit DOM Reference
+      // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html)
+      // TODO(fredsa) Many, many more functions and member data to add
+      "ontouchcancel",
+      "ontouchend",
+      "ontouchmove",
+      "ontouchstart",
+      "ongesturestart",
+      "ongesturechange",
+      "ongestureend",
+
+      // extra window methods
+      "uneval",
+
+      // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7,
+      // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1
+      "getPrototypeOf",
+      "let",
+
+      // "future reserved words"
+      "abstract",
+      "int",
+      "short",
+      "boolean",
+      "interface",
+      "static",
+      "byte",
+      "long",
+      "char",
+      "final",
+      "native",
+      "synchronized",
+      "float",
+      "package",
+      "throws",
+      "goto",
+      "private",
+      "transient",
+      "implements",
+      "protected",
+      "volatile",
+      "double",
+      "public",
+
+      // IE methods
+      // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#)
+      "attachEvent",
+      "clientInformation",
+      "clipboardData",
+      "createPopup",
+      "dialogHeight",
+      "dialogLeft",
+      "dialogTop",
+      "dialogWidth",
+      "onafterprint",
+      "onbeforedeactivate",
+      "onbeforeprint",
+      "oncontrolselect",
+      "ondeactivate",
+      "onhelp",
+      "onresizeend",
+
+      // Common browser-defined identifiers not defined in ECMAScript
+      "event",
+      "external",
+      "Debug",
+      "Enumerator",
+      "Global",
+      "Image",
+      "ActiveXObject",
+      "VBArray",
+      "Components",
+
+      // Functions commonly defined on Object
+      "toString",
+      "getClass",
+      "constructor",
+      "prototype",
+
+      // Client-side JavaScript identifiers, which are needed for linkers
+      // that don't ensure GWT's window != $wnd, document != $doc, etc.
+      // Taken from the Rhino book, pg 715
+      "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient", "CanvasPattern",
+      "CanvasRenderingContext2D", "CDATASection", "CharacterData", "Comment",
+      "CSS2Properties", "CSSRule", "CSSStyleSheet", "Document",
+      "DocumentFragment", "DocumentType", "DOMException", "DOMImplementation",
+      "DOMParser", "Element", "Event", "ExternalInterface", "FlashPlayer",
+      "Form", "Frame", "History", "HTMLCollection", "HTMLDocument",
+      "HTMLElement", "IFrame", "Image", "Input", "JSObject", "KeyEvent",
+      "Link", "Location", "MimeType", "MouseEvent", "Navigator", "Node",
+      "NodeList", "Option", "Plugin", "ProcessingInstruction", "Range",
+      "RangeException", "Screen", "Select", "Table", "TableCell", "TableRow",
+      "TableSelection", "Text", "TextArea", "UIEvent", "Window",
+      "XMLHttpRequest", "XMLSerializer", "XPathException", "XPathResult",
+      "XSLTProcessor",
+
+      /*
+       * These keywords trigger the loading of the java-plugin. For the
+       * next-generation plugin, this results in starting a new Java process.
+       */
+      "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
+      "JavaArray", "JavaMember",
+
+      // GWT-defined identifiers
+      "$wnd", "$doc", "$moduleName", "$moduleBase", "$gwt_version",
+      "$sessionId",
+
+      // Identifiers used by JsStackEmulator; later set to obfuscatable
+      "$stack", "$stackDepth", "$location",
+
+      // TODO: prove why this is necessary or remove it
+      "call",};
+
+  static {
+    INSTANCE = new JsRootScope();
+  }
+
+  private final Map<String, JsName> names = new LinkedHashMap<String, JsName>();
+
+  private final JsName undefined;
+
+  private JsRootScope() {
     super("Root");
-    this.program = program;
-    ctorAddKnownGlobalSymbols();
+    for (String ident : COMMON_BUILTINS) {
+      names.put(ident, new JsRootName(this, ident));
+    }
+    undefined = names.get("undefined");
+    assert undefined != null;
   }
 
   @Override
-  public JsProgram getProgram() {
-    return program;
+  public Iterator<JsName> getAllNames() {
+    return Collections.unmodifiableCollection(names.values()).iterator();
+  }
+
+  @Override
+  public List<JsScope> getChildren() {
+    return Lists.create();
+  }
+
+  @Override
+  public JsScope getParent() {
+    return null;
+  }
+
+  public JsName getUndefined() {
+    return undefined;
+  }
+
+  @Override
+  protected void addChild(JsScope child) {
+    // Don't record children.
   }
 
   @Override
   protected JsName doCreateName(String ident, String shortIdent) {
-    JsName name = super.doCreateName(ident, shortIdent);
-    name.setObfuscatable(false);
-    return name;
+    throw new UnsupportedOperationException(
+        "Cannot create new names in the root scope");
   }
 
-  private void ctorAddKnownGlobalSymbols() {
-    // Section references are from Ecma-262
-    // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)
-    String[] commonBuiltins = new String[]{
-        // 15.1.1 Value Properties of the Global Object
-        "NaN",
-        "Infinity",
-        "undefined",
+  @Override
+  protected JsName findExistingNameNoRecurse(String ident) {
+    return names.get(ident);
+  }
 
-        // 15.1.2 Function Properties of the Global Object
-        "eval",
-        "parseInt",
-        "parseFloat",
-        "isNan",
-        "isFinite",
-
-        // 15.1.3 URI Handling Function Properties
-        "decodeURI",
-        "decodeURIComponent",
-        "encodeURI",
-        "encodeURIComponent",
-
-        // 15.1.4 Constructor Properties of the Global Object
-        "Object",
-        "Function",
-        "Array",
-        "String",
-        "Boolean",
-        "Number",
-        "Date",
-        "RegExp",
-        "Error",
-        "EvalError",
-        "RangeError",
-        "ReferenceError",
-        "SyntaxError",
-        "TypeError",
-        "URIError",
-
-        // 15.1.5 Other Properties of the Global Object
-        "Math",
-
-        // 10.1.6 Activation Object
-        "arguments",
-
-        // B.2 Additional Properties (non-normative)
-        "escape",
-        "unescape",
-
-        // Window props (https://developer.mozilla.org/en/DOM/window)
-        "applicationCache",
-        "closed",
-        "Components",
-        "content",
-        "controllers",
-        "crypto",
-        "defaultStatus",
-        "dialogArguments",
-        "directories",
-        "document",
-        "frameElement",
-        "frames",
-        "fullScreen",
-        "globalStorage",
-        "history",
-        "innerHeight",
-        "innerWidth",
-        "length",
-        "location",
-        "locationbar",
-        "localStorage",
-        "menubar",
-        "mozInnerScreenX",
-        "mozInnerScreenY",
-        "mozScreenPixelsPerCssPixel",
-        "name",
-        "navigator",
-        "opener",
-        "outerHeight",
-        "outerWidth",
-        "pageXOffset",
-        "pageYOffset",
-        "parent",
-        "personalbar",
-        "pkcs11",
-        "returnValue",
-        "screen",
-        "scrollbars",
-        "scrollMaxX",
-        "scrollMaxY",
-        "self",
-        "sessionStorage",
-        "sidebar",
-        "status",
-        "statusbar",
-        "toolbar",
-        "top",
-        "window",
-
-        // Window methods (https://developer.mozilla.org/en/DOM/window)
-        "alert",
-        "addEventListener",
-        "atob",
-        "back",
-        "blur",
-        "btoa",
-        "captureEvents",
-        "clearInterval",
-        "clearTimeout",
-        "close",
-        "confirm",
-        "disableExternalCapture",
-        "dispatchEvent",
-        "dump",
-        "enableExternalCapture",
-        "escape",
-        "find",
-        "focus",
-        "forward",
-        "GeckoActiveXObject",
-        "getAttention",
-        "getAttentionWithCycleCount",
-        "getComputedStyle",
-        "getSelection",
-        "home",
-        "maximize",
-        "minimize",
-        "moveBy",
-        "moveTo",
-        "open",
-        "openDialog",
-        "postMessage",
-        "print",
-        "prompt",
-        "QueryInterface",
-        "releaseEvents",
-        "removeEventListener",
-        "resizeBy",
-        "resizeTo",
-        "restore",
-        "routeEvent",
-        "scroll",
-        "scrollBy",
-        "scrollByLines",
-        "scrollByPages",
-        "scrollTo",
-        "setInterval",
-        "setResizeable",
-        "setTimeout",
-        "showModalDialog",
-        "sizeToContent",
-        "stop",
-        "uuescape",
-        "updateCommands",
-        "XPCNativeWrapper",
-        "XPCSafeJSOjbectWrapper",
-
-        // Mozilla Window event handlers, same cite
-        "onabort",
-        "onbeforeunload",
-        "onchange",
-        "onclick",
-        "onclose",
-        "oncontextmenu",
-        "ondragdrop",
-        "onerror",
-        "onfocus",
-        "onhashchange",
-        "onkeydown",
-        "onkeypress",
-        "onkeyup",
-        "onload",
-        "onmousedown",
-        "onmousemove",
-        "onmouseout",
-        "onmouseover",
-        "onmouseup",
-        "onmozorientation",
-        "onpaint",
-        "onreset",
-        "onresize",
-        "onscroll",
-        "onselect",
-        "onsubmit",
-        "onunload",
-
-        // Safari Web Content Guide
-        // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf
-        // WebKit Window member data, from WebKit DOM Reference
-        // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html)
-        // TODO(fredsa) Many, many more functions and member data to add
-        "ontouchcancel",
-        "ontouchend",
-        "ontouchmove",
-        "ontouchstart",
-        "ongesturestart",
-        "ongesturechange",
-        "ongestureend",
-
-        // extra window methods
-        "uneval",
-
-        // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7,
-        // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1
-        "getPrototypeOf",
-        "let",
-
-        // "future reserved words"
-        "abstract",
-        "int",
-        "short",
-        "boolean",
-        "interface",
-        "static",
-        "byte",
-        "long",
-        "char",
-        "final",
-        "native",
-        "synchronized",
-        "float",
-        "package",
-        "throws",
-        "goto",
-        "private",
-        "transient",
-        "implements",
-        "protected",
-        "volatile",
-        "double",
-        "public",
-
-        // IE methods
-        // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#)
-        "attachEvent",
-        "clientInformation",
-        "clipboardData",
-        "createPopup",
-        "dialogHeight",
-        "dialogLeft",
-        "dialogTop",
-        "dialogWidth",
-        "onafterprint",
-        "onbeforedeactivate",
-        "onbeforeprint",
-        "oncontrolselect",
-        "ondeactivate",
-        "onhelp",
-        "onresizeend",
-
-        // Common browser-defined identifiers not defined in ECMAScript
-        "event",
-        "external",
-        "Debug",
-        "Enumerator",
-        "Global",
-        "Image",
-        "ActiveXObject",
-        "VBArray",
-        "Components",
-
-        // Functions commonly defined on Object
-        "toString",
-        "getClass",
-        "constructor",
-        "prototype",
-
-        // Client-side JavaScript identifiers, which are needed for linkers
-        // that don't ensure GWT's window != $wnd, document != $doc, etc.
-        // Taken from the Rhino book, pg 715
-        "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient",
-        "CanvasPattern", "CanvasRenderingContext2D", "CDATASection",
-        "CharacterData", "Comment", "CSS2Properties", "CSSRule",
-        "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType",
-        "DOMException", "DOMImplementation", "DOMParser", "Element", "Event",
-        "ExternalInterface", "FlashPlayer", "Form", "Frame", "History",
-        "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image",
-        "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType",
-        "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin",
-        "ProcessingInstruction", "Range", "RangeException", "Screen", "Select",
-        "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
-        "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
-        "XPathException", "XPathResult", "XSLTProcessor",
-
-        /*
-         * These keywords trigger the loading of the java-plugin. For the
-         * next-generation plugin, this results in starting a new Java process.
-         */
-        "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
-        "JavaArray", "JavaMember",
-
-        // GWT-defined identifiers
-        "$wnd", "$doc", "$entry", "$moduleName", "$moduleBase", "$gwt_version",
-        "$sessionId",
-
-        // Identifiers used by JsStackEmulator; later set to obfuscatable
-        "$stack", "$stackDepth", "$location",
-
-        // TODO: prove why this is necessary or remove it
-        "call",};
-
-    for (int i = 0; i < commonBuiltins.length; i++) {
-      String ident = commonBuiltins[i];
-      this.doCreateName(ident, ident);
-    }
+  private Object writeReplace() {
+    return new SerializedForm();
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
index 5dc5ed9..a1ff03c 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
@@ -17,14 +17,10 @@
 
 import com.google.gwt.dev.js.JsKeywords;
 import com.google.gwt.dev.util.StringInterner;
-import com.google.gwt.dev.util.collect.Lists;
-import com.google.gwt.dev.util.collect.Maps;
 
 import java.io.Serializable;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 /**
  * A scope is a factory for creating and allocating
@@ -51,7 +47,7 @@
  * qualifier and could therefore never be confused with the global scope
  * hierarchy.
  */
-public class JsScope implements Serializable {
+public abstract class JsScope implements Serializable {
 
   /**
    * Prevents the client from programmatically creating an illegal ident.
@@ -63,27 +59,10 @@
     return StringInterner.get().intern(ident);
   }
 
-  private List<JsScope> children = Collections.emptyList();
   private final String description;
-  private Map<String, JsName> names = Collections.emptyMap();
-  private final JsScope parent;
 
-  /**
-   * Create a scope with parent.
-   */
-  public JsScope(JsScope parent, String description) {
-    assert (parent != null);
-    this.description = StringInterner.get().intern(description);
-    this.parent = parent;
-    parent.children = Lists.add(parent.children, this);
-  }
-
-  /**
-   * Subclasses can be parentless.
-   */
   protected JsScope(String description) {
     this.description = StringInterner.get().intern(description);
-    this.parent = null;
   }
 
   /**
@@ -92,7 +71,7 @@
    * 
    * @param ident An identifier that is unique within this scope.
    */
-  public JsName declareName(String ident) {
+  public final JsName declareName(String ident) {
     ident = maybeMangleKeyword(ident);
     JsName name = findExistingNameNoRecurse(ident);
     if (name != null) {
@@ -110,7 +89,7 @@
    * @throws IllegalArgumentException if ident already exists in this scope but
    *           the requested short name does not match the existing short name.
    */
-  public JsName declareName(String ident, String shortIdent) {
+  public final JsName declareName(String ident, String shortIdent) {
     ident = maybeMangleKeyword(ident);
     shortIdent = maybeMangleKeyword(shortIdent);
     JsName name = findExistingNameNoRecurse(ident);
@@ -134,8 +113,8 @@
   public final JsName findExistingName(String ident) {
     ident = maybeMangleKeyword(ident);
     JsName name = findExistingNameNoRecurse(ident);
-    if (name == null && parent != null) {
-      return parent.findExistingName(ident);
+    if (name == null && getParent() != null) {
+      return getParent().findExistingName(ident);
     }
     return name;
   }
@@ -152,8 +131,8 @@
     if (name != null && name.isObfuscatable()) {
       name = null;
     }
-    if (name == null && parent != null) {
-      return parent.findExistingUnobfuscatableName(ident);
+    if (name == null && getParent() != null) {
+      return getParent().findExistingUnobfuscatableName(ident);
     }
     return name;
   }
@@ -161,52 +140,34 @@
   /**
    * Returns an iterator for all the names defined by this scope.
    */
-  public Iterator<JsName> getAllNames() {
-    return names.values().iterator();
-  }
+  public abstract Iterator<JsName> getAllNames();
 
   /**
    * Returns a list of this scope's child scopes.
    */
-  public final List<JsScope> getChildren() {
-    return children;
-  }
+  public abstract List<JsScope> getChildren();
 
   /**
    * Returns the parent scope of this scope, or <code>null</code> if this is the
    * root scope.
    */
-  public final JsScope getParent() {
-    return parent;
-  }
-
-  /**
-   * Returns the associated program.
-   */
-  public JsProgram getProgram() {
-    assert (parent != null) : "Subclasses must override getProgram() if they do not set a parent";
-    return parent.getProgram();
-  }
+  public abstract JsScope getParent();
 
   @Override
   public final String toString() {
-    if (parent != null) {
-      return description + "->" + parent;
+    if (getParent() != null) {
+      return description + "->" + getParent();
     } else {
       return description;
     }
   }
 
+  protected abstract void addChild(JsScope child);
+
   /**
    * Creates a new name in this scope.
    */
-  protected JsName doCreateName(String ident, String shortIdent) {
-    ident = StringInterner.get().intern(ident);
-    shortIdent = StringInterner.get().intern(shortIdent);
-    JsName name = new JsName(this, ident, shortIdent);
-    names = Maps.putOrdered(names, ident, name);
-    return name;
-  }
+  protected abstract JsName doCreateName(String ident, String shortIdent);
 
   /**
    * Attempts to find the name object for the specified ident, searching in this
@@ -214,8 +175,5 @@
    * 
    * @return <code>null</code> if the identifier has no associated name
    */
-  protected JsName findExistingNameNoRecurse(String ident) {
-    return names.get(ident);
-  }
-
+  protected abstract JsName findExistingNameNoRecurse(String ident);
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java
index 6691d80..7eba50f 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java
@@ -25,8 +25,7 @@
 
   private final String value;
 
-  // These only get created by JsProgram so that they can be interned.
-  JsStringLiteral(SourceInfo sourceInfo, String value) {
+  public JsStringLiteral(SourceInfo sourceInfo, String value) {
     super(sourceInfo);
     this.value = StringInterner.get().intern(value);
   }
diff --git a/dev/core/src/com/google/gwt/dev/shell/Jsni.java b/dev/core/src/com/google/gwt/dev/shell/Jsni.java
index a5a2d45..5c2b519 100644
--- a/dev/core/src/com/google/gwt/dev/shell/Jsni.java
+++ b/dev/core/src/com/google/gwt/dev/shell/Jsni.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.dev.shell;
 
-import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.dev.javac.JsniMethod;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.JsSourceGenerationVisitor;
@@ -25,7 +24,8 @@
 import com.google.gwt.dev.js.ast.JsInvocation;
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsNode;
-import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsNullLiteral;
+import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.util.DefaultTextOutput;
 import com.google.gwt.dev.util.TextOutput;
 
@@ -85,14 +85,11 @@
       JsSourceGenerationVisitor {
     private final DispatchIdOracle dispatchInfo;
     private final TextOutput out;
-    private final JsProgram program;
 
-    public JsSourceGenWithJsniIdentFixup(TextOutput out, DispatchIdOracle ccl,
-        JsProgram program) {
+    public JsSourceGenWithJsniIdentFixup(TextOutput out, DispatchIdOracle ccl) {
       super(out);
       this.dispatchInfo = ccl;
       this.out = out;
-      this.program = program;
     }
 
     /**
@@ -149,11 +146,11 @@
 
         List<JsExpression> arguments = rewritten.getArguments();
         if (q == null) {
-          q = program.getNullLiteral();
+          q = JsNullLiteral.INSTANCE;
         }
         arguments.add(q);
-        arguments.add(program.getNumberLiteral(dispId));
-        arguments.add(program.getNumberLiteral(paramCount));
+        arguments.add(new JsNumberLiteral(newSourceInfo, dispId));
+        arguments.add(new JsNumberLiteral(newSourceInfo, paramCount));
 
         accept(rewritten);
         return false;
@@ -210,17 +207,18 @@
             JsInvocation inner = new JsInvocation(newSourceInfo);
             inner.setQualifier(new JsNameRef(newSourceInfo,
                 "__gwt_makeJavaInvoke"));
-            inner.getArguments().add(program.getNumberLiteral(paramCount));
+            inner.getArguments().add(
+                new JsNumberLiteral(newSourceInfo, paramCount));
 
             JsInvocation outer = new JsInvocation(newSourceInfo);
             outer.setQualifier(inner);
             JsExpression q = ref.getQualifier();
             if (q == null) {
-              q = program.getNullLiteral();
+              q = JsNullLiteral.INSTANCE;
             }
             List<JsExpression> arguments = outer.getArguments();
             arguments.add(q);
-            arguments.add(program.getNumberLiteral(dispId));
+            arguments.add(new JsNumberLiteral(newSourceInfo, dispId));
             arguments.addAll(x.getArguments());
 
             accept(outer);
@@ -240,14 +238,13 @@
    * 
    * @param logger a TreeLogger
    */
-  public static String getJavaScriptForHostedMode(TreeLogger logger,
+  public static String getJavaScriptForHostedMode(
       DispatchIdOracle dispatchInfo, JsniMethod jsniMethod) {
     JsFunction func = jsniMethod.function();
     if (func == null) {
       return null;
     }
-    return generateJavaScriptForHostedMode(dispatchInfo, jsniMethod.program(),
-        func.getBody());
+    return generateJavaScriptForHostedMode(dispatchInfo, func.getBody());
   }
 
   /**
@@ -255,10 +252,10 @@
    * JSNI idents have been replaced with legal JavaScript for hosted mode.
    */
   private static String generateJavaScriptForHostedMode(
-      DispatchIdOracle dispatchInfo, JsProgram program, JsNode node) {
+      DispatchIdOracle dispatchInfo, JsNode node) {
     DefaultTextOutput out = new DefaultTextOutput(false);
     JsSourceGenWithJsniIdentFixup vi = new JsSourceGenWithJsniIdentFixup(out,
-        dispatchInfo, program);
+        dispatchInfo);
     vi.accept(node);
     return out.toString();
   }
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java
index 97b2f05..8ef7931 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java
@@ -49,7 +49,7 @@
       if (jsniMethod.isScriptOnly()) {
         continue;
       }
-      String body = Jsni.getJavaScriptForHostedMode(logger, dispatchIdOracle,
+      String body = Jsni.getJavaScriptForHostedMode(dispatchIdOracle,
           jsniMethod);
       if (body == null) {
         // The error has been logged; just ignore it for now.
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
index 133e356..6a015b0 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -152,7 +152,7 @@
       code.append("  private final String name;\n");
       code.append("  private final int ordinal;\n");
       code.append("  public final String name() { return name; }\n");
-      code.append("  public final int ordinal() { return ordinal; }\n"); 
+      code.append("  public final int ordinal() { return ordinal; }\n");
       code.append("}\n");
       return code;
     }
@@ -231,7 +231,7 @@
 
     JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true);
 
-    CorrelationFactory correlator = new DummyCorrelationFactory();
+    CorrelationFactory correlator = DummyCorrelationFactory.INSTANCE;
     JProgram jprogram = new JProgram(correlator);
     JsProgram jsProgram = new JsProgram(correlator);
 
diff --git a/dev/core/test/com/google/gwt/dev/js/JsToStringGenerationVisitorAccuracyTest.java b/dev/core/test/com/google/gwt/dev/js/JsToStringGenerationVisitorAccuracyTest.java
index 590ffe4..5a99ddd 100644
--- a/dev/core/test/com/google/gwt/dev/js/JsToStringGenerationVisitorAccuracyTest.java
+++ b/dev/core/test/com/google/gwt/dev/js/JsToStringGenerationVisitorAccuracyTest.java
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.jjs.SourceOrigin;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsStringLiteral;
 import com.google.gwt.dev.js.ast.JsVisitor;
 import com.google.gwt.dev.util.DefaultTextOutput;
 import com.google.gwt.dev.util.TextOutput;
@@ -192,8 +193,7 @@
   }
 
   private void doTestEscapes(String value, String expected) {
-    String actual = new JsProgram().getStringLiteral(SourceOrigin.UNKNOWN,
-        value).toString();
+    String actual = new JsStringLiteral(SourceOrigin.UNKNOWN, value).toString();
     assertEquals(expected, actual);
   }