Add inital SourceInfo tracking to the GWT compiler to support Story Of Your Compile work.
 - Adds SourceInfo to JsNode contsructor and all JsNode types
 - SourceInfo objects must be constructed via JProgram or JsProgram
 - Add a sample, temporary report generator

Patch by: bobv
Review by: spoon


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3684 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
index a652c72..6d16a2e 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -33,6 +33,7 @@
 import com.google.gwt.dev.cfg.Script;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.JJSOptions;
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.JsObfuscateNamer;
 import com.google.gwt.dev.js.JsParser;
 import com.google.gwt.dev.js.JsParserException;
@@ -80,14 +81,20 @@
   private static class TopFunctionStringInterner extends JsModVisitor {
 
     public static boolean exec(JsProgram program) {
-      TopFunctionStringInterner v = new TopFunctionStringInterner();
+      TopFunctionStringInterner v = new TopFunctionStringInterner(program);
       v.accept(program);
       return v.didChange();
     }
 
+    private final JsProgram program;
+
+    public TopFunctionStringInterner(JsProgram program) {
+      this.program = program;
+    }
+
     @Override
     public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
-      didChange |= JsStringInterner.exec(x.getBody(), x.getScope());
+      didChange |= JsStringInterner.exec(program, x.getBody(), x.getScope());
       return false;
     }
   }
@@ -331,6 +338,8 @@
     funcName.setObfuscatable(false);
 
     try {
+      SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic("Linker-derived JS");
+      parser.setSourceInfo(sourceInfo);
       parser.parseInto(topScope, jsProgram.getGlobalBlock(), r, 1);
     } catch (IOException e) {
       logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e);
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index de85f00..b11b457 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -50,6 +50,7 @@
 import com.google.gwt.util.tools.ArgHandlerExtra;
 import com.google.gwt.util.tools.ArgHandlerFlag;
 import com.google.gwt.util.tools.ArgHandlerOutDir;
+import com.google.gwt.util.tools.ArgHandlerString;
 import com.google.gwt.util.tools.ToolBase;
 
 import java.io.File;
@@ -86,6 +87,29 @@
     }
   }
 
+  private class ArgHandlerStoryOfYourCompile extends ArgHandlerString {
+    @Override
+    public String getPurpose() {
+      return "Generate the story of your compile";
+    }
+
+    @Override
+    public String getTag() {
+      return "-soyc";
+    }
+
+    @Override
+    public String[] getTagArgs() {
+      return new String[] {"/path/to/report/dir"};
+    }
+
+    @Override
+    public boolean setString(String str) {
+      jjsOptions.setSoycOutputDir(str);
+      return true;
+    }
+  }
+
   /**
    * Argument handler for making the compiler run in "validation" mode.
    */
@@ -232,6 +256,8 @@
 
     registerHandler(new ArgHandlerScriptStyle(jjsOptions));
 
+    registerHandler(new ArgHandlerStoryOfYourCompile());
+
     registerHandler(new ArgHandlerEnableAssertions(jjsOptions));
 
     registerHandler(new ArgHandlerDisableAggressiveOptimization() {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java b/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java
index b6f0e77..50411c5 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -53,6 +53,8 @@
       JsProgram jsPgm = new JsProgram();
       JsParser jsParser = new JsParser();
       StringReader r = new StringReader(jsniSrc);
+      jsParser.setSourceInfo(jsPgm.createSourceInfoSynthetic("Default property provider for "
+          + getProperty().getName()));
       List<JsStatement> stmts = jsParser.parse(jsPgm.getScope(), r, 1);
       JsFunction fn = (JsFunction) ((JsExprStmt) stmts.get(0)).getExpression();
       return fn.getBody();
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
index f66bce6..f2ea0a7 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
@@ -241,6 +241,8 @@
       ModuleDefSchema schema = new ModuleDefSchema(logger, this, moduleURL,
           moduleDir, moduleDef);
       ReflectiveParser.parse(logger, schema, r);
+    } catch (Throwable e) {
+      e.printStackTrace();
     } finally {
       Utility.close(r);
     }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
index b7b9a5d..81effa0 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -914,8 +914,8 @@
   }
 
   /**
-   * Returns <code>true</code> if the string equals "true" or "yes" using a
-   * case insensitive comparison.
+   * Returns <code>true</code> if the string equals "true" or "yes" using a case
+   * insensitive comparison.
    */
   private static boolean toPrimitiveBoolean(String s) {
     return "yes".equalsIgnoreCase(s) || "true".equalsIgnoreCase(s);
@@ -1005,6 +1005,8 @@
     StringReader r = new StringReader(script);
     List<JsStatement> stmts;
     try {
+      // TODO Provide more context here
+      jsParser.setSourceInfo(jsPgm.createSourceInfoSynthetic("Module.xml"));
       stmts = jsParser.parse(jsPgm.getScope(), r, startLineNumber);
     } catch (IOException e) {
       logger.log(TreeLogger.ERROR, "Error reading script source", e);
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 6acae2c..323bce3 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
@@ -268,8 +268,9 @@
     }
 
     try {
-      List<JsStatement> stmts = new JsParser().parse(program.getScope(), r,
-          startLine);
+      JsParser parser = new JsParser();
+      parser.setSourceInfo(program.createSourceInfo(startLine, location));
+      List<JsStatement> stmts = parser.parse(program.getScope(), r, startLine);
 
       return (JsFunction) ((JsExprStmt) stmts.get(0)).getExpression();
     } catch (IOException e) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index a689917..c80e722 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -23,6 +23,7 @@
   private boolean aggressivelyOptimize = true;
   private boolean enableAssertions = false;
   private JsOutputOption output = JsOutputOption.OBFUSCATED;
+  private String soycOutputPath = null;
   private boolean validateOnly = false;
 
   public JJSOptions() {
@@ -35,6 +36,7 @@
   public void copyFrom(JJSOptions other) {
     this.aggressivelyOptimize = other.aggressivelyOptimize;
     this.enableAssertions = other.enableAssertions;
+    this.soycOutputPath = other.soycOutputPath;
     this.output = other.output;
     this.validateOnly = other.validateOnly;
   }
@@ -47,6 +49,13 @@
   }
 
   /**
+   * Returns the path of the SOYC output directory, if it has been set.
+   */
+  public String getSoycOutputDir() {
+    return soycOutputPath;
+  }
+
+  /**
    * Returns true if the compiler should aggressively optimize.
    */
   public boolean isAggressivelyOptimize() {
@@ -61,8 +70,8 @@
   }
 
   /**
-   * Returns true if the compiler should run in validation mode, not producing
-   * any output.
+   * /** Returns true if the compiler should run in validation mode, not
+   * producing any output.
    */
   public boolean isValidateOnly() {
     return validateOnly;
@@ -90,6 +99,13 @@
   }
 
   /**
+   * Sets the output path of the SOYC reports.
+   */
+  public void setSoycOutputDir(String path) {
+    this.soycOutputPath = path;
+  }
+
+  /**
    * Sets whether or not the compiler should run in validation mode.
    */
   public void setValidateOnly(boolean validateOnly) {
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 5e8d32a..826df7b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -69,6 +69,8 @@
 import com.google.gwt.dev.js.JsSymbolResolver;
 import com.google.gwt.dev.js.JsUnusedFunctionRemover;
 import com.google.gwt.dev.js.JsVerboseNamer;
+import com.google.gwt.dev.js.SourceInfoHistogram;
+import com.google.gwt.dev.js.SourceInfoHistogram.HistogramData;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.util.DefaultTextOutput;
 import com.google.gwt.dev.util.PerfLogger;
@@ -91,8 +93,8 @@
 import java.util.TreeSet;
 
 /**
- * Compiles the Java <code>JProgram</code> representation into its
- * corresponding JavaScript source.
+ * Compiles the Java <code>JProgram</code> representation into its corresponding
+ * JavaScript source.
  */
 public class JavaToJavaScriptCompiler {
 
@@ -127,9 +129,11 @@
       throw new UnableToCompleteException();
     }
 
+    SourceInfo sourceInfo = reboundEntryType.getSourceInfo().makeChild(
+        "Rebound entry point");
     JExpression qualifier = null;
     if (!entryMethod.isStatic()) {
-      qualifier = JGwtCreate.createInstantiationExpression(program, null,
+      qualifier = JGwtCreate.createInstantiationExpression(program, sourceInfo,
           entryClass);
 
       if (qualifier == null) {
@@ -142,14 +146,16 @@
         throw new UnableToCompleteException();
       }
     }
-    return new JMethodCall(program, null, qualifier, entryMethod);
+    return new JMethodCall(program, sourceInfo, qualifier, entryMethod);
   }
 
   private static void findEntryPoints(TreeLogger logger,
       RebindPermutationOracle rpo, String[] mainClassNames, JProgram program)
       throws UnableToCompleteException {
-    JMethod bootStrapMethod = program.createMethod(null, "init".toCharArray(),
-        null, program.getTypeVoid(), false, true, true, false, false);
+    JMethod bootStrapMethod = program.createMethod(
+        program.createSourceInfoSynthetic("Bootstrap method"),
+        "init".toCharArray(), null, program.getTypeVoid(), false, true, true,
+        false, false);
     bootStrapMethod.freezeParamTypes();
 
     JMethodBody body = (JMethodBody) bootStrapMethod.getBody();
@@ -242,16 +248,18 @@
    */
   private static JStatement makeStatsCalls(JProgram program,
       String mainClassName) {
+    SourceInfo sourceInfo = program.createSourceInfoSynthetic("onModuleStart() stats call");
     JMethod isStatsAvailableMethod = program.getIndexedMethod("Stats.isStatsAvailable");
     JMethod onModuleStartMethod = program.getIndexedMethod("Stats.onModuleStart");
 
-    JMethodCall availableCall = new JMethodCall(program, null, null,
+    JMethodCall availableCall = new JMethodCall(program, sourceInfo, null,
         isStatsAvailableMethod);
-    JMethodCall onModuleStartCall = new JMethodCall(program, null, null,
+    JMethodCall onModuleStartCall = new JMethodCall(program, sourceInfo, null,
         onModuleStartMethod);
-    onModuleStartCall.getArgs().add(program.getLiteralString(mainClassName));
+    onModuleStartCall.getArgs().add(
+        program.getLiteralString(sourceInfo, mainClassName));
 
-    JBinaryOperation amp = new JBinaryOperation(program, null,
+    JBinaryOperation amp = new JBinaryOperation(program, sourceInfo,
         program.getTypePrimitiveBoolean(), JBinaryOperator.AND, availableCall,
         onModuleStartCall);
 
@@ -260,6 +268,7 @@
 
   private final long astMemoryUsage;
   private final String[] declEntryPoints;
+  private final HistogramData histogramData;
   private final Object myLockObject = new Object();
   private final JJSOptions options;
   private final Set<IProblem> problemSet = new HashSet<IProblem>();
@@ -312,8 +321,11 @@
     checkForErrors(logger, goldenCuds, false);
 
     PerfLogger.start("Build AST");
+    boolean enableDescendants = compilerOptions.getSoycOutputDir() != null;
     JProgram jprogram = savedJProgram = new JProgram();
+    jprogram.setEnableSourceInfoDescendants(enableDescendants);
     JsProgram jsProgram = savedJsProgram = new JsProgram();
+    jsProgram.setEnableSourceInfoDescendants(enableDescendants);
 
     long memoryDelta;
     try {
@@ -331,6 +343,12 @@
       // BuildTypeMap can uncover syntactic JSNI errors; report & abort
       checkForErrors(logger, goldenCuds, true);
 
+      if (enableDescendants) {
+        histogramData = SourceInfoHistogram.exec(jprogram);
+      } else {
+        histogramData = null;
+      }
+
       // Compute all super type/sub type info
       jprogram.typeOracle.computeBeforeAST();
 
@@ -567,6 +585,12 @@
     // http://code.google.com/p/google-web-toolkit/issues/detail?id=1440
     JsIEBlockSizeVisitor.exec(jsProgram);
 
+    // Write the SOYC reports into the output
+    if (histogramData != null) {
+      SourceInfoHistogram.exec(jsProgram, histogramData,
+          options.getSoycOutputDir());
+    }
+
     // (12) Generate the final output text.
     DefaultTextOutput out = new DefaultTextOutput(
         options.getOutput().shouldMinimize());
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 7debc2c..a95794e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
@@ -16,22 +16,107 @@
 package com.google.gwt.dev.jjs;
 
 import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Tracks file and line information for AST nodes.
  */
 public class SourceInfo implements Serializable {
 
+  /**
+   * Indicates that the source for an AST element is unknown. This indicates a
+   * deficiency in the compiler.
+   */
+  public static final SourceInfo UNKNOWN = new SourceInfo(0, 0, 0,
+      "Unknown source", true);
+
+  /**
+   * Examines the call stack to automatically determine a useful value to
+   * provide as the caller argument to SourceInfo factory methods.
+   */
+  public static String findCaller() {
+    /*
+     * TODO(bobv): This function needs to be made robust for middle-man callers
+     * other than JProgram and JsProgram.
+     */
+    String sourceInfoClassName = SourceInfo.class.getName();
+    boolean wasInFindCaller = false;
+
+    for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
+      boolean inSourceInfo = e.getClassName().equals(SourceInfo.class.getName());
+      if (inSourceInfo && "findCaller".equals(e.getMethodName())) {
+        wasInFindCaller = true;
+      } else if (wasInFindCaller
+          && !"createSourceInfoSynthetic".equals(e.getMethodName())
+          && !sourceInfoClassName.equals(e.getClassName())) {
+        return e.getClassName() + "." + e.getMethodName();
+      }
+    }
+
+    return "Unknown caller";
+  }
+
+  private final Set<SourceInfo> additionalAncestors = new HashSet<SourceInfo>();
+  private final String caller;
+  /**
+   * This flag controls the behaviors of {@link #makeSynthetic(String)} and
+   * {@link #makeChild(String)}.
+   */
+  private final boolean createDescendants;
   private final int endPos;
   private final String fileName;
+  private transient Reference<Collection<SourceInfo>> lazyRoots;
+  private final String mutation;
+  private final SourceInfo parent;
   private final int startLine;
   private final int startPos;
 
-  public SourceInfo(int startPos, int endPos, int startLine, String fileName) {
+  protected SourceInfo(int startPos, int endPos, int startLine,
+      String fileName, boolean createDescendants) {
+    this.createDescendants = createDescendants;
     this.startPos = startPos;
     this.endPos = endPos;
     this.startLine = startLine;
     this.fileName = fileName;
+    this.parent = null;
+    this.mutation = null;
+    this.caller = null;
+  }
+
+  private SourceInfo(SourceInfo parent, String mutation, String caller,
+      SourceInfo... additionalAncestors) {
+    this.createDescendants = parent.createDescendants;
+    this.startPos = parent.startPos;
+    this.endPos = parent.endPos;
+    this.startLine = parent.startLine;
+    this.fileName = parent.fileName;
+    this.additionalAncestors.addAll(Arrays.asList(additionalAncestors));
+    this.additionalAncestors.addAll(parent.additionalAncestors);
+    this.parent = parent;
+    this.mutation = mutation;
+    this.caller = caller;
+  }
+
+  public synchronized void addAdditonalAncestors(SourceInfo... sourceInfos) {
+    for (SourceInfo ancestor : sourceInfos) {
+      if (ancestor == this) {
+        continue;
+      } else if (additionalAncestors.contains(ancestor)) {
+        continue;
+      } else {
+        additionalAncestors.add(ancestor);
+      }
+    }
+    if (lazyRoots != null) {
+      lazyRoots.clear();
+    }
   }
 
   public int getEndPos() {
@@ -42,6 +127,41 @@
     return fileName;
   }
 
+  /**
+   * Returns the SourceInfos from which this SourceInfo was ultimately derived.
+   * SourceInfo objects which were not derived from others, via
+   * {@link #makeChild}, will list itself as its root SourceInfo.
+   */
+  public synchronized Collection<SourceInfo> getRoots() {
+    if (parent == null) {
+      // If parent is null, we shouldn't have additional ancestors
+      assert additionalAncestors.size() == 0;
+      return Collections.singleton(this);
+
+    } else if (additionalAncestors.size() == 0) {
+      // This is a fairly typical case, where a node only has a parent
+      return parent.getRoots();
+    }
+
+    // See if previously-computed work is available
+    Collection<SourceInfo> roots;
+    if (lazyRoots != null && (roots = lazyRoots.get()) != null) {
+      return roots;
+    }
+
+    // Otherwise, do some actual work
+    roots = new ArrayList<SourceInfo>();
+
+    roots.addAll(parent.getRoots());
+    for (SourceInfo ancestor : additionalAncestors) {
+      roots.addAll(ancestor.getRoots());
+    }
+
+    Collection<SourceInfo> toReturn = Collections.unmodifiableCollection(roots);
+    lazyRoots = new SoftReference<Collection<SourceInfo>>(toReturn);
+    return toReturn;
+  }
+
   public int getStartLine() {
     return startLine;
   }
@@ -49,4 +169,50 @@
   public int getStartPos() {
     return startPos;
   }
+
+  public String getStory() {
+    /*
+     * TODO(bobv): This is a temporary implementation. At some point it should
+     * return a proper object which tools could use.
+     */
+    StringBuilder toReturn = new StringBuilder();
+
+    if (caller != null) {
+      SourceInfo si = this;
+      while (si != null) {
+        if (si.mutation != null) {
+          toReturn.append(si.mutation + " by " + si.caller + "\n  ");
+        }
+        si = si.parent;
+      }
+    }
+
+    for (SourceInfo root : getRoots()) {
+      toReturn.append(root.fileName + ":" + root.startLine + "\n");
+    }
+
+    return toReturn.toString();
+  }
+
+  /**
+   * Create a derived SourceInfo object. If SourceInfo collection is disabled,
+   * this method will return the current object.
+   */
+  public SourceInfo makeChild(String description) {
+    return makeChild(description, new SourceInfo[0]);
+  }
+
+  /**
+   * Create a derived SourceInfo object that indicates that one or more AST
+   * nodes were merged to create a new node. The derived node will inherit its
+   * location from the SourceInfo object on which the method is invoked.
+   */
+  public SourceInfo makeChild(String description,
+      SourceInfo... additionalAncestors) {
+    if (!createDescendants) {
+      return this;
+    }
+
+    return new SourceInfo(this, description, findCaller(), additionalAncestors);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JAbsentArrayDimension.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JAbsentArrayDimension.java
index 09c0a5f..e4cff33 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JAbsentArrayDimension.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JAbsentArrayDimension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents an array dimension that was not specified in an array
  * instantiation expression.
@@ -24,8 +26,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JAbsentArrayDimension(JProgram program) {
-    super(program);
+  JAbsentArrayDimension(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo);
   }
 
   public JType getType() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
index a220699..78688e6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
@@ -35,7 +35,8 @@
    * These are only supposed to be constructed by JProgram.
    */
   JArrayType(JProgram program, JType leafType, int dims) {
-    super(program, null, calcName(leafType, dims), false, false);
+    super(program, leafType.getSourceInfo().makeChild("Array type"), calcName(
+        leafType, dims), false, false);
     this.leafType = leafType;
     this.dims = dims;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBooleanLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBooleanLiteral.java
index 4499762..34acce3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBooleanLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBooleanLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java boolean literal expression.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JBooleanLiteral(JProgram program, boolean value) {
-    super(program);
+  JBooleanLiteral(JProgram program, SourceInfo sourceInfo, boolean value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JCharLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JCharLiteral.java
index 1e71bfa..361971d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JCharLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JCharLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java character literal expression.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JCharLiteral(JProgram program, char value) {
-    super(program);
+  JCharLiteral(JProgram program, SourceInfo sourceInfo, char value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
index a2c27fa..032c381 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
@@ -39,8 +39,8 @@
     assert method != null;
 
     JMethodCall call = new JMethodCall(program, info, null, method);
-    call.getArgs().add(program.getLiteralString(getPackageName(typeName)));
-    call.getArgs().add(program.getLiteralString(getClassName(typeName)));
+    call.getArgs().add(program.getLiteralString(info, getPackageName(typeName)));
+    call.getArgs().add(program.getLiteralString(info, getClassName(typeName)));
 
     if (type instanceof JClassType && !(type instanceof JArrayType)) {
       /*
@@ -121,8 +121,9 @@
   /**
    * This constructor is only used by {@link JProgram}.
    */
-  JClassLiteral(JProgram program, JType type, JField field) {
-    super(program);
+  JClassLiteral(JProgram program, SourceInfo sourceInfo, JType type,
+      JField field) {
+    super(program, sourceInfo);
     refType = type;
     this.field = field;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDoubleLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDoubleLiteral.java
index df35d14..2429c0e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDoubleLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDoubleLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java double literal expression.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JDoubleLiteral(JProgram program, double value) {
-    super(program);
+  JDoubleLiteral(JProgram program, SourceInfo sourceInfo, double value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java
index 289f99d..25efecc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java literal typed as a float.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JFloatLiteral(JProgram program, float value) {
-    super(program);
+  JFloatLiteral(JProgram program, SourceInfo sourceInfo, float value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JIntLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JIntLiteral.java
index 6c01603..a502ffd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JIntLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JIntLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java integer literal expression.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JIntLiteral(JProgram program, int value) {
-    super(program);
+  JIntLiteral(JProgram program, SourceInfo sourceInfo, int value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLiteral.java
index 3c3b5ff..4798701 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,13 +15,15 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Base class for any Java literal expression.
  */
 public abstract class JLiteral extends JExpression {
 
-  public JLiteral(JProgram program) {
-    super(program, null);
+  public JLiteral(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo);
   }
 
   public boolean hasSideEffects() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLongLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLongLiteral.java
index ba73371..2e4a075 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLongLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLongLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java literal expression that evaluates to a Long.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JLongLiteral(JProgram program, long value) {
-    super(program);
+  JLongLiteral(JProgram program, SourceInfo sourceInfo, long value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
index 88fc652..761bd2e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
@@ -38,7 +38,14 @@
     } else {
       this.program = program;
     }
-    this.info = info;
+    
+    if (info != null) {
+      this.info = info;
+    } else {
+      // This indicates a deficiency in the compiler
+      this.info = SourceInfo.UNKNOWN.makeChild("Unknown "
+          + getClass().getSimpleName());
+    }
   }
 
   public JProgram getProgram() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullLiteral.java
index cfd99c7..6d9a8b2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java null literal expression.
  */
@@ -23,8 +25,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JNullLiteral(JProgram program) {
-    super(program);
+  JNullLiteral(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
index bc52521..3f69adb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,14 +16,15 @@
 package com.google.gwt.dev.jjs.ast;
 
 import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.SourceInfo;
 
 /**
  * Java null reference type.
  */
 public class JNullType extends JReferenceType {
 
-  public JNullType(JProgram program) {
-    super(program, null, "<null>");
+  public JNullType(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo, "<null>");
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
index 320b1c5..efa30c3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
@@ -28,7 +28,8 @@
    */
   JPrimitiveType(JProgram program, String name, String signatureName,
       String wrapperTypeName, JLiteral defaultValue) {
-    super(program, null, name, defaultValue);
+    super(program, program.createSourceInfoSynthetic(name + " primitive type"),
+        name, defaultValue);
     this.signatureName = signatureName;
     this.wrapperTypeName = wrapperTypeName;
   }
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 0d3e6b7..7fdb9ae 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
@@ -172,6 +172,8 @@
    */
   private final ArrayList<HashMap<JType, JArrayType>> dimensions = new ArrayList<HashMap<JType, JArrayType>>();
 
+  private boolean enableSourceInfoDescendants;
+
   private final Map<String, JField> indexedFields = new HashMap<String, JField>();
 
   private final Map<String, JMethod> indexedMethods = new HashMap<String, JMethod>();
@@ -183,19 +185,25 @@
   private List<JsonObject> jsonTypeTable;
 
   private final JAbsentArrayDimension literalAbsentArrayDim = new JAbsentArrayDimension(
-      this);
+      this, createSourceInfoSynthetic("Absent array dimension"));
 
-  private final JBooleanLiteral literalFalse = new JBooleanLiteral(this, false);
+  private final JBooleanLiteral literalFalse = new JBooleanLiteral(this,
+      createSourceInfoSynthetic("false literal"), false);
 
-  private final JIntLiteral literalIntNegOne = new JIntLiteral(this, -1);
+  private final JIntLiteral literalIntNegOne = new JIntLiteral(this,
+      createSourceInfoSynthetic("-1 literal"), -1);
 
-  private final JIntLiteral literalIntOne = new JIntLiteral(this, 1);
+  private final JIntLiteral literalIntOne = new JIntLiteral(this,
+      createSourceInfoSynthetic("1 literal"), 1);
 
-  private final JIntLiteral literalIntZero = new JIntLiteral(this, 0);
+  private final JIntLiteral literalIntZero = new JIntLiteral(this,
+      createSourceInfoSynthetic("0 literal"), 0);
 
-  private final JNullLiteral literalNull = new JNullLiteral(this);
+  private final JNullLiteral literalNull = new JNullLiteral(this,
+      createSourceInfoSynthetic("null literal"));
 
-  private final JBooleanLiteral literalTrue = new JBooleanLiteral(this, true);
+  private final JBooleanLiteral literalTrue = new JBooleanLiteral(this,
+      createSourceInfoSynthetic("true literal"), true);
 
   private JField nullField;
 
@@ -241,7 +249,8 @@
 
   private final Map<String, JReferenceType> typeNameMap = new HashMap<String, JReferenceType>();
 
-  private final JNullType typeNull = new JNullType(this);
+  private final JNullType typeNull = new JNullType(this,
+      createSourceInfoSynthetic("null type"));
 
   private final JPrimitiveType typeShort = new JPrimitiveType(this, "short",
       "S", "java.lang.Short", literalIntZero);
@@ -256,7 +265,7 @@
       "java.lang.Void", null);
 
   public JProgram() {
-    super(null, null);
+    super(null, SourceInfoJava.INTRINSIC.makeChild("Top-level program"));
   }
 
   public void addEntryMethod(JMethod entryPoint) {
@@ -431,6 +440,34 @@
     return x;
   }
 
+  /**
+   * Create a SourceInfo object when the source is derived from a physical
+   * location.
+   */
+  public SourceInfo createSourceInfo(int startPos, int endPos, int startLine,
+      String fileName) {
+    return new SourceInfoJava(startPos, endPos, startLine, fileName,
+        enableSourceInfoDescendants);
+  }
+
+  /**
+   * Create a SourceInfo object when the source is derived from a physical
+   * location.
+   */
+  public SourceInfo createSourceInfo(int startLine, String fileName) {
+    return new SourceInfoJava(-1, -1, startLine, fileName,
+        enableSourceInfoDescendants);
+  }
+
+  /**
+   * Create a SourceInfo object when the source is created by the compiler
+   * itself.
+   */
+  public SourceInfo createSourceInfoSynthetic(String description) {
+    return createSourceInfo(0, SourceInfoJava.findCaller()).makeChild(
+        description);
+  }
+
   public JReferenceType generalizeTypes(
       Collection<? extends JReferenceType> types) {
     assert (types != null);
@@ -509,7 +546,7 @@
 
   public JCharLiteral getLiteralChar(char c) {
     // could be interned
-    return new JCharLiteral(this, c);
+    return new JCharLiteral(this, createSourceInfoSynthetic(c + " literal"), c);
   }
 
   /**
@@ -547,7 +584,9 @@
           0).getBody();
       clinitBody.getStatements().add(decl);
 
-      classLiteral = new JClassLiteral(this, type, field);
+      classLiteral = new JClassLiteral(this,
+          createSourceInfoSynthetic("class literal for " + type.getName()),
+          type, field);
       classLiterals.put(type, classLiteral);
     } else {
       // Make sure the field hasn't been pruned.
@@ -567,17 +606,18 @@
    */
   public JClassSeed getLiteralClassSeed(JClassType type) {
     // could be interned
-    return new JClassSeed(this, type);
+    return new JClassSeed(this, createSourceInfoSynthetic("class seed"), type);
   }
 
   public JDoubleLiteral getLiteralDouble(double d) {
     // could be interned
-    return new JDoubleLiteral(this, d);
+    return new JDoubleLiteral(this, createSourceInfoSynthetic(d + " literal"),
+        d);
   }
 
   public JFloatLiteral getLiteralFloat(float f) {
     // could be interned
-    return new JFloatLiteral(this, f);
+    return new JFloatLiteral(this, createSourceInfoSynthetic(f + " literal"), f);
   }
 
   public JIntLiteral getLiteralInt(int i) {
@@ -590,40 +630,41 @@
         return literalIntOne;
       default:
         // could be interned
-        return new JIntLiteral(this, i);
+        return new JIntLiteral(this, createSourceInfoSynthetic(i + " literal"),
+            i);
     }
   }
 
   public JLongLiteral getLiteralLong(long l) {
-    return new JLongLiteral(this, l);
+    return new JLongLiteral(this, createSourceInfoSynthetic(l + " literal"), l);
   }
 
   public JNullLiteral getLiteralNull() {
     return literalNull;
   }
 
-  public JStringLiteral getLiteralString(char[] s) {
+  public JStringLiteral getLiteralString(SourceInfo sourceInfo, char[] s) {
     // should consolidate so we can build a string table in output code later?
-    return new JStringLiteral(this, String.valueOf(s));
+    return new JStringLiteral(this, sourceInfo, String.valueOf(s));
   }
 
-  public JStringLiteral getLiteralString(String s) {
+  public JStringLiteral getLiteralString(SourceInfo sourceInfo, String s) {
     // should consolidate so we can build a string table in output code later?
-    return new JStringLiteral(this, s);
+    return new JStringLiteral(this, sourceInfo, s);
   }
 
   public JField getNullField() {
     if (nullField == null) {
-      nullField = new JField(this, null, "nullField", null, typeNull, false,
-          Disposition.FINAL);
+      nullField = new JField(this, createSourceInfoSynthetic("Null field"),
+          "nullField", null, typeNull, false, Disposition.FINAL);
     }
     return nullField;
   }
 
   public JMethod getNullMethod() {
     if (nullMethod == null) {
-      nullMethod = new JMethod(this, null, "nullMethod", null, typeNull, false,
-          false, true, true);
+      nullMethod = new JMethod(this, createSourceInfoSynthetic("Null method"),
+          "nullMethod", null, typeNull, false, false, true, true);
     }
     return nullMethod;
   }
@@ -783,9 +824,19 @@
   }
 
   /**
-   * If <code>method</code> is a static impl method, returns the instance
-   * method that <code>method</code> is the implementation of. Otherwise,
-   * returns <code>null</code>.
+   * Controls whether or not SourceInfo nodes created via the JProgram will
+   * record descendant information. Enabling this feature will collect extra
+   * data during the compilation cycle, but at a cost of memory and object
+   * allocations.
+   */
+  public void setEnableSourceInfoDescendants(boolean enable) {
+    enableSourceInfoDescendants = enable;
+  }
+
+  /**
+   * If <code>method</code> is a static impl method, returns the instance method
+   * that <code>method</code> is the implementation of. Otherwise, returns
+   * <code>null</code>.
    */
   public JMethod staticImplFor(JMethod method) {
     return staticToInstanceMap.get(method);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java
index be45a1f..d6f3df2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Java literal expression that evaluates to a string.
  */
@@ -25,8 +27,8 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JStringLiteral(JProgram program, String value) {
-    super(program);
+  JStringLiteral(JProgram program, SourceInfo sourceInfo, String value) {
+    super(program, sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java
index 5d80cfb..35334b5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,13 +15,15 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Base class for any Java literal expression.
  */
 public abstract class JValueLiteral extends JLiteral {
 
-  public JValueLiteral(JProgram program) {
-    super(program);
+  public JValueLiteral(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo);
   }
 
   public abstract Object getValueObj();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/SourceInfoJava.java b/dev/core/src/com/google/gwt/dev/jjs/ast/SourceInfoJava.java
new file mode 100644
index 0000000..b817334
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/SourceInfoJava.java
@@ -0,0 +1,40 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * An implementation of SourceInfo representing SourceInfo nodes derived from
+ * the Java AST. Instances of this class should only be constructed by JProgram.
+ */
+class SourceInfoJava extends SourceInfo {
+  /**
+   * Indicates that an AST element is an intrinsic element of the AST and has no
+   * meaningful source location. This is typically used by singleton AST
+   * elements or for literal values.
+   */
+  public static final SourceInfo INTRINSIC = new SourceInfoJava(0, 0, 0,
+      "Java intrinsics", true);
+
+  /**
+   * Called only from JProgram.
+   */
+  SourceInfoJava(int startPos, int endPos, int startLine, String fileName,
+      boolean createDescendants) {
+    super(startPos, endPos, startLine, fileName, createDescendants);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JClassSeed.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JClassSeed.java
index 07fa28c..8aefa08 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JClassSeed.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JClassSeed.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JLiteral;
@@ -33,8 +34,8 @@
    */
   private final JClassType refType;
 
-  public JClassSeed(JProgram program, JClassType type) {
-    super(program);
+  public JClassSeed(JProgram program, SourceInfo sourceInfo, JClassType type) {
+    super(program, sourceInfo);
     refType = type;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
index 4bf9d26..9221003 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
@@ -32,8 +33,8 @@
 
   public List<JExpression> exprs = new ArrayList<JExpression>();
 
-  public JsonArray(JProgram program) {
-    super(program, null);
+  public JsonArray(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo);
   }
 
   public JType getType() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java
index 8602bc0..f1b8c83 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
@@ -39,9 +40,9 @@
     public JExpression labelExpr;
     public JExpression valueExpr;
 
-    public JsonPropInit(JProgram program, JExpression labelExpr,
+    public JsonPropInit(JProgram program, SourceInfo sourceInfo, JExpression labelExpr,
         JExpression valueExpr) {
-      super(program, null);
+      super(program, sourceInfo);
       this.labelExpr = labelExpr;
       this.valueExpr = valueExpr;
     }
@@ -57,8 +58,8 @@
 
   public final List<JsonPropInit> propInits = new ArrayList<JsonPropInit>();
 
-  public JsonObject(JProgram program) {
-    super(program, null);
+  public JsonObject(JProgram program, SourceInfo sourceInfo) {
+    super(program, sourceInfo);
   }
 
   public JType getType() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index 4c0ac80..86d1727 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
@@ -137,12 +138,13 @@
     private void processDims(JNewArray x, Context ctx, JArrayType arrayType,
         int dims) {
       // override the type of the called method with the array's type
-      JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
-          initDims, arrayType);
-      JsonArray classLitList = new JsonArray(program);
-      JsonArray typeIdList = new JsonArray(program);
-      JsonArray queryIdList = new JsonArray(program);
-      JsonArray dimList = new JsonArray(program);
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild("Creating dimensions");
+      JMethodCall call = new JMethodCall(program, sourceInfo, null, initDims,
+          arrayType);
+      JsonArray classLitList = new JsonArray(program, sourceInfo);
+      JsonArray typeIdList = new JsonArray(program, sourceInfo);
+      JsonArray queryIdList = new JsonArray(program, sourceInfo);
+      JsonArray dimList = new JsonArray(program, sourceInfo);
       JType cur = arrayType;
       for (int i = 0; i < dims; ++i) {
         // Walk down each type from most dims to least.
@@ -172,12 +174,13 @@
     private void processInitializers(JNewArray x, Context ctx,
         JArrayType arrayType) {
       // override the type of the called method with the array's type
-      JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild("Array initializer");
+      JMethodCall call = new JMethodCall(program, sourceInfo, null,
           initValues, arrayType);
       JLiteral classLit = x.getClassLiteral();
       JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
-      JsonArray initList = new JsonArray(program);
+      JsonArray initList = new JsonArray(program, sourceInfo);
       for (int i = 0; i < x.initializers.size(); ++i) {
         initList.exprs.add(x.initializers.get(i));
       }
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 1c641a4..67467d7 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
@@ -114,40 +114,14 @@
    */
   private static class BuildDeclMapVisitor extends ASTVisitor {
 
-    private static SourceInfo makeSourceInfo(
-        AbstractMethodDeclaration methodDecl) {
-      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);
-      return new SourceInfo(methodDecl.sourceStart, methodDecl.bodyEnd,
-          startLine, fileName);
-    }
-
-    private static InternalCompilerException translateException(
-        AbstractMethodDeclaration amd, Throwable e) {
-      if (e instanceof OutOfMemoryError) {
-        // Always rethrow OOMs (might have no memory to load ICE class anyway).
-        throw (OutOfMemoryError) e;
-      }
-      InternalCompilerException ice;
-      if (e instanceof InternalCompilerException) {
-        ice = (InternalCompilerException) e;
-      } else {
-        ice = new InternalCompilerException("Error building type map", e);
-      }
-      ice.addNode(amd.getClass().getName(), amd.toString(), makeSourceInfo(amd));
-      return ice;
-    }
-
     private String currentFileName;
+
     private int[] currentSeparatorPositions;
+
     private final JsParser jsParser = new JsParser();
     private final JsProgram jsProgram;
     private JProgram program;
     private List<TypeDeclaration> typeDecls = new ArrayList<TypeDeclaration>();
-
     private final TypeMap typeMap;
 
     public BuildDeclMapVisitor(TypeMap typeMap, JsProgram jsProgram) {
@@ -378,8 +352,10 @@
     private JField createField(SyntheticArgumentBinding binding,
         JReferenceType enclosingType) {
       JType type = (JType) typeMap.get(binding.type);
-      JField field = program.createField(null, binding.name, enclosingType,
-          type, false, Disposition.FINAL);
+      JField field = program.createField(
+          enclosingType.getSourceInfo().makeChild(
+              "Field " + String.valueOf(binding.name)), binding.name,
+          enclosingType, type, false, Disposition.FINAL);
       if (binding.matchingField != null) {
         typeMap.put(binding.matchingField, field);
       }
@@ -400,8 +376,9 @@
     private JParameter createParameter(SyntheticArgumentBinding arg,
         String argName, JMethod enclosingMethod) {
       JType type = (JType) typeMap.get(arg.type);
-      JParameter param = program.createParameter(null, argName.toCharArray(),
-          type, true, false, enclosingMethod);
+      JParameter param = program.createParameter(
+          enclosingMethod.getSourceInfo().makeChild("Parameter " + argName),
+          argName.toCharArray(), type, true, false, enclosingMethod);
       return param;
     }
 
@@ -421,15 +398,18 @@
       JClassType type = (JClassType) constructor.getEnclosingType();
 
       // Define the method
-      JMethod synthetic = program.createMethod(null, "new".toCharArray(), type,
-          type, false, true, true, false, false);
+      JMethod synthetic = program.createMethod(type.getSourceInfo().makeChild(
+          "Synthetic constructor"), "new".toCharArray(), type, type, false,
+          true, true, false, false);
 
       // new Foo() : Create the instance
-      JNewInstance newInstance = new JNewInstance(program, null, type);
+      JNewInstance newInstance = new JNewInstance(program,
+          type.getSourceInfo().makeChild("new instance"), type);
 
       // (new Foo()).Foo() : Invoke the constructor method on the instance
-      JMethodCall call = new JMethodCall(program, null, newInstance,
-          constructor);
+      JMethodCall call = new JMethodCall(program,
+          type.getSourceInfo().makeChild("constructor invocation"),
+          newInstance, constructor);
       List<JExpression> args = call.getArgs();
 
       /*
@@ -439,7 +419,8 @@
        */
       JParameter enclosingInstance = null;
       if (!staticClass) {
-        enclosingInstance = program.createParameter(null,
+        enclosingInstance = program.createParameter(
+            synthetic.getSourceInfo().makeChild("outer instance"),
             "this$outer".toCharArray(), enclosingType, false, false, synthetic);
       }
 
@@ -454,12 +435,17 @@
          * implicitly as the last argument to the constructor.
          */
         if (enclosingInstance != null && !i.hasNext()) {
-          args.add(new JParameterRef(program, null, enclosingInstance));
+          args.add(new JParameterRef(program,
+              synthetic.getSourceInfo().makeChild("enclosing instance"),
+              enclosingInstance));
         } else {
-          JParameter syntheticParam = program.createParameter(null,
+          JParameter syntheticParam = program.createParameter(
+              synthetic.getSourceInfo().makeChild("Argument " + param.getName()),
               param.getName().toCharArray(), param.getType(), true, false,
               synthetic);
-          args.add(new JParameterRef(program, null, syntheticParam));
+          args.add(new JParameterRef(program,
+              syntheticParam.getSourceInfo().makeChild("reference"),
+              syntheticParam));
         }
       }
 
@@ -467,7 +453,8 @@
       synthetic.freezeParamTypes();
 
       // return (new Foo()).Foo() : The only statement in the function
-      JReturnStatement ret = new JReturnStatement(program, null, call);
+      JReturnStatement ret = new JReturnStatement(program,
+          synthetic.getSourceInfo().makeChild("Return statement"), call);
 
       // Add the return statement to the method body
       JMethodBody body = (JMethodBody) synthetic.getBody();
@@ -499,11 +486,21 @@
       return (JMethodBody) method.getBody();
     }
 
+    private SourceInfo makeSourceInfo(AbstractMethodDeclaration methodDecl) {
+      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);
+      return program.createSourceInfo(methodDecl.sourceStart,
+          methodDecl.bodyEnd, startLine, fileName);
+    }
+
     private SourceInfo makeSourceInfo(Statement stmt) {
       int startLine = Util.getLineNumber(stmt.sourceStart,
           currentSeparatorPositions, 0, currentSeparatorPositions.length - 1);
-      return new SourceInfo(stmt.sourceStart, stmt.sourceEnd, startLine,
-          currentFileName);
+      return program.createSourceInfo(stmt.sourceStart, stmt.sourceEnd,
+          startLine, currentFileName);
     }
 
     private void mapParameters(JMethod method, AbstractMethodDeclaration x) {
@@ -556,7 +553,8 @@
         if (type instanceof JClassType
             && type != program.getTypeJavaLangObject()
             && type != program.getIndexedType("Array")) {
-          JMethod getClassMethod = program.createMethod(null,
+          JMethod getClassMethod = program.createMethod(
+              type.getSourceInfo().makeChild("Synthetic getClass()"),
               "getClass".toCharArray(), type, program.getTypeJavaLangClass(),
               false, false, false, false, false);
           assert (type.methods.get(2) == getClassMethod);
@@ -621,14 +619,16 @@
       // Visit the synthetic values() and valueOf() methods.
       for (MethodBinding methodBinding : binding.methods()) {
         if (methodBinding instanceof SyntheticMethodBinding) {
-          JMethod newMethod = processMethodBinding(methodBinding, type, null);
+          JMethod newMethod = processMethodBinding(methodBinding, type,
+              type.getSourceInfo());
           TypeBinding[] parameters = methodBinding.parameters;
           if (parameters.length == 0) {
             assert newMethod.getName().equals("values");
           } else if (parameters.length == 1) {
             assert newMethod.getName().equals("valueOf");
             assert typeMap.get(parameters[0]) == program.getTypeJavaLangString();
-            program.createParameter(null, "name".toCharArray(),
+            program.createParameter(newMethod.getSourceInfo().makeChild(
+                "name parameter"), "name".toCharArray(),
                 program.getTypeJavaLangString(), true, false, newMethod);
           } else {
             assert false;
@@ -702,6 +702,7 @@
       try {
         // start at -1 to avoid counting our synthetic header
         // TODO: get the character position start correct
+        jsParser.setSourceInfo(info);
         List<JsStatement> result = jsParser.parse(jsProgram.getScope(), sr, -1);
         JsExprStmt jsExprStmt = (JsExprStmt) result.get(0);
         JsFunction jsFunction = (JsFunction) jsExprStmt.getExpression();
@@ -746,12 +747,28 @@
         // TODO: check this
         // Map into the original source stream;
         i += startPos + detail.getLineOffset();
-        info = new SourceInfo(i, i, info.getStartLine() + detail.getLine(),
-            info.getFileName());
+        info = program.createSourceInfo(i, i, info.getStartLine()
+            + detail.getLine(), info.getFileName());
         GenerateJavaAST.reportJsniError(info, methodDeclaration, e.getMessage());
       }
     }
 
+    private InternalCompilerException translateException(
+        AbstractMethodDeclaration amd, Throwable e) {
+      if (e instanceof OutOfMemoryError) {
+        // Always rethrow OOMs (might have no memory to load ICE class anyway).
+        throw (OutOfMemoryError) e;
+      }
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+      } else {
+        ice = new InternalCompilerException("Error building type map", e);
+      }
+      ice.addNode(amd.getClass().getName(), amd.toString(), makeSourceInfo(amd));
+      return ice;
+    }
+
     private InternalCompilerException translateException(Statement stmt,
         Throwable e) {
       if (e instanceof OutOfMemoryError) {
@@ -778,34 +795,8 @@
    */
   private static class BuildTypeMapVisitor extends ASTVisitor {
 
-    private static SourceInfo makeSourceInfo(TypeDeclaration typeDecl) {
-      CompilationResult compResult = typeDecl.compilationResult;
-      int[] indexes = compResult.lineSeparatorPositions;
-      String fileName = String.valueOf(compResult.fileName);
-      int startLine = Util.getLineNumber(typeDecl.sourceStart, indexes, 0,
-          indexes.length - 1);
-      return new SourceInfo(typeDecl.sourceStart, typeDecl.bodyEnd, startLine,
-          fileName);
-    }
-
-    private static InternalCompilerException translateException(
-        TypeDeclaration typeDecl, Throwable e) {
-      if (e instanceof OutOfMemoryError) {
-        // Always rethrow OOMs (might have no memory to load ICE class anyway).
-        throw (OutOfMemoryError) e;
-      }
-      InternalCompilerException ice;
-      if (e instanceof InternalCompilerException) {
-        ice = (InternalCompilerException) e;
-      } else {
-        ice = new InternalCompilerException("Error building type map", e);
-      }
-      ice.addNode(typeDecl.getClass().getName(), typeDecl.toString(),
-          makeSourceInfo(typeDecl));
-      return ice;
-    }
-
     private final JProgram program;
+
     private final TypeMap typeMap;
 
     public BuildTypeMapVisitor(TypeMap typeMap) {
@@ -830,6 +821,16 @@
       return process(typeDeclaration);
     }
 
+    private SourceInfo makeSourceInfo(TypeDeclaration typeDecl) {
+      CompilationResult compResult = typeDecl.compilationResult;
+      int[] indexes = compResult.lineSeparatorPositions;
+      String fileName = String.valueOf(compResult.fileName);
+      int startLine = Util.getLineNumber(typeDecl.sourceStart, indexes, 0,
+          indexes.length - 1);
+      return program.createSourceInfo(typeDecl.sourceStart, typeDecl.bodyEnd,
+          startLine, fileName);
+    }
+
     private boolean process(TypeDeclaration typeDeclaration) {
       try {
         char[][] name = typeDeclaration.binding.compoundName;
@@ -878,12 +879,14 @@
          * more like output JavaScript. Clinit is always in slot 0, init (if it
          * exists) is always in slot 1.
          */
-        JMethod clinit = program.createMethod(null, "$clinit".toCharArray(),
+        JMethod clinit = program.createMethod(
+            info.makeChild("Class initializer"), "$clinit".toCharArray(),
             newType, program.getTypeVoid(), false, true, true, true, false);
         clinit.freezeParamTypes();
 
         if (newType instanceof JClassType) {
-          JMethod init = program.createMethod(null, "$init".toCharArray(),
+          JMethod init = program.createMethod(
+              info.makeChild("Instance initializer"), "$init".toCharArray(),
               newType, program.getTypeVoid(), false, false, true, true, false);
           init.freezeParamTypes();
         }
@@ -894,6 +897,23 @@
         throw translateException(typeDeclaration, e);
       }
     }
+
+    private InternalCompilerException translateException(
+        TypeDeclaration typeDecl, Throwable e) {
+      if (e instanceof OutOfMemoryError) {
+        // Always rethrow OOMs (might have no memory to load ICE class anyway).
+        throw (OutOfMemoryError) e;
+      }
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+      } else {
+        ice = new InternalCompilerException("Error building type map", e);
+      }
+      ice.addNode(typeDecl.getClass().getName(), typeDecl.toString(),
+          makeSourceInfo(typeDecl));
+      return ice;
+    }
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index 3dd6b4f..8514ba0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
@@ -102,7 +103,8 @@
 
       // the 0th entry is the "always false" entry
       classes.add(null);
-      jsonObjects.add(new JsonObject(program));
+      jsonObjects.add(new JsonObject(program,
+          program.createSourceInfoSynthetic("always-false typeinfo entry")));
 
       /*
        * Do String first to reserve typeIds 1 and 2 for Object and String,
@@ -256,14 +258,15 @@
       }
 
       // Create a sparse lookup object.
-      JsonObject jsonObject = new JsonObject(program);
+      SourceInfo sourceInfo = program.createSourceInfoSynthetic("typeinfo lookup");
+      JsonObject jsonObject = new JsonObject(program, sourceInfo);
       // Start at 1; 0 is Object and always true.
       for (int i = 1; i < nextQueryId; ++i) {
         if (yesArray[i] != null) {
           JIntLiteral labelExpr = program.getLiteralInt(i);
           JIntLiteral valueExpr = program.getLiteralInt(1);
-          jsonObject.propInits.add(new JsonPropInit(program, labelExpr,
-              valueExpr));
+          jsonObject.propInits.add(new JsonPropInit(program, sourceInfo,
+              labelExpr, valueExpr));
         }
       }
 
@@ -354,7 +357,8 @@
       if (expr.getType() == charType) {
         if (expr instanceof JCharLiteral) {
           JCharLiteral charLit = (JCharLiteral) expr;
-          return program.getLiteralString(new char[] {charLit.getValue()});
+          return program.getLiteralString(expr.getSourceInfo(),
+              new char[] {charLit.getValue()});
         } else {
           // Replace with Cast.charToString(c)
           JMethodCall call = new JMethodCall(program, expr.getSourceInfo(),
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CatchBlockNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CatchBlockNormalizer.java
index 1833665..14aa9f9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CatchBlockNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CatchBlockNormalizer.java
@@ -82,7 +82,7 @@
        * Go backwards so we can nest the else statements in the correct order!
        */
       // rethrow the current exception if no one caught it
-      JStatement cur = new JThrowStatement(program, null, exRef);
+      JStatement cur = new JThrowStatement(program, catchInfo, exRef);
       for (int i = x.getCatchBlocks().size() - 1; i >= 0; --i) {
         JBlock block = x.getCatchBlocks().get(i);
         JLocalRef arg = x.getCatchArgs().get(i);
@@ -118,7 +118,7 @@
     @Override
     public boolean visit(JTryStatement x, Context ctx) {
       if (!x.getCatchBlocks().isEmpty()) {
-        pushTempLocal();
+        pushTempLocal(x.getSourceInfo());
       }
       return true;
     }
@@ -151,9 +151,9 @@
     return tempLocals.get(--localIndex);
   }
 
-  private void pushTempLocal() {
+  private void pushTempLocal(SourceInfo sourceInfo) {
     if (localIndex == tempLocals.size()) {
-      JLocal newTemp = program.createLocal(null,
+      JLocal newTemp = program.createLocal(sourceInfo,
           ("$e" + localIndex).toCharArray(), program.getTypeJavaLangObject(),
           false, currentMethodBody);
       tempLocals.add(newTemp);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
index eb70ed3..7e3455c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
@@ -142,7 +142,7 @@
 
   @Override
   public boolean visit(JClassSeed x, Context ctx) {
-    expression = new JClassSeed(program, x.getRefType());
+    expression = new JClassSeed(program, x.getSourceInfo(), x.getRefType());
     return false;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 01800bc..ef50130 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -84,8 +84,8 @@
    * operations in favor of pure side effects.
    * 
    * TODO(spoon): move more simplifications into methods like
-   * {@link #cast(JExpression, SourceInfo, JType, JExpression) simplifyCast},
-   * so that more simplifications can be made on a single pass through a tree.
+   * {@link #cast(JExpression, SourceInfo, JType, JExpression) simplifyCast}, so
+   * that more simplifications can be made on a single pass through a tree.
    */
   public class DeadCodeVisitor extends JModVisitor {
 
@@ -94,8 +94,8 @@
     /**
      * Expressions whose result does not matter. A parent node should add any
      * children whose result does not matter to this set during the parent's
-     * <code>visit()</code> method. It should then remove those children
-     * during its own <code>endVisit()</code>.
+     * <code>visit()</code> method. It should then remove those children during
+     * its own <code>endVisit()</code>.
      * 
      * TODO: there's a latent bug here: some immutable nodes (such as literals)
      * can be multiply referenced in the AST. In theory, one reference to that
@@ -666,8 +666,9 @@
       if (lhs instanceof JValueLiteral && rhs instanceof JValueLiteral) {
         Object lhsObj = ((JValueLiteral) lhs).getValueObj();
         Object rhsObj = ((JValueLiteral) rhs).getValueObj();
-        ctx.replaceMe(program.getLiteralString(String.valueOf(lhsObj)
-            + String.valueOf(rhsObj)));
+        ctx.replaceMe(program.getLiteralString(lhs.getSourceInfo().makeChild(
+            "String concatenation", rhs.getSourceInfo()),
+            String.valueOf(lhsObj) + String.valueOf(rhsObj)));
       }
     }
 
@@ -1344,8 +1345,8 @@
     }
 
     /**
-     * Simplify <code>exp == bool</code>, where <code>bool</code> is a
-     * boolean literal.
+     * Simplify <code>exp == bool</code>, where <code>bool</code> is a boolean
+     * literal.
      */
     private void simplifyBooleanEq(JExpression exp, boolean bool, Context ctx) {
       if (bool) {
@@ -1359,8 +1360,8 @@
     /**
      * Simplify <code>lhs == rhs</code>, where <code>lhs</code> and
      * <code>rhs</code> are known to be boolean. If <code>negate</code> is
-     * <code>true</code>, then treat it as <code>lhs != rhs</code> instead
-     * of <code>lhs == rhs</code>. Assumes that the case where both sides are
+     * <code>true</code>, then treat it as <code>lhs != rhs</code> instead of
+     * <code>lhs == rhs</code>. Assumes that the case where both sides are
      * literals has already been checked.
      */
     private void simplifyBooleanEq(JExpression lhs, JExpression rhs,
@@ -1392,8 +1393,8 @@
     }
 
     /**
-     * Simplify <code>lhs == rhs</code>. If <code>negate</code> is true,
-     * then it's actually static evaluation of <code>lhs != rhs</code>.
+     * Simplify <code>lhs == rhs</code>. If <code>negate</code> is true, then
+     * it's actually static evaluation of <code>lhs != rhs</code>.
      */
     private void simplifyEq(JExpression lhs, JExpression rhs, Context ctx,
         boolean negated) {
@@ -1626,7 +1627,8 @@
         }
         Object result = actual.invoke(instance, paramValues);
         if (result instanceof String) {
-          ctx.replaceMe(program.getLiteralString((String) result));
+          ctx.replaceMe(program.getLiteralString(x.getSourceInfo().makeChild(
+              "Optimized String call"), (String) result));
         } else if (result instanceof Boolean) {
           ctx.replaceMe(program.getLiteralBoolean(((Boolean) result).booleanValue()));
         } else if (result instanceof Character) {
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 b3f3b9a..d63a706 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
@@ -531,7 +531,8 @@
     }
 
     JStringLiteral processConstant(StringConstant x) {
-      return program.getLiteralString(x.stringValue().toCharArray());
+      return program.getLiteralString(currentMethod.getSourceInfo().makeChild(
+          "String literal"), x.stringValue().toCharArray());
     }
 
     /**
@@ -677,7 +678,7 @@
         currentMethod = null;
 
         // synthesize a return statement to emulate returning the new object
-        statements.add(new JReturnStatement(program, null, thisRef));
+        statements.add(new JReturnStatement(program, info, thisRef));
       } catch (Throwable e) {
         throw translateException(ctor, e);
       }
@@ -734,7 +735,7 @@
 
       // Enums: hidden arguments for the name and id.
       if (x.enumConstant != null) {
-        call.getArgs().add(program.getLiteralString(x.enumConstant.name));
+        call.getArgs().add(program.getLiteralString(info, x.enumConstant.name));
         call.getArgs().add(
             program.getLiteralInt(x.enumConstant.binding.original().id));
       }
@@ -1955,20 +1956,25 @@
     }
 
     private JField createEnumValueMap(JEnumType type) {
-      JsonObject map = new JsonObject(program);
+      SourceInfo sourceInfo = type.getSourceInfo().makeChild(
+          "enum value lookup map");
+      JsonObject map = new JsonObject(program, sourceInfo);
       for (JEnumField field : type.enumList) {
         // JSON maps require leading underscores to prevent collisions.
-        JStringLiteral key = program.getLiteralString("_" + field.getName());
-        JFieldRef value = new JFieldRef(program, null, null, field, type);
-        map.propInits.add(new JsonObject.JsonPropInit(program, key, value));
+        JStringLiteral key = program.getLiteralString(field.getSourceInfo(),
+            "_" + field.getName());
+        JFieldRef value = new JFieldRef(program, sourceInfo, null, field, type);
+        map.propInits.add(new JsonObject.JsonPropInit(program, sourceInfo, key,
+            value));
       }
-      JField mapField = program.createField(null, "enum$map".toCharArray(),
-          type, map.getType(), true, Disposition.FINAL);
+      JField mapField = program.createField(sourceInfo,
+          "enum$map".toCharArray(), type, map.getType(), true,
+          Disposition.FINAL);
 
       // Initialize in clinit.
       JMethodBody clinitBody = (JMethodBody) type.methods.get(0).getBody();
-      JExpressionStatement assignment = program.createAssignmentStmt(null,
-          createVariableRef(null, mapField), map);
+      JExpressionStatement assignment = program.createAssignmentStmt(
+          sourceInfo, createVariableRef(sourceInfo, mapField), map);
       clinitBody.getStatements().add(assignment);
       return mapField;
     }
@@ -2197,14 +2203,15 @@
      * expression. Beware that when autoboxing, the type of the expression is
      * not necessarily the same as the type of the box to be created. The JDT
      * figures out what the necessary conversion is, depending on the context
-     * the expression appears in, and stores it in <code>x.implicitConversion</code>,
-     * so extract it from there.
+     * the expression appears in, and stores it in
+     * <code>x.implicitConversion</code>, so extract it from there.
      */
     private JPrimitiveType implicitConversionTargetType(Expression x)
         throws InternalCompilerException {
       /*
        * This algorithm for finding the target type is copied from
-       * org.eclipse.jdt.internal.compiler.codegen.CodeStream.generateReturnBytecode() .
+       * org.eclipse.jdt
+       * .internal.compiler.codegen.CodeStream.generateReturnBytecode() .
        */
       switch ((x.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4) {
         case TypeIds.T_boolean:
@@ -2232,7 +2239,7 @@
     private SourceInfo makeSourceInfo(Statement x) {
       int startLine = Util.getLineNumber(x.sourceStart,
           currentSeparatorPositions, 0, currentSeparatorPositions.length - 1);
-      return new SourceInfo(x.sourceStart, x.sourceEnd, startLine,
+      return program.createSourceInfo(x.sourceStart, x.sourceEnd, startLine,
           currentFileName);
     }
 
@@ -2488,28 +2495,34 @@
 
     private void writeEnumValueOfMethod(JEnumType type, JField mapField) {
       // return Enum.valueOf(map, name);
-      JFieldRef mapRef = new JFieldRef(program, null, null, mapField, type);
-      JVariableRef nameRef = createVariableRef(null,
+      SourceInfo sourceInfo = mapField.getSourceInfo().makeChild(
+          "enum accessor method");
+      JFieldRef mapRef = new JFieldRef(program, sourceInfo, null, mapField,
+          type);
+      JVariableRef nameRef = createVariableRef(sourceInfo,
           currentMethod.params.get(0));
       JMethod delegateTo = program.getIndexedMethod("Enum.valueOf");
-      JMethodCall call = new JMethodCall(program, null, null, delegateTo);
+      JMethodCall call = new JMethodCall(program, sourceInfo, null, delegateTo);
       call.getArgs().add(mapRef);
       call.getArgs().add(nameRef);
       currentMethodBody.getStatements().add(
-          new JReturnStatement(program, null, call));
+          new JReturnStatement(program, sourceInfo, call));
     }
 
     private void writeEnumValuesMethod(JEnumType type) {
       // return new E[]{A,B,C};
+      SourceInfo sourceInfo = type.getSourceInfo().makeChild(
+          "enum values method");
       List<JExpression> initializers = new ArrayList<JExpression>();
       for (JEnumField field : type.enumList) {
-        JFieldRef fieldRef = new JFieldRef(program, null, null, field, type);
+        JFieldRef fieldRef = new JFieldRef(program, sourceInfo, null, field,
+            type);
         initializers.add(fieldRef);
       }
-      JNewArray newExpr = JNewArray.createInitializers(program, null,
+      JNewArray newExpr = JNewArray.createInitializers(program, sourceInfo,
           program.getTypeArray(type, 1), initializers);
       currentMethodBody.getStatements().add(
-          new JReturnStatement(program, null, newExpr));
+          new JReturnStatement(program, sourceInfo, newExpr));
     }
   }
 
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 b79c1f8..23f9aa2 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
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.JsOutputOption;
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
 import com.google.gwt.dev.jjs.ast.HasName;
@@ -315,7 +316,8 @@
         jsFunction.setName(globalName);
       } else {
         // create a new peer JsFunction
-        jsFunction = new JsFunction(topScope, globalName, true);
+        jsFunction = new JsFunction(x.getSourceInfo(), topScope, globalName,
+            true);
         methodBodyMap.put(x.getBody(), jsFunction);
       }
       push(jsFunction.getScope());
@@ -331,7 +333,8 @@
       for (int i = 0, c = catchArgs.size(); i < c; ++i) {
         JLocalRef arg = catchArgs.get(i);
         JBlock catchBlock = catchBlocks.get(i);
-        JsCatch jsCatch = new JsCatch(peek(), arg.getTarget().getName());
+        JsCatch jsCatch = new JsCatch(x.getSourceInfo(), peek(),
+            arg.getTarget().getName());
         JsParameter jsParam = jsCatch.getParameter();
         names.put(arg.getTarget(), jsParam.getName());
         catchMap.put(catchBlock, jsCatch);
@@ -389,7 +392,7 @@
 
     @Override
     public void endVisit(JArrayRef x, Context ctx) {
-      JsArrayAccess jsArrayAccess = new JsArrayAccess();
+      JsArrayAccess jsArrayAccess = new JsArrayAccess(x.getSourceInfo());
       jsArrayAccess.setIndexExpr((JsExpression) pop());
       jsArrayAccess.setArrayExpr((JsExpression) pop());
       push(jsArrayAccess);
@@ -420,12 +423,12 @@
         myOp = JsBinaryOperator.REF_NEQ;
       }
 
-      push(new JsBinaryOperation(myOp, lhs, rhs));
+      push(new JsBinaryOperation(x.getSourceInfo(), myOp, lhs, rhs));
     }
 
     @Override
     public void endVisit(JBlock x, Context ctx) {
-      JsBlock jsBlock = new JsBlock();
+      JsBlock jsBlock = new JsBlock(x.getSourceInfo());
       List<JsStatement> stmts = jsBlock.getStatements();
       popList(stmts, x.statements.size()); // stmts
       Iterator<JsStatement> iterator = stmts.iterator();
@@ -443,17 +446,17 @@
       JsNameRef labelRef = null;
       if (x.getLabel() != null) {
         JsLabel label = (JsLabel) pop(); // label
-        labelRef = label.getName().makeRef();
+        labelRef = label.getName().makeRef(x.getSourceInfo());
       }
-      push(new JsBreak(labelRef));
+      push(new JsBreak(x.getSourceInfo(), labelRef));
     }
 
     @Override
     public void endVisit(JCaseStatement x, Context ctx) {
       if (x.getExpr() == null) {
-        push(new JsDefault());
+        push(new JsDefault(x.getSourceInfo()));
       } else {
-        JsCase jsCase = new JsCase();
+        JsCase jsCase = new JsCase(x.getSourceInfo());
         jsCase.setCaseExpr((JsExpression) pop()); // expr
         push(jsCase);
       }
@@ -467,12 +470,12 @@
     @Override
     public void endVisit(JClassLiteral x, Context ctx) {
       JsName classLit = names.get(x.getField());
-      push(classLit.makeRef());
+      push(classLit.makeRef(x.getSourceInfo()));
     }
 
     @Override
     public void endVisit(JClassSeed x, Context ctx) {
-      push(names.get(x.getRefType()).makeRef());
+      push(names.get(x.getRefType()).makeRef(x.getSourceInfo()));
     }
 
     @SuppressWarnings("unchecked")
@@ -516,7 +519,7 @@
       }
 
       // setup fields
-      JsVars vars = new JsVars();
+      JsVars vars = new JsVars(x.getSourceInfo());
       for (int i = 0; i < jsFields.size(); ++i) {
         JsNode node = jsFields.get(i);
         if (node instanceof JsVar) {
@@ -537,7 +540,7 @@
       JsExpression elseExpr = (JsExpression) pop(); // elseExpr
       JsExpression thenExpr = (JsExpression) pop(); // thenExpr
       JsExpression ifTest = (JsExpression) pop(); // ifTest
-      push(new JsConditional(ifTest, thenExpr, elseExpr));
+      push(new JsConditional(x.getSourceInfo(), ifTest, thenExpr, elseExpr));
     }
 
     @Override
@@ -545,9 +548,9 @@
       JsNameRef labelRef = null;
       if (x.getLabel() != null) {
         JsLabel label = (JsLabel) pop(); // label
-        labelRef = label.getName().makeRef();
+        labelRef = label.getName().makeRef(x.getSourceInfo());
       }
-      push(new JsContinue(labelRef));
+      push(new JsContinue(x.getSourceInfo(), labelRef));
     }
 
     @Override
@@ -573,15 +576,15 @@
         return;
       }
 
-      JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.ASG,
-          localRef, initializer);
+      JsBinaryOperation binOp = new JsBinaryOperation(x.getSourceInfo(),
+          JsBinaryOperator.ASG, localRef, initializer);
 
       push(binOp.makeStmt());
     }
 
     @Override
     public void endVisit(JDoStatement x, Context ctx) {
-      JsDoWhile stmt = new JsDoWhile();
+      JsDoWhile stmt = new JsDoWhile(x.getSourceInfo());
       if (x.getBody() != null) {
         stmt.setBody((JsStatement) pop()); // body
       } else {
@@ -619,16 +622,16 @@
 
       if (x.isStatic()) {
         // setup a var for the static
-        JsVar var = new JsVar(name);
+        JsVar var = new JsVar(x.getSourceInfo(), name);
         var.setInitExpr(rhs);
         push(var);
       } else {
         // for non-statics, only setup an assignment if needed
         if (rhs != null) {
-          JsNameRef fieldRef = name.makeRef();
-          fieldRef.setQualifier(globalTemp.makeRef());
+          JsNameRef fieldRef = name.makeRef(x.getSourceInfo());
+          fieldRef.setQualifier(globalTemp.makeRef(x.getSourceInfo()));
           JsExpression asg = createAssignment(fieldRef, rhs);
-          push(new JsExprStmt(asg));
+          push(new JsExprStmt(x.getSourceInfo(), asg));
         } else {
           push(null);
         }
@@ -639,7 +642,7 @@
     public void endVisit(JFieldRef x, Context ctx) {
       JField field = x.getField();
       JsName jsFieldName = names.get(field);
-      JsNameRef nameRef = jsFieldName.makeRef();
+      JsNameRef nameRef = jsFieldName.makeRef(x.getSourceInfo());
       JsExpression curExpr = nameRef;
 
       /*
@@ -670,7 +673,7 @@
 
     @Override
     public void endVisit(JForStatement x, Context ctx) {
-      JsFor jsFor = new JsFor();
+      JsFor jsFor = new JsFor(x.getSourceInfo());
 
       // body
       if (x.getBody() != null) {
@@ -716,7 +719,7 @@
 
     @Override
     public void endVisit(JIfStatement x, Context ctx) {
-      JsIf stmt = new JsIf();
+      JsIf stmt = new JsIf(x.getSourceInfo());
 
       if (x.getElseStmt() != null) {
         stmt.setElseStmt((JsStatement) pop()); // elseStmt
@@ -750,7 +753,7 @@
       }
 
       // setup fields
-      JsVars vars = new JsVars();
+      JsVars vars = new JsVars(x.getSourceInfo());
       for (int i = 0; i < jsFields.size(); ++i) {
         vars.add(jsFields.get(i));
       }
@@ -761,7 +764,7 @@
 
     @Override
     public void endVisit(JLabel x, Context ctx) {
-      push(new JsLabel(names.get(x)));
+      push(new JsLabel(x.getSourceInfo(), names.get(x)));
     }
 
     @Override
@@ -774,12 +777,12 @@
 
     @Override
     public void endVisit(JLocal x, Context ctx) {
-      push(names.get(x).makeRef());
+      push(names.get(x).makeRef(x.getSourceInfo()));
     }
 
     @Override
     public void endVisit(JLocalRef x, Context ctx) {
-      push(names.get(x.getTarget()).makeRef());
+      push(names.get(x.getTarget()).makeRef(x.getSourceInfo()));
     }
 
     @Override
@@ -798,7 +801,7 @@
       JsName longLit = topScope.declareName(nameString);
       longLits.put(x.getValue(), longLit);
       longObjects.put(longLit, longLiteralAllocation);
-      push(longLit.makeRef());
+      push(longLit.makeRef(x.getSourceInfo()));
     }
 
     @Override
@@ -856,14 +859,14 @@
        * other in Java. We use the alreadySeen set to make sure we don't declare
        * the same-named local var twice.
        */
-      JsVars vars = new JsVars();
+      JsVars vars = new JsVars(x.getSourceInfo());
       Set<String> alreadySeen = new HashSet<String>();
       for (int i = 0; i < locals.size(); ++i) {
         JsName name = names.get(x.locals.get(i));
         String ident = name.getIdent();
         if (!alreadySeen.contains(ident)) {
           alreadySeen.add(ident);
-          vars.add(new JsVar(name));
+          vars.add(new JsVar(x.getSourceInfo(), name));
         }
       }
 
@@ -877,7 +880,7 @@
     @Override
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
-      JsInvocation jsInvocation = new JsInvocation();
+      JsInvocation jsInvocation = new JsInvocation(x.getSourceInfo());
 
       popList(jsInvocation.getArguments(), x.getArgs().size()); // args
 
@@ -887,7 +890,7 @@
         if (x.getInstance() != null) {
           unnecessaryQualifier = (JsExpression) pop(); // instance
         }
-        qualifier = names.get(method).makeRef();
+        qualifier = names.get(method).makeRef(x.getSourceInfo());
       } else {
         if (x.isStaticDispatchOnly()) {
           /*
@@ -901,12 +904,12 @@
            */
           JsName callName = objectScope.declareName("call");
           callName.setObfuscatable(false);
-          qualifier = callName.makeRef();
-          qualifier.setQualifier(names.get(method).makeRef());
+          qualifier = callName.makeRef(x.getSourceInfo());
+          qualifier.setQualifier(names.get(method).makeRef(x.getSourceInfo()));
           jsInvocation.getArguments().add(0, (JsExpression) pop()); // instance
         } else {
           // Dispatch polymorphically (normal case).
-          qualifier = polymorphicNames.get(method).makeRef();
+          qualifier = polymorphicNames.get(method).makeRef(x.getSourceInfo());
           qualifier.setQualifier((JsExpression) pop()); // instance
         }
       }
@@ -936,32 +939,32 @@
 
     @Override
     public void endVisit(JNewInstance x, Context ctx) {
-      JsNew newOp = new JsNew();
-      JsNameRef nameRef = names.get(x.getType()).makeRef();
+      JsNew newOp = new JsNew(x.getSourceInfo());
+      JsNameRef nameRef = names.get(x.getType()).makeRef(x.getSourceInfo());
       newOp.setConstructorExpression(nameRef);
       push(newOp);
     }
 
     @Override
     public void endVisit(JParameter x, Context ctx) {
-      push(new JsParameter(names.get(x)));
+      push(new JsParameter(x.getSourceInfo(), names.get(x)));
     }
 
     @Override
     public void endVisit(JParameterRef x, Context ctx) {
-      push(names.get(x.getTarget()).makeRef());
+      push(names.get(x.getTarget()).makeRef(x.getSourceInfo()));
     }
 
     @Override
     public void endVisit(JPostfixOperation x, Context ctx) {
-      JsUnaryOperation op = new JsPostfixOperation(
+      JsUnaryOperation op = new JsPostfixOperation(x.getSourceInfo(),
           JavaToJsOperatorMap.get(x.getOp()), ((JsExpression) pop())); // arg
       push(op);
     }
 
     @Override
     public void endVisit(JPrefixOperation x, Context ctx) {
-      JsUnaryOperation op = new JsPrefixOperation(
+      JsUnaryOperation op = new JsPrefixOperation(x.getSourceInfo(),
           JavaToJsOperatorMap.get(x.getOp()), ((JsExpression) pop())); // arg
       push(op);
     }
@@ -987,8 +990,8 @@
       // Add a few things onto the beginning.
 
       // Reserve the "_" identifier.
-      JsVars vars = new JsVars();
-      vars.add(new JsVar(globalTemp));
+      JsVars vars = new JsVars(x.getSourceInfo());
+      vars.add(new JsVar(x.getSourceInfo(), globalTemp));
       globalStmts.add(0, vars);
 
       // Long lits must go at the top, they can be constant field initializers.
@@ -997,7 +1000,7 @@
       // Class objects, but only if there are any.
       if (x.getDeclaredTypes().contains(x.getTypeClassLiteralHolder())) {
         // TODO: perhaps they could be constant field initializers also?
-        vars = new JsVars();
+        vars = new JsVars(x.getSourceInfo());
         generateClassLiterals(vars);
         if (!vars.isEmpty()) {
           globalStmts.add(vars);
@@ -1013,29 +1016,29 @@
     @Override
     public void endVisit(JReturnStatement x, Context ctx) {
       if (x.getExpr() != null) {
-        push(new JsReturn((JsExpression) pop())); // expr
+        push(new JsReturn(x.getSourceInfo(), (JsExpression) pop())); // expr
       } else {
-        push(new JsReturn());
+        push(new JsReturn(x.getSourceInfo()));
       }
     }
 
     @Override
     public void endVisit(JsniMethodRef x, Context ctx) {
       JMethod method = x.getTarget();
-      JsNameRef nameRef = names.get(method).makeRef();
+      JsNameRef nameRef = names.get(method).makeRef(x.getSourceInfo());
       push(nameRef);
     }
 
     @Override
     public void endVisit(JsonArray x, Context ctx) {
-      JsArrayLiteral jsArrayLiteral = new JsArrayLiteral();
+      JsArrayLiteral jsArrayLiteral = new JsArrayLiteral(x.getSourceInfo());
       popList(jsArrayLiteral.getExpressions(), x.exprs.size());
       push(jsArrayLiteral);
     }
 
     @Override
     public void endVisit(JsonObject x, Context ctx) {
-      JsObjectLiteral jsObjectLiteral = new JsObjectLiteral();
+      JsObjectLiteral jsObjectLiteral = new JsObjectLiteral(x.getSourceInfo());
       popList(jsObjectLiteral.getPropertyInitializers(), x.propInits.size());
       push(jsObjectLiteral);
     }
@@ -1044,22 +1047,22 @@
     public void endVisit(JsonPropInit init, Context ctx) {
       JsExpression valueExpr = (JsExpression) pop();
       JsExpression labelExpr = (JsExpression) pop();
-      push(new JsPropertyInitializer(labelExpr, valueExpr));
+      push(new JsPropertyInitializer(init.getSourceInfo(), labelExpr, valueExpr));
     }
 
     @Override
     public void endVisit(JThisRef x, Context ctx) {
-      push(new JsThisRef());
+      push(new JsThisRef(x.getSourceInfo()));
     }
 
     @Override
     public void endVisit(JThrowStatement x, Context ctx) {
-      push(new JsThrow((JsExpression) pop())); // expr
+      push(new JsThrow(x.getSourceInfo(), (JsExpression) pop())); // expr
     }
 
     @Override
     public void endVisit(JTryStatement x, Context ctx) {
-      JsTry jsTry = new JsTry();
+      JsTry jsTry = new JsTry(x.getSourceInfo());
 
       if (x.getFinallyBlock() != null) {
         JsBlock finallyBlock = (JsBlock) pop(); // finallyBlock
@@ -1085,7 +1088,7 @@
 
     @Override
     public void endVisit(JWhileStatement x, Context ctx) {
-      JsWhile stmt = new JsWhile();
+      JsWhile stmt = new JsWhile(x.getSourceInfo());
       if (x.getBody() != null) {
         stmt.setBody((JsStatement) pop()); // body
       } else {
@@ -1181,7 +1184,7 @@
        * What a pain.. JSwitchStatement and JsSwitch are modeled completely
        * differently. Here we try to resolve those differences.
        */
-      JsSwitch jsSwitch = new JsSwitch();
+      JsSwitch jsSwitch = new JsSwitch(x.getSourceInfo());
       accept(x.getExpr());
       jsSwitch.setExpr((JsExpression) pop()); // expr
 
@@ -1213,7 +1216,8 @@
     }
 
     private JsExpression createAssignment(JsExpression lhs, JsExpression rhs) {
-      return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, rhs);
+      return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.ASG,
+          lhs, rhs);
     }
 
     private JsExpression createCommaExpression(JsExpression lhs,
@@ -1223,7 +1227,8 @@
       } else if (rhs == null) {
         return lhs;
       }
-      return new JsBinaryOperation(JsBinaryOperator.COMMA, lhs, rhs);
+      return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.COMMA,
+          lhs, rhs);
     }
 
     private void generateClassLiteral(JDeclarationStatement decl, JsVars vars) {
@@ -1231,7 +1236,7 @@
       JsName jsName = names.get(field);
       this.accept(decl.getInitializer());
       JsExpression classObjectAlloc = pop();
-      JsVar var = new JsVar(jsName);
+      JsVar var = new JsVar(decl.getSourceInfo(), jsName);
       var.setInitExpr(classObjectAlloc);
       vars.add(var);
     }
@@ -1283,58 +1288,61 @@
        * }
        * </pre>
        */
+      SourceInfo sourceInfo = program.createSourceInfoSynthetic("gwtOnLoad");
       JsName gwtOnLoadName = topScope.declareName("gwtOnLoad");
       gwtOnLoadName.setObfuscatable(false);
-      JsFunction gwtOnLoad = new JsFunction(topScope, gwtOnLoadName, true);
+      JsFunction gwtOnLoad = new JsFunction(sourceInfo, topScope,
+          gwtOnLoadName, true);
       globalStmts.add(gwtOnLoad.makeStmt());
-      JsBlock body = new JsBlock();
+      JsBlock body = new JsBlock(sourceInfo);
       gwtOnLoad.setBody(body);
       JsScope fnScope = gwtOnLoad.getScope();
       List<JsParameter> params = gwtOnLoad.getParameters();
       JsName errFn = fnScope.declareName("errFn");
       JsName modName = fnScope.declareName("modName");
       JsName modBase = fnScope.declareName("modBase");
-      params.add(new JsParameter(errFn));
-      params.add(new JsParameter(modName));
-      params.add(new JsParameter(modBase));
+      params.add(new JsParameter(sourceInfo, errFn));
+      params.add(new JsParameter(sourceInfo, modName));
+      params.add(new JsParameter(sourceInfo, modBase));
       JsExpression asg = createAssignment(
-          topScope.findExistingUnobfuscatableName("$moduleName").makeRef(),
-          modName.makeRef());
+          topScope.findExistingUnobfuscatableName("$moduleName").makeRef(
+              sourceInfo), modName.makeRef(sourceInfo));
       body.getStatements().add(asg.makeStmt());
       asg = createAssignment(topScope.findExistingUnobfuscatableName(
-          "$moduleBase").makeRef(), modBase.makeRef());
+          "$moduleBase").makeRef(sourceInfo), modBase.makeRef(sourceInfo));
       body.getStatements().add(asg.makeStmt());
-      JsIf jsIf = new JsIf();
+      JsIf jsIf = new JsIf(sourceInfo);
       body.getStatements().add(jsIf);
-      jsIf.setIfExpr(errFn.makeRef());
-      JsTry jsTry = new JsTry();
+      jsIf.setIfExpr(errFn.makeRef(sourceInfo));
+      JsTry jsTry = new JsTry(sourceInfo);
       jsIf.setThenStmt(jsTry);
-      JsBlock callBlock = new JsBlock();
+      JsBlock callBlock = new JsBlock(sourceInfo);
       jsIf.setElseStmt(callBlock);
       jsTry.setTryBlock(callBlock);
       for (int i = 0; i < entryFuncs.size(); ++i) {
         JsFunction func = entryFuncs.get(i);
         if (func != null) {
-          JsInvocation call = new JsInvocation();
-          call.setQualifier(func.getName().makeRef());
+          JsInvocation call = new JsInvocation(sourceInfo);
+          call.setQualifier(func.getName().makeRef(sourceInfo));
           callBlock.getStatements().add(call.makeStmt());
         }
       }
-      JsCatch jsCatch = new JsCatch(fnScope, "e");
+      JsCatch jsCatch = new JsCatch(sourceInfo, fnScope, "e");
       jsTry.getCatches().add(jsCatch);
-      JsBlock catchBlock = new JsBlock();
+      JsBlock catchBlock = new JsBlock(sourceInfo);
       jsCatch.setBody(catchBlock);
-      JsInvocation errCall = new JsInvocation();
+      JsInvocation errCall = new JsInvocation(sourceInfo);
       catchBlock.getStatements().add(errCall.makeStmt());
-      errCall.setQualifier(errFn.makeRef());
-      errCall.getArguments().add(modName.makeRef());
+      errCall.setQualifier(errFn.makeRef(sourceInfo));
+      errCall.getArguments().add(modName.makeRef(sourceInfo));
     }
 
     private void generateLongLiterals(JsVars vars) {
       for (Entry<Long, JsName> entry : longLits.entrySet()) {
         JsName jsName = entry.getValue();
         JsExpression longObjectAlloc = longObjects.get(jsName);
-        JsVar var = new JsVar(jsName);
+        JsVar var = new JsVar(vars.getSourceInfo().makeChild(
+            "Long literal " + entry.getKey()), jsName);
         var.setInitExpr(longObjectAlloc);
         vars.add(var);
       }
@@ -1342,46 +1350,55 @@
 
     private void generateNullFunc(List<JsStatement> globalStatements) {
       // handle null method
-      JsFunction nullFunc = new JsFunction(topScope, nullMethodName, true);
-      nullFunc.setBody(new JsBlock());
+      SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic("Null function");
+      JsFunction nullFunc = new JsFunction(sourceInfo, topScope,
+          nullMethodName, true);
+      nullFunc.setBody(new JsBlock(sourceInfo));
       globalStatements.add(nullFunc.makeStmt());
     }
 
     private void generateSeedFuncAndPrototype(JClassType x,
         List<JsStatement> globalStmts) {
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild(
+          "Seed and function prototype");
       if (x != program.getTypeJavaLangString()) {
         JsName seedFuncName = names.get(x);
 
         // seed function
         // function com_example_foo_Foo() { }
-        JsFunction seedFunc = new JsFunction(topScope, seedFuncName, true);
-        JsBlock body = new JsBlock();
+        JsFunction seedFunc = new JsFunction(sourceInfo, topScope,
+            seedFuncName, true);
+        JsBlock body = new JsBlock(sourceInfo);
         seedFunc.setBody(body);
         globalStmts.add(seedFunc.makeStmt());
 
         // setup prototype, assign to temp
         // _ = com_example_foo_Foo.prototype = new com_example_foo_FooSuper();
-        JsNameRef lhs = prototype.makeRef();
-        lhs.setQualifier(seedFuncName.makeRef());
+        JsNameRef lhs = prototype.makeRef(sourceInfo);
+        lhs.setQualifier(seedFuncName.makeRef(sourceInfo));
         JsExpression rhs;
         if (x.extnds != null) {
-          JsNew newExpr = new JsNew();
-          newExpr.setConstructorExpression(names.get(x.extnds).makeRef());
+          JsNew newExpr = new JsNew(sourceInfo);
+          newExpr.setConstructorExpression(names.get(x.extnds).makeRef(
+              sourceInfo));
           rhs = newExpr;
         } else {
-          rhs = new JsObjectLiteral();
+          rhs = new JsObjectLiteral(sourceInfo);
         }
         JsExpression protoAsg = createAssignment(lhs, rhs);
-        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(), protoAsg);
+        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
+            protoAsg);
         globalStmts.add(tmpAsg.makeStmt());
       } else {
         /*
          * MAGIC: java.lang.String is implemented as a JavaScript String
          * primitive with a modified prototype.
          */
-        JsNameRef rhs = prototype.makeRef();
-        rhs.setQualifier(jsProgram.getRootScope().declareName("String").makeRef());
-        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(), rhs);
+        JsNameRef rhs = prototype.makeRef(sourceInfo);
+        rhs.setQualifier(jsProgram.getRootScope().declareName("String").makeRef(
+            sourceInfo));
+        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
+            rhs);
         globalStmts.add(tmpAsg.makeStmt());
       }
     }
@@ -1390,29 +1407,30 @@
         List<JsStatement> globalStmts) {
       JMethod toStringMeth = program.getIndexedMethod("Object.toString");
       if (x.methods.contains(toStringMeth)) {
+        SourceInfo sourceInfo = x.getSourceInfo().makeChild("_.toString");
         // _.toString = function(){return this.java_lang_Object_toString();}
 
         // lhs
         JsName lhsName = objectScope.declareName("toString");
         lhsName.setObfuscatable(false);
-        JsNameRef lhs = lhsName.makeRef();
-        lhs.setQualifier(globalTemp.makeRef());
+        JsNameRef lhs = lhsName.makeRef(sourceInfo);
+        lhs.setQualifier(globalTemp.makeRef(sourceInfo));
 
         // rhs
-        JsInvocation call = new JsInvocation();
-        JsNameRef toStringRef = new JsNameRef(
+        JsInvocation call = new JsInvocation(sourceInfo);
+        JsNameRef toStringRef = new JsNameRef(sourceInfo,
             polymorphicNames.get(toStringMeth));
-        toStringRef.setQualifier(new JsThisRef());
+        toStringRef.setQualifier(new JsThisRef(sourceInfo));
         call.setQualifier(toStringRef);
-        JsReturn jsReturn = new JsReturn(call);
-        JsFunction rhs = new JsFunction(topScope);
-        JsBlock body = new JsBlock();
+        JsReturn jsReturn = new JsReturn(sourceInfo, call);
+        JsFunction rhs = new JsFunction(sourceInfo, topScope);
+        JsBlock body = new JsBlock(sourceInfo);
         body.getStatements().add(jsReturn);
         rhs.setBody(body);
 
         // asg
         JsExpression asg = createAssignment(lhs, rhs);
-        globalStmts.add(new JsExprStmt(asg));
+        globalStmts.add(new JsExprStmt(sourceInfo, asg));
       }
     }
 
@@ -1425,11 +1443,13 @@
           // Was pruned; this compilation must have no dynamic casts.
           return;
         }
-        JsNameRef fieldRef = typeIdName.makeRef();
-        fieldRef.setQualifier(globalTemp.makeRef());
+        SourceInfo sourceInfo = x.getSourceInfo().makeChild(
+            "Type id assignment");
+        JsNameRef fieldRef = typeIdName.makeRef(sourceInfo);
+        fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
         JsNumberLiteral typeIdLit = jsProgram.getNumberLiteral(typeId);
         JsExpression asg = createAssignment(fieldRef, typeIdLit);
-        globalStmts.add(new JsExprStmt(asg));
+        globalStmts.add(new JsExprStmt(sourceInfo, asg));
       }
     }
 
@@ -1440,14 +1460,17 @@
         // Was pruned; this compilation must have no JSO instanceof tests.
         return;
       }
-      JsNameRef fieldRef = typeMarkerName.makeRef();
-      fieldRef.setQualifier(globalTemp.makeRef());
-      JsExpression asg = createAssignment(fieldRef, nullMethodName.makeRef());
-      globalStmts.add(new JsExprStmt(asg));
+      SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic("Type marker");
+      JsNameRef fieldRef = typeMarkerName.makeRef(sourceInfo);
+      fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
+      JsExpression asg = createAssignment(fieldRef,
+          nullMethodName.makeRef(sourceInfo));
+      globalStmts.add(new JsExprStmt(sourceInfo, asg));
     }
 
     private JsExpression generateTypeTable() {
-      JsArrayLiteral arrayLit = new JsArrayLiteral();
+      JsArrayLiteral arrayLit = new JsArrayLiteral(
+          jsProgram.createSourceInfoSynthetic("Type table"));
       for (int i = 0; i < program.getJsonTypeTable().size(); ++i) {
         JsonObject jsonObject = program.getJsonTypeTable().get(i);
         accept(jsonObject);
@@ -1459,12 +1482,14 @@
     private void generateVTables(JClassType x, List<JsStatement> globalStmts) {
       for (int i = 0; i < x.methods.size(); ++i) {
         JMethod method = x.methods.get(i);
+        SourceInfo sourceInfo = method.getSourceInfo().makeChild(
+            "vtable assignment");
         if (!method.isStatic() && !method.isAbstract()) {
-          JsNameRef lhs = polymorphicNames.get(method).makeRef();
-          lhs.setQualifier(globalTemp.makeRef());
-          JsNameRef rhs = names.get(method).makeRef();
+          JsNameRef lhs = polymorphicNames.get(method).makeRef(sourceInfo);
+          lhs.setQualifier(globalTemp.makeRef(sourceInfo));
+          JsNameRef rhs = names.get(method).makeRef(sourceInfo);
           JsExpression asg = createAssignment(lhs, rhs);
-          globalStmts.add(new JsExprStmt(asg));
+          globalStmts.add(new JsExprStmt(x.getSourceInfo(), asg));
         }
       }
     }
@@ -1473,9 +1498,11 @@
       clinitFunc.setExecuteOnce(true);
       clinitFunc.setImpliedExecute(superClinit);
       List<JsStatement> statements = clinitFunc.getBody().getStatements();
+      SourceInfo sourceInfo = clinitFunc.getSourceInfo().makeChild(
+          "clinit reassignment");
       // self-assign to the null method immediately (to prevent reentrancy)
-      JsExpression asg = createAssignment(clinitFunc.getName().makeRef(),
-          nullMethodName.makeRef());
+      JsExpression asg = createAssignment(clinitFunc.getName().makeRef(
+          sourceInfo), nullMethodName.makeRef(sourceInfo));
       statements.add(0, asg.makeStmt());
     }
 
@@ -1491,8 +1518,9 @@
       }
 
       JMethod clinitMethod = enclosingType.methods.get(0);
-      JsInvocation jsInvocation = new JsInvocation();
-      jsInvocation.setQualifier(names.get(clinitMethod).makeRef());
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild("clinit invocation");
+      JsInvocation jsInvocation = new JsInvocation(sourceInfo);
+      jsInvocation.setQualifier(names.get(clinitMethod).makeRef(sourceInfo));
       return jsInvocation;
     }
 
@@ -1513,8 +1541,9 @@
       }
 
       JMethod clinitMethod = enclosingType.methods.get(0);
-      JsInvocation jsInvocation = new JsInvocation();
-      jsInvocation.setQualifier(names.get(clinitMethod).makeRef());
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild("clinit call");
+      JsInvocation jsInvocation = new JsInvocation(sourceInfo);
+      jsInvocation.setQualifier(names.get(clinitMethod).makeRef(sourceInfo));
       return jsInvocation;
     }
   }
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 2fb1574..f6152e8 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -78,7 +78,7 @@
 
   @Override
   public void endVisit(JLongLiteral x, Context ctx) {
-    JsArrayLiteral arrayLit = new JsArrayLiteral();
+    JsArrayLiteral arrayLit = new JsArrayLiteral(x.getSourceInfo());
     double[] doubleArray = LongLib.typeChange(x.getValue());
     arrayLit.getExpressions().add(program.getNumberLiteral(doubleArray[0]));
     arrayLit.getExpressions().add(program.getNumberLiteral(doubleArray[1]));
@@ -92,7 +92,7 @@
 
   @Override
   public final void endVisit(JStringLiteral x, Context ctx) {
-    push(program.getStringLiteral(x.getValue()));
+    push(program.getStringLiteral(x.getSourceInfo(), x.getValue()));
   }
 
   @SuppressWarnings("unchecked")
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
index ff4610e..13ce278 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
@@ -126,15 +126,16 @@
    */
   private JMethod createDevirtualMethod(JMethod objectMethod, JMethod jsoImpl) {
     JClassType jsoType = program.getJavaScriptObject();
-    SourceInfo sourceInfo = null;
+    SourceInfo sourceInfo = jsoType.getSourceInfo();
 
     // Create the new method.
     String name = objectMethod.getName() + "__devirtual$";
-    JMethod newMethod = program.createMethod(sourceInfo, name.toCharArray(),
-        jsoType, objectMethod.getType(), false, true, true, false, false);
+    JMethod newMethod = program.createMethod(
+        sourceInfo.makeChild("Devirtualized method"), name.toCharArray(), jsoType,
+        objectMethod.getType(), false, true, true, false, false);
 
     // Setup parameters.
-    JParameter thisParam = program.createParameter(null,
+    JParameter thisParam = program.createParameter(sourceInfo,
         "this$static".toCharArray(), program.getTypeJavaLangObject(), true,
         true, newMethod);
     for (JParameter oldParam : objectMethod.params) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
index 7e5e399..5cb6caa 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
@@ -83,7 +83,8 @@
 
       @Override
       public void endVisit(JsThisRef x, JsContext<JsExpression> ctx) {
-        ctx.replaceMe(thisParam.makeRef());
+        ctx.replaceMe(thisParam.makeRef(x.getSourceInfo().makeChild(
+            "Devirtualized instance")));
       }
 
       @Override
@@ -113,14 +114,16 @@
       public void endVisit(JParameterRef x, Context ctx) {
         JParameter param = varMap.get(x.getTarget());
         JParameterRef paramRef = new JParameterRef(x.getProgram(),
-            x.getSourceInfo(), param);
+            x.getSourceInfo().makeChild("Reference to devirtualized parameter"),
+            param);
         ctx.replaceMe(paramRef);
       }
 
       @Override
       public void endVisit(JThisRef x, Context ctx) {
         JParameterRef paramRef = new JParameterRef(x.getProgram(),
-            x.getSourceInfo(), thisParam);
+            x.getSourceInfo().makeChild("Reference to devirtualized instance"),
+            thisParam);
         ctx.replaceMe(paramRef);
       }
     }
@@ -142,12 +145,14 @@
        * its enclosing class.
        */
       JProgram program = x.getProgram();
-      JMethod newMethod = new JMethod(program, sourceInfo, newName,
-          enclosingType, returnType, false, true, true, x.isPrivate());
+      JMethod newMethod = new JMethod(program,
+          sourceInfo.makeChild("Devirtualized function"), newName, enclosingType,
+          returnType, false, true, true, x.isPrivate());
 
       // Setup parameters; map from the old params to the new params
-      JParameter thisParam = program.createParameter(null,
-          "this$static".toCharArray(), enclosingType, true, true, newMethod);
+      JParameter thisParam = program.createParameter(
+          sourceInfo.makeChild("Instance parameter"), "this$static".toCharArray(),
+          enclosingType, true, true, newMethod);
       Map<JParameter, JParameter> varMap = new IdentityHashMap<JParameter, JParameter>();
       for (int i = 0; i < x.params.size(); ++i) {
         JParameter oldVar = x.params.get(i);
@@ -168,20 +173,24 @@
       newMethod.setBody(movedBody);
 
       // Create a new body for the instance method that delegates to the static
-      JMethodBody newBody = new JMethodBody(program, sourceInfo);
+      SourceInfo delegateCallSourceInfo = sourceInfo.makeChild("Degelgating to devirtualized method");
+      JMethodBody newBody = new JMethodBody(program, delegateCallSourceInfo);
       x.setBody(newBody);
-      JMethodCall newCall = new JMethodCall(program, sourceInfo, null,
-          newMethod);
-      newCall.getArgs().add(program.getExprThisRef(sourceInfo, enclosingType));
+      JMethodCall newCall = new JMethodCall(program, delegateCallSourceInfo,
+          null, newMethod);
+      newCall.getArgs().add(
+          program.getExprThisRef(delegateCallSourceInfo, enclosingType));
       for (int i = 0; i < x.params.size(); ++i) {
         JParameter param = x.params.get(i);
-        newCall.getArgs().add(new JParameterRef(program, sourceInfo, param));
+        newCall.getArgs().add(
+            new JParameterRef(program, delegateCallSourceInfo, param));
       }
       JStatement statement;
       if (returnType == program.getTypeVoid()) {
         statement = newCall.makeStatement();
       } else {
-        statement = new JReturnStatement(program, sourceInfo, newCall);
+        statement = new JReturnStatement(program, delegateCallSourceInfo,
+            newCall);
       }
       newBody.getStatements().add(statement);
 
@@ -195,7 +204,8 @@
         // TODO: Do we really need to do that in BuildTypeMap?
         JsFunction jsFunc = ((JsniMethodBody) movedBody).getFunc();
         JsName paramName = jsFunc.getScope().declareName("this$static");
-        jsFunc.getParameters().add(0, new JsParameter(paramName));
+        jsFunc.getParameters().add(0,
+            new JsParameter(sourceInfo.makeChild("Static accessor"), paramName));
         RewriteJsniMethodBody rewriter = new RewriteJsniMethodBody(paramName);
         // Accept the body to avoid the recursion blocker.
         rewriter.accept(jsFunc.getBody());
@@ -282,8 +292,9 @@
 
   static JMethodCall makeStaticCall(JMethodCall x, JMethod newMethod) {
     // Update the call site
-    JMethodCall newCall = new JMethodCall(x.getProgram(), x.getSourceInfo(),
-        null, newMethod);
+    JMethodCall newCall = new JMethodCall(x.getProgram(),
+        x.getSourceInfo().makeChild("Devirtualized function call"), null,
+        newMethod);
 
     // The qualifier becomes the first arg
     newCall.getArgs().add(x.getInstance());
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 10daca0..4ffa405 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -275,7 +275,8 @@
       // Fake an assignment-to-self on all args to prevent tightening
       JMethod method = x.getTarget();
       for (JParameter param : method.params) {
-        addAssignment(param, new JParameterRef(program, null, param));
+        addAssignment(param, new JParameterRef(program,
+            program.createSourceInfoSynthetic("Fake assignment"), param));
       }
     }
 
@@ -392,8 +393,8 @@
   public class TightenTypesVisitor extends JModVisitor {
 
     /**
-     * <code>true</code> if this visitor has changed the AST apart from calls
-     * to Context.
+     * <code>true</code> if this visitor has changed the AST apart from calls to
+     * Context.
      */
     private boolean myDidChange = false;
 
diff --git a/dev/core/src/com/google/gwt/dev/js/JsConstructExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsConstructExpressionVisitor.java
index 3d09cba..1ca37e0 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsConstructExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsConstructExpressionVisitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -26,6 +26,7 @@
 import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.js.ast.JsVisitor;
+import com.google.gwt.dev.js.ast.SourceInfoJs;
 
 /**
  * Searches for method invocations in constructor expressions that would not
@@ -33,7 +34,8 @@
  */
 public class JsConstructExpressionVisitor extends JsVisitor {
 
-  private static final int PRECEDENCE_NEW = JsPrecedenceVisitor.exec(new JsNew());
+  private static final int PRECEDENCE_NEW = JsPrecedenceVisitor.exec(new JsNew(
+      SourceInfoJs.INTRINSIC));
 
   public static boolean exec(JsExpression expression) {
     if (JsPrecedenceVisitor.exec(expression) < PRECEDENCE_NEW) {
diff --git a/dev/core/src/com/google/gwt/dev/js/JsHoister.java b/dev/core/src/com/google/gwt/dev/js/JsHoister.java
index 33964f2..e5f02ec 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsHoister.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsHoister.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -60,7 +60,7 @@
 
     @Override
     public void endVisit(JsArrayAccess x, JsContext<JsExpression> ctx) {
-      JsArrayAccess newExpression = new JsArrayAccess();
+      JsArrayAccess newExpression = new JsArrayAccess(x.getSourceInfo());
       newExpression.setIndexExpr(stack.pop());
       newExpression.setArrayExpr(stack.pop());
       stack.push(newExpression);
@@ -68,7 +68,7 @@
 
     @Override
     public void endVisit(JsArrayLiteral x, JsContext<JsExpression> ctx) {
-      JsArrayLiteral toReturn = new JsArrayLiteral();
+      JsArrayLiteral toReturn = new JsArrayLiteral(x.getSourceInfo());
       List<JsExpression> expressions = toReturn.getExpressions();
       for (JsExpression e : x.getExpressions()) {
         expressions.add(0, stack.pop());
@@ -78,7 +78,7 @@
 
     @Override
     public void endVisit(JsBinaryOperation x, JsContext<JsExpression> ctx) {
-      JsBinaryOperation toReturn = new JsBinaryOperation(x.getOperator());
+      JsBinaryOperation toReturn = new JsBinaryOperation(x.getSourceInfo(), x.getOperator());
       toReturn.setArg2(stack.pop());
       toReturn.setArg1(stack.pop());
       stack.push(toReturn);
@@ -91,7 +91,7 @@
 
     @Override
     public void endVisit(JsConditional x, JsContext<JsExpression> ctx) {
-      JsConditional toReturn = new JsConditional();
+      JsConditional toReturn = new JsConditional(x.getSourceInfo());
       toReturn.setElseExpression(stack.pop());
       toReturn.setThenExpression(stack.pop());
       toReturn.setTestExpression(stack.pop());
@@ -116,7 +116,7 @@
      */
     @Override
     public void endVisit(JsInvocation x, JsContext<JsExpression> ctx) {
-      JsInvocation toReturn = new JsInvocation();
+      JsInvocation toReturn = new JsInvocation(x.getSourceInfo());
       List<JsExpression> params = toReturn.getArguments();
       for (JsExpression e : x.getArguments()) {
         params.add(0, stack.pop());
@@ -132,7 +132,7 @@
      */
     @Override
     public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
-      JsNameRef toReturn = new JsNameRef(x.getName());
+      JsNameRef toReturn = new JsNameRef(x.getSourceInfo(), x.getName());
 
       if (x.getQualifier() != null) {
         toReturn.setQualifier(stack.pop());
@@ -142,7 +142,7 @@
 
     @Override
     public void endVisit(JsNew x, JsContext<JsExpression> ctx) {
-      JsNew toReturn = new JsNew();
+      JsNew toReturn = new JsNew(x.getSourceInfo());
 
       List<JsExpression> arguments = toReturn.getArguments();
       for (JsExpression a : x.getArguments()) {
@@ -164,7 +164,7 @@
 
     @Override
     public void endVisit(JsObjectLiteral x, JsContext<JsExpression> ctx) {
-      JsObjectLiteral toReturn = new JsObjectLiteral();
+      JsObjectLiteral toReturn = new JsObjectLiteral(x.getSourceInfo());
       List<JsPropertyInitializer> inits = toReturn.getPropertyInitializers();
 
       for (JsPropertyInitializer init : x.getPropertyInitializers()) {
@@ -174,7 +174,7 @@
          * rather than expecting it to be on the stack and having to perform
          * narrowing casts at all stack.pop() invocations.
          */
-        JsPropertyInitializer newInit = new JsPropertyInitializer();
+        JsPropertyInitializer newInit = new JsPropertyInitializer(x.getSourceInfo());
         newInit.setValueExpr(stack.pop());
         newInit.setLabelExpr(stack.pop());
 
@@ -185,14 +185,14 @@
 
     @Override
     public void endVisit(JsPostfixOperation x, JsContext<JsExpression> ctx) {
-      JsPostfixOperation toReturn = new JsPostfixOperation(x.getOperator());
+      JsPostfixOperation toReturn = new JsPostfixOperation(x.getSourceInfo(), x.getOperator());
       toReturn.setArg(stack.pop());
       stack.push(toReturn);
     }
 
     @Override
     public void endVisit(JsPrefixOperation x, JsContext<JsExpression> ctx) {
-      JsPrefixOperation toReturn = new JsPrefixOperation(x.getOperator());
+      JsPrefixOperation toReturn = new JsPrefixOperation(x.getSourceInfo(), x.getOperator());
       toReturn.setArg(stack.pop());
       stack.push(toReturn);
     }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java
index 56d6d85..6b602a5 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsCase;
 import com.google.gwt.dev.js.ast.JsContext;
@@ -51,6 +52,12 @@
    */
   private static class BlockVisitor extends JsVisitor {
 
+    private final JsProgram program;
+
+    public BlockVisitor(JsProgram program) {
+      this.program = program;
+    }
+
     @Override
     public void endVisit(JsBlock x, JsContext<JsStatement> ctx) {
       restructure(x.getStatements());
@@ -71,6 +78,7 @@
      * necessary to prevent any given block from exceeding the maximum size.
      */
     private void restructure(List<JsStatement> statements) {
+      SourceInfo sourceInfo = program.createSourceInfoSynthetic("Restructured to reduce block size");
       // This outer loop will collapse the newly-created block into super-blocks
       while (statements.size() > MAX_BLOCK_SIZE) {
         ListIterator<JsStatement> i = statements.listIterator();
@@ -82,7 +90,7 @@
 
           if (statementsInNewBlock == null) {
             // Replace the current statement with a new block
-            JsBlock newBlock = new JsBlock();
+            JsBlock newBlock = new JsBlock(sourceInfo);
             statementsInNewBlock = newBlock.getStatements();
             i.set(newBlock);
           } else {
@@ -118,6 +126,6 @@
    * Entry point.
    */
   public static void exec(JsProgram program) {
-    (new BlockVisitor()).accept(program);
+    (new BlockVisitor(program)).accept(program);
   }
 }
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 9b4c2b3..a53ed05 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsInliner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsInliner.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.js;
 
 import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.ast.JsArrayAccess;
 import com.google.gwt.dev.js.ast.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -189,7 +190,8 @@
        * Create a new comma expression with the original LHS and the LHS of the
        * nested comma expression.
        */
-      JsBinaryOperation newOp = new JsBinaryOperation(JsBinaryOperator.COMMA);
+      JsBinaryOperation newOp = new JsBinaryOperation(x.getSourceInfo().makeChild(
+          "Simplifying comma expression"), JsBinaryOperator.COMMA);
       newOp.setArg1(x.getArg1());
       newOp.setArg2(toUpdate.getArg1());
 
@@ -727,7 +729,7 @@
          * side-effects.
          */
         if (op.getArg2().hasSideEffects()) {
-          statements.add(0, new JsExprStmt(op.getArg2()));
+          statements.add(0, op.getArg2().makeStmt());
         }
 
         e = op.getArg1();
@@ -739,7 +741,7 @@
        * side-effects.
        */
       if (e.hasSideEffects()) {
-        statements.add(0, new JsExprStmt(e));
+        statements.add(0, e.makeStmt());
       }
 
       if (statements.size() == 0) {
@@ -760,7 +762,8 @@
            * single JsExprStmt with a JsBlock that contains all of the
            * statements.
            */
-          JsBlock b = new JsBlock();
+          JsBlock b = new JsBlock(x.getSourceInfo().makeChild(
+              "Block required for control function"));
           b.getStatements().addAll(statements);
           ctx.replaceMe(b);
           return;
@@ -794,17 +797,18 @@
       assert !statements.isEmpty();
 
       // Find or create the JsVars as the first statement
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild("Synthetic locals");
       JsVars vars;
       if (statements.get(0) instanceof JsVars) {
         vars = (JsVars) statements.get(0);
       } else {
-        vars = new JsVars();
+        vars = new JsVars(sourceInfo);
         statements.add(0, vars);
       }
 
       // Add all variables
       for (JsName name : newLocalVariables) {
-        vars.add(new JsVar(name));
+        vars.add(new JsVar(sourceInfo, name));
       }
     }
 
@@ -893,10 +897,11 @@
        * ensures that this logic will function correctly in the case of a single
        * expression.
        */
+      SourceInfo sourceInfo = x.getSourceInfo().makeChild("Inlined invocation");
       ListIterator<JsExpression> i = hoisted.listIterator(hoisted.size());
       JsExpression op = i.previous();
       while (i.hasPrevious()) {
-        JsBinaryOperation outerOp = new JsBinaryOperation(
+        JsBinaryOperation outerOp = new JsBinaryOperation(sourceInfo,
             JsBinaryOperator.COMMA);
         outerOp.setArg1(i.previous());
         outerOp.setArg2(op);
@@ -1067,7 +1072,8 @@
         return;
       }
 
-      JsExpression replacement = tryGetReplacementExpression(x.getName());
+      JsExpression replacement = tryGetReplacementExpression(
+          x.getSourceInfo().makeChild("Inlined expression"), x.getName());
 
       if (replacement != null) {
         ctx.replaceMe(replacement);
@@ -1092,7 +1098,8 @@
      * given name. Returns <code>null</code> if no replacement has been set
      * for the name.
      */
-    private JsExpression tryGetReplacementExpression(JsName name) {
+    private JsExpression tryGetReplacementExpression(SourceInfo sourceInfo,
+        JsName name) {
       if (paramsToArgsMap.containsKey(name)) {
         /*
          * TODO if we ever allow mutable JsExpression types to be considered
@@ -1101,7 +1108,7 @@
         return paramsToArgsMap.get(name);
 
       } else if (nameReplacements.containsKey(name)) {
-        return nameReplacements.get(name).makeRef();
+        return nameReplacements.get(name).makeRef(sourceInfo);
 
       } else {
         return null;
@@ -1512,13 +1519,15 @@
         // Extract the initialization expression
         JsExpression init = var.getInitExpr();
         if (init != null) {
-          JsBinaryOperation assignment = new JsBinaryOperation(
+          SourceInfo sourceInfo = var.getSourceInfo().makeChild(
+              "Hoisted initializer into inline site");
+          JsBinaryOperation assignment = new JsBinaryOperation(sourceInfo,
               JsBinaryOperator.ASG);
-          assignment.setArg1(var.getName().makeRef());
+          assignment.setArg1(var.getName().makeRef(sourceInfo));
           assignment.setArg2(init);
 
           // Multiple initializers go into a comma expression
-          JsBinaryOperation comma = new JsBinaryOperation(
+          JsBinaryOperation comma = new JsBinaryOperation(sourceInfo,
               JsBinaryOperator.COMMA);
           comma.setArg1(expression);
           comma.setArg2(assignment);
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 e28e8e1..446ad6d 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsParser.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.ast.JsArrayAccess;
 import com.google.gwt.dev.js.ast.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -79,6 +80,7 @@
 
   private JsProgram program;
   private final Stack<JsScope> scopeStack = new Stack<JsScope>();
+  private SourceInfo sourceInfo = SourceInfo.UNKNOWN;
 
   public JsParser() {
     // Create a custom error handler so that we can throw our own exceptions.
@@ -133,6 +135,13 @@
     }
   }
 
+  /**
+   * Set the base SourceInfo object to use when creating new JS AST nodes.
+   */
+  public void setSourceInfo(SourceInfo sourceInfo) {
+    this.sourceInfo = sourceInfo;
+  }
+
   private JsParserException createParserException(String msg, Node offender) {
     // TODO: get source info
     offender.getLineno();
@@ -143,11 +152,16 @@
     return scopeStack.peek();
   }
 
+  private SourceInfo makeSourceInfo(Node node) {
+    return program.createSourceInfo(node.getLineno()
+        + sourceInfo.getStartLine() + 1, sourceInfo.getFileName());
+  }
+
   private JsNode<?> map(Node node) throws JsParserException {
 
     switch (node.getType()) {
       case TokenStream.SCRIPT: {
-        JsBlock block = new JsBlock();
+        JsBlock block = new JsBlock(makeSourceInfo(node));
         mapStatements(block.getStatements(), node);
         return block;
       }
@@ -223,7 +237,8 @@
         return mapName(node);
 
       case TokenStream.STRING:
-        return program.getStringLiteral(node.getString());
+        return program.getStringLiteral(
+            sourceInfo.makeChild("JS String literal"), node.getString());
 
       case TokenStream.NUMBER:
         return mapNumber(node);
@@ -320,7 +335,7 @@
   }
 
   private JsArrayLiteral mapArrayLit(Node node) throws JsParserException {
-    JsArrayLiteral toLit = new JsArrayLiteral();
+    JsArrayLiteral toLit = new JsArrayLiteral(makeSourceInfo(node));
     Node from = node.getFirstChild();
     while (from != null) {
       toLit.getExpressions().add(mapExpression(from));
@@ -341,7 +356,7 @@
     if (unknown instanceof JsStringLiteral) {
       JsStringLiteral lit = (JsStringLiteral) unknown;
       String litName = lit.getValue();
-      return new JsNameRef(litName);
+      return new JsNameRef(makeSourceInfo(nameRefNode), litName);
     } else {
       throw createParserException("Expecting a name reference", nameRefNode);
     }
@@ -400,11 +415,11 @@
     JsExpression to1 = mapExpression(from1);
     JsExpression to2 = mapExpression(from2);
 
-    return new JsBinaryOperation(op, to1, to2);
+    return new JsBinaryOperation(makeSourceInfo(node), op, to1, to2);
   }
 
   private JsBlock mapBlock(Node nodeStmts) throws JsParserException {
-    JsBlock block = new JsBlock();
+    JsBlock block = new JsBlock(makeSourceInfo(nodeStmts));
     mapStatements(block.getStatements(), nodeStmts);
     return block;
   }
@@ -412,14 +427,14 @@
   private JsBreak mapBreak(Node breakNode) {
     Node fromLabel = breakNode.getFirstChild();
     if (fromLabel != null) {
-      return new JsBreak(mapName(fromLabel));
+      return new JsBreak(makeSourceInfo(breakNode), mapName(fromLabel));
     } else {
-      return new JsBreak();
+      return new JsBreak(makeSourceInfo(breakNode));
     }
   }
 
   private JsInvocation mapCall(Node callNode) throws JsParserException {
-    JsInvocation invocation = new JsInvocation();
+    JsInvocation invocation = new JsInvocation(makeSourceInfo(callNode));
 
     // Map the target expression.
     //
@@ -441,7 +456,7 @@
   }
 
   private JsExpression mapConditional(Node condNode) throws JsParserException {
-    JsConditional toCond = new JsConditional();
+    JsConditional toCond = new JsConditional(makeSourceInfo(condNode));
 
     Node fromTest = condNode.getFirstChild();
     toCond.setTestExpression(mapExpression(fromTest));
@@ -458,9 +473,9 @@
   private JsContinue mapContinue(Node contNode) {
     Node fromLabel = contNode.getFirstChild();
     if (fromLabel != null) {
-      return new JsContinue(mapName(fromLabel));
+      return new JsContinue(makeSourceInfo(contNode), mapName(fromLabel));
     } else {
-      return new JsContinue();
+      return new JsContinue(makeSourceInfo(contNode));
     }
   }
 
@@ -474,9 +489,11 @@
     Node from = node.getFirstChild();
     JsExpression to = mapExpression(from);
     if (to instanceof JsNameRef) {
-      return new JsPrefixOperation(JsUnaryOperator.DELETE, to);
+      return new JsPrefixOperation(makeSourceInfo(node),
+          JsUnaryOperator.DELETE, to);
     } else if (to instanceof JsArrayAccess) {
-      return new JsPrefixOperation(JsUnaryOperator.DELETE, to);
+      return new JsPrefixOperation(makeSourceInfo(node),
+          JsUnaryOperator.DELETE, to);
     } else {
       throw createParserException(
           "'delete' can only operate on property names and array elements",
@@ -510,9 +527,9 @@
     // Create and attach the "while" or "do" statement we're mapping to.
     //
     if (isWhile) {
-      return new JsWhile(toTestExpr, toBody);
+      return new JsWhile(makeSourceInfo(ifNode), toTestExpr, toBody);
     } else {
-      return new JsDoWhile(toTestExpr, toBody);
+      return new JsDoWhile(makeSourceInfo(ifNode), toTestExpr, toBody);
     }
   }
 
@@ -578,7 +595,7 @@
         Node fromIterVarName = fromIter.getFirstChild();
         String fromName = fromIterVarName.getString();
         JsName toName = getScope().declareName(fromName);
-        toForIn = new JsForIn(toName);
+        toForIn = new JsForIn(makeSourceInfo(forNode), toName);
         Node fromIterInit = fromIterVarName.getFirstChild();
         if (fromIterInit != null) {
           // That has an initializer expression (useful only for side effects).
@@ -588,7 +605,7 @@
       } else {
         // An unnamed iterator var.
         //
-        toForIn = new JsForIn();
+        toForIn = new JsForIn(makeSourceInfo(forNode));
         toForIn.setIterExpr(mapExpression(fromIter));
       }
       toForIn.setObjExpr(mapExpression(fromObjExpr));
@@ -606,7 +623,7 @@
     } else {
       // Regular ol' for loop.
       //
-      JsFor toFor = new JsFor();
+      JsFor toFor = new JsFor(makeSourceInfo(forNode));
 
       // The first item is either an expression or a JsVars.
       //
@@ -648,7 +665,8 @@
 
     // Create it, and set the params.
     //
-    JsFunction toFn = new JsFunction(getScope(), toFnName);
+    SourceInfo fnSourceInfo = makeSourceInfo(fnNode);
+    JsFunction toFn = new JsFunction(fnSourceInfo, getScope(), toFnName);
 
     // Creating a function also creates a new scope, which we push onto
     // the scope stack.
@@ -659,7 +677,7 @@
       String fromParamName = fromParamNode.getString();
       // should this be unique? I think not since you can have dup args.
       JsName paramName = toFn.getScope().declareName(fromParamName);
-      toFn.getParameters().add(new JsParameter(paramName));
+      toFn.getParameters().add(new JsParameter(fnSourceInfo, paramName));
       fromParamNode = fromParamNode.getNext();
     }
 
@@ -682,7 +700,7 @@
     JsExpression to1 = mapExpression(from1);
     JsExpression to2 = mapExpression(from2);
 
-    return new JsArrayAccess(to1, to2);
+    return new JsArrayAccess(makeSourceInfo(getElemNode), to1, to2);
   }
 
   private JsNameRef mapGetProp(Node getPropNode) throws JsParserException {
@@ -698,7 +716,7 @@
       //
       Object obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP);
       assert (obj instanceof String);
-      toNameRef = new JsNameRef((String) obj);
+      toNameRef = new JsNameRef(makeSourceInfo(getPropNode), (String) obj);
     }
     toNameRef.setQualifier(toQualifier);
 
@@ -715,7 +733,7 @@
 
     // Create the "if" statement we're mapping to.
     //
-    JsIf toIf = new JsIf();
+    JsIf toIf = new JsIf(makeSourceInfo(ifNode));
 
     // Map the test expression.
     //
@@ -752,7 +770,7 @@
     String fromName = labelNode.getFirstChild().getString();
     JsName toName = getScope().declareName(fromName);
     Node fromStmt = labelNode.getFirstChild().getNext();
-    JsLabel toLabel = new JsLabel(toName);
+    JsLabel toLabel = new JsLabel(makeSourceInfo(labelNode), toName);
     toLabel.setStmt(mapStatement(fromStmt));
     return toLabel;
   }
@@ -763,12 +781,12 @@
    */
   private JsNameRef mapName(Node node) {
     String ident = node.getString();
-    return new JsNameRef(ident);
+    return new JsNameRef(makeSourceInfo(node), ident);
   }
 
   private JsNew mapNew(Node newNode) throws JsParserException {
 
-    JsNew newExpr = new JsNew();
+    JsNew newExpr = new JsNew(makeSourceInfo(newNode));
 
     // Map the constructor expression, which is often just the name of
     // some lambda.
@@ -793,7 +811,7 @@
   }
 
   private JsExpression mapObjectLit(Node objLitNode) throws JsParserException {
-    JsObjectLiteral toLit = new JsObjectLiteral();
+    JsObjectLiteral toLit = new JsObjectLiteral(makeSourceInfo(objLitNode));
     Node fromPropInit = objLitNode.getFirstChild();
     while (fromPropInit != null) {
 
@@ -810,8 +828,8 @@
       }
       JsExpression toValueExpr = mapExpression(fromValueExpr);
 
-      JsPropertyInitializer toPropInit = new JsPropertyInitializer(toLabelExpr,
-          toValueExpr);
+      JsPropertyInitializer toPropInit = new JsPropertyInitializer(
+          makeSourceInfo(fromLabelExpr), toLabelExpr, toValueExpr);
       toLit.getPropertyInitializers().add(toPropInit);
 
       // Begin the next property initializer, if there is one.
@@ -839,20 +857,20 @@
       throws JsParserException {
     Node from = node.getFirstChild();
     JsExpression to = mapExpression(from);
-    return new JsPostfixOperation(op, to);
+    return new JsPostfixOperation(makeSourceInfo(node), op, to);
   }
 
   private JsExpression mapPrefixOperation(JsUnaryOperator op, Node node)
       throws JsParserException {
     Node from = node.getFirstChild();
     JsExpression to = mapExpression(from);
-    return new JsPrefixOperation(op, to);
+    return new JsPrefixOperation(makeSourceInfo(node), op, to);
   }
 
   private JsExpression mapPrimary(Node node) throws JsParserException {
     switch (node.getIntDatum()) {
       case TokenStream.THIS:
-        return new JsThisRef();
+        return new JsThisRef(makeSourceInfo(node));
 
       case TokenStream.TRUE:
         return program.getTrueLiteral();
@@ -869,7 +887,7 @@
   }
 
   private JsNode<?> mapRegExp(Node regExpNode) {
-    JsRegExp toRegExp = new JsRegExp();
+    JsRegExp toRegExp = new JsRegExp(makeSourceInfo(regExpNode));
 
     Node fromPattern = regExpNode.getFirstChild();
     toRegExp.setPattern(fromPattern.getString());
@@ -910,7 +928,7 @@
   }
 
   private JsReturn mapReturn(Node returnNode) throws JsParserException {
-    JsReturn toReturn = new JsReturn();
+    JsReturn toReturn = new JsReturn(makeSourceInfo(returnNode));
     Node from = returnNode.getFirstChild();
     if (from != null) {
       JsExpression to = mapExpression(from);
@@ -930,7 +948,8 @@
     Node fromRhs = setElemNode.getFirstChild().getNext().getNext();
     JsExpression toRhs = mapExpression(fromRhs);
 
-    return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, toRhs);
+    return new JsBinaryOperation(makeSourceInfo(setElemNode),
+        JsBinaryOperator.ASG, lhs, toRhs);
   }
 
   private JsExpression mapSetProp(Node getPropNode) throws JsParserException {
@@ -943,7 +962,8 @@
     Node fromRhs = getPropNode.getFirstChild().getNext().getNext();
     JsExpression toRhs = mapExpression(fromRhs);
 
-    return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, toRhs);
+    return new JsBinaryOperation(makeSourceInfo(getPropNode),
+        JsBinaryOperator.ASG, lhs, toRhs);
   }
 
   private JsExpression mapShiftVariant(Node shiftNode) throws JsParserException {
@@ -1003,7 +1023,7 @@
   }
 
   private JsSwitch mapSwitchStatement(Node switchNode) throws JsParserException {
-    JsSwitch toSwitch = new JsSwitch();
+    JsSwitch toSwitch = new JsSwitch(makeSourceInfo(switchNode));
 
     // The switch expression.
     //
@@ -1015,7 +1035,7 @@
     Node fromMember = fromSwitchExpr.getNext();
     while (fromMember != null) {
       if (fromMember.getType() == TokenStream.CASE) {
-        JsCase toCase = new JsCase();
+        JsCase toCase = new JsCase(makeSourceInfo(fromMember));
 
         // Set the case expression. In JS, this can be any expression.
         //
@@ -1035,7 +1055,7 @@
         // If more than one is present, we keep the last one.
         //
         assert (fromMember.getType() == TokenStream.DEFAULT);
-        JsDefault toDefault = new JsDefault();
+        JsDefault toDefault = new JsDefault(makeSourceInfo(fromMember));
 
         // Set the default statements.
         //
@@ -1056,12 +1076,13 @@
     // Create, map, and attach.
     //
     Node fromExpr = throwNode.getFirstChild();
-    JsThrow toThrow = new JsThrow(mapExpression(fromExpr));
+    JsThrow toThrow = new JsThrow(makeSourceInfo(throwNode),
+        mapExpression(fromExpr));
     return toThrow;
   }
 
   private JsTry mapTryStatement(Node tryNode) throws JsParserException {
-    JsTry toTry = new JsTry();
+    JsTry toTry = new JsTry(makeSourceInfo(tryNode));
 
     // Map the "try" body.
     //
@@ -1077,7 +1098,8 @@
       // Map the catch variable.
       //
       Node fromCatchVarName = fromCatchNode.getFirstChild();
-      JsCatch catchBlock = new JsCatch(getScope(), fromCatchVarName.getString());
+      JsCatch catchBlock = new JsCatch(makeSourceInfo(fromCatchNode),
+          getScope(), fromCatchVarName.getString());
 
       // Pre-advance to the next catch block, if any.
       // We do this here to decide whether or not this is the last one.
@@ -1149,7 +1171,7 @@
   }
 
   private JsVars mapVar(Node varNode) throws JsParserException {
-    JsVars toVars = new JsVars();
+    JsVars toVars = new JsVars(makeSourceInfo(varNode));
     Node fromVar = varNode.getFirstChild();
     while (fromVar != null) {
       // Use a conservative name allocation strategy that allocates all names
@@ -1158,7 +1180,7 @@
       //
       String fromName = fromVar.getString();
       JsName toName = getScope().declareName(fromName);
-      JsVars.JsVar toVar = new JsVars.JsVar(toName);
+      JsVars.JsVar toVar = new JsVars.JsVar(makeSourceInfo(fromVar), toName);
 
       Node fromInit = fromVar.getFirstChild();
       if (fromInit != null) {
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 050bc77..653f8db 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.ast.CanBooleanEval;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
 import com.google.gwt.dev.js.ast.JsBinaryOperator;
@@ -143,7 +144,8 @@
         itr.previous();
         itr.add(x.makeStmt());
         itr.next();
-        ctx.replaceMe(x.getName().makeRef());
+        ctx.replaceMe(x.getName().makeRef(
+            x.getSourceInfo().makeChild("Shuffled evaluation order")));
       }
 
       // Dive into the function itself.
@@ -186,10 +188,12 @@
 
     @Override
     public void endVisit(JsVars x, JsContext<JsStatement> ctx) {
-      JsVars strippedVars = new JsVars();
+      JsVars strippedVars = new JsVars(x.getSourceInfo().makeChild(
+          "Simplified execution"));
       boolean mustReplace = false;
       for (JsVar var : x) {
-        JsVar strippedVar = new JsVar(var.getName());
+        JsVar strippedVar = new JsVar(var.getSourceInfo().makeChild(
+            "Simplified execution"), var.getName());
         strippedVars.add(strippedVar);
         if (var.getInitExpr() != null) {
           mustReplace = true;
@@ -298,13 +302,15 @@
         CanBooleanEval condEval = (CanBooleanEval) condExpr;
         if (condEval.isBooleanTrue()) {
           // e.g. (true() ? then : else) -> true() && then
-          JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.AND,
-              condExpr, thenExpr);
+          JsBinaryOperation binOp = new JsBinaryOperation(
+              x.getSourceInfo().makeChild("Simplified always-true condition"),
+              JsBinaryOperator.AND, condExpr, thenExpr);
           ctx.replaceMe(accept(binOp));
         } else if (condEval.isBooleanFalse()) {
           // e.g. (false() ? then : else) -> false() || else
-          JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.OR,
-              condExpr, elseExpr);
+          JsBinaryOperation binOp = new JsBinaryOperation(
+              x.getSourceInfo().makeChild("Simplified always-false condition"),
+              JsBinaryOperator.OR, condExpr, elseExpr);
           ctx.replaceMe(accept(binOp));
         }
       }
@@ -327,7 +333,8 @@
           FindBreakContinueStatementsVisitor visitor = new FindBreakContinueStatementsVisitor();
           visitor.accept(x.getBody());
           if (!visitor.hasBreakContinueStatements()) {
-            JsBlock block = new JsBlock();
+            JsBlock block = new JsBlock(x.getSourceInfo().makeChild(
+                "Simplified always-false condition"));
             block.getStatements().add(x.getBody());
             block.getStatements().add(expr.makeStmt());
             ctx.replaceMe(accept(block));
@@ -360,7 +367,8 @@
 
         // If false, replace with initializers and condition.
         if (cond.isBooleanFalse()) {
-          JsBlock block = new JsBlock();
+          JsBlock block = new JsBlock(x.getSourceInfo().makeChild(
+              "Simplified always-false condition"));
           if (x.getInitExpr() != null) {
             block.getStatements().add(x.getInitExpr().makeStmt());
           }
@@ -387,6 +395,7 @@
       JsExpression expr = x.getIfExpr();
       JsStatement thenStmt = x.getThenStmt();
       JsStatement elseStmt = x.getElseStmt();
+      SourceInfo sourceInfo;
       if (expr instanceof CanBooleanEval) {
         CanBooleanEval cond = (CanBooleanEval) expr;
         JsStatement onlyStmtToExecute;
@@ -394,13 +403,17 @@
         if (cond.isBooleanTrue()) {
           onlyStmtToExecute = thenStmt;
           removed = elseStmt;
+          sourceInfo = x.getSourceInfo().makeChild(
+              "Simplified always-true condition");
         } else if (cond.isBooleanFalse()) {
           onlyStmtToExecute = elseStmt;
           removed = thenStmt;
+          sourceInfo = x.getSourceInfo().makeChild(
+              "Simplified always-false condition");
         } else {
           return;
         }
-        JsBlock block = new JsBlock();
+        JsBlock block = new JsBlock(sourceInfo);
         block.getStatements().add(expr.makeStmt());
 
         if (onlyStmtToExecute != null) {
@@ -452,7 +465,8 @@
 
         // If false, replace with condition.
         if (cond.isBooleanFalse()) {
-          JsBlock block = new JsBlock();
+          JsBlock block = new JsBlock(x.getSourceInfo().makeChild(
+              "Simplified always-false condition"));
           block.getStatements().add(expr.makeStmt());
           JsStatement decls = ensureDeclarations(x.getBody());
           if (decls != null) {
@@ -525,7 +539,8 @@
       } else if (stmts.size() == 1) {
         return stmts.get(0);
       } else {
-        JsBlock jsBlock = new JsBlock();
+        JsBlock jsBlock = new JsBlock(stmt.getSourceInfo().makeChild(
+            "Ensuring declarations"));
         jsBlock.getStatements().addAll(stmts);
         return jsBlock;
       }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
index bc7c92d..5c3895f 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
 import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsContext;
@@ -131,7 +132,8 @@
         toCreate.put(x, name);
       }
 
-      ctx.replaceMe(name.makeRef());
+      ctx.replaceMe(name.makeRef(x.getSourceInfo().makeChild(
+          "Interned reference")));
 
       return false;
     }
@@ -156,15 +158,17 @@
    * @param scope the JsScope in which to reserve the new identifiers
    * @return <code>true</code> if any changes were made to the block
    */
-  public static boolean exec(JsBlock block, JsScope scope) {
+  public static boolean exec(JsProgram program, JsBlock block, JsScope scope) {
     StringVisitor v = new StringVisitor(scope);
     v.accept(block);
 
     if (v.toCreate.size() > 0) {
       // Create the pool of variable names.
-      JsVars vars = new JsVars();
+      JsVars vars = new JsVars(
+          program.createSourceInfoSynthetic("Interned string pool"));
+      SourceInfo sourceInfo = program.createSourceInfoSynthetic("Interned string assignment");
       for (Map.Entry<JsStringLiteral, JsName> entry : v.toCreate.entrySet()) {
-        JsVar var = new JsVar(entry.getValue());
+        JsVar var = new JsVar(sourceInfo, entry.getValue());
         var.setInitExpr(entry.getKey());
         vars.add(var);
       }
@@ -184,7 +188,7 @@
    * @return <code>true</code> if any changes were made to the program
    */
   public static boolean exec(JsProgram program) {
-    return exec(program.getGlobalBlock(), program.getScope());
+    return exec(program, program.getGlobalBlock(), program.getScope());
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/js/SourceInfoHistogram.java b/dev/core/src/com/google/gwt/dev/js/SourceInfoHistogram.java
new file mode 100644
index 0000000..9ba532c
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/SourceInfoHistogram.java
@@ -0,0 +1,536 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.js;
+
+import com.google.gwt.dev.jjs.HasSourceInfo;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsVisitable;
+import com.google.gwt.dev.util.DefaultTextOutput;
+import com.google.gwt.dev.util.TextOutput;
+import com.google.gwt.dev.util.Util;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.Stack;
+import java.util.TreeMap;
+
+/**
+ * This is a test reporting visitor for SOYC experiments. It will likely
+ * disappear once a proper export format and viewer application are written.
+ */
+public class SourceInfoHistogram {
+  /**
+   * Stub; unused and will probably be discarded.
+   */
+  public static class HistogramData {
+    // Use weak references to AST nodes. If the AST node gets pruned, we won't
+    // need it either
+  }
+
+  private static class HSVUtils {
+    private static final String[] VALUES = {
+        "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
+        "e", "f"};
+
+    /**
+     * The number of degrees to advance around H-space.
+     */
+    private static final int DEGREES = 53;
+    private static final double SATURATION = 0.2;
+    private static final double VALUE = 0.9;
+
+    /**
+     * We go around and around the HSV color space to generate colors.
+     * 
+     * @param color The index of the item to be colored.
+     * @return An RGB in hex format for the color.
+     */
+    public static String color(int color) {
+      final int h = (DEGREES * color) % 360;
+      final double s = SATURATION;
+      final double v = VALUE;
+
+      final int hi = (int) Math.floor(h / 60.) % 6;
+      final double f = (h / 60.) - hi;
+      final double p = v * (1 - s);
+      final double q = v * (1 - f * s);
+      final double t = v * (1 - (1 - f) * s);
+
+      final double r, g, b;
+      switch (hi) {
+        case 0:
+          r = v;
+          g = t;
+          b = p;
+          break;
+        case 1:
+          r = q;
+          g = v;
+          b = p;
+          break;
+        case 2:
+          r = p;
+          g = v;
+          b = t;
+          break;
+        case 3:
+          r = p;
+          g = q;
+          b = v;
+          break;
+        case 4:
+          r = t;
+          g = p;
+          b = v;
+          break;
+        case 5:
+          r = v;
+          g = p;
+          b = q;
+          break;
+        default:
+          throw new RuntimeException("Unexpected hi of " + hi);
+      }
+
+      return numberToHex(r) + numberToHex(g) + numberToHex(b);
+    }
+
+    /**
+     * Convert a number between 0 and 1 to a two-char hex value between 00 and
+     * FF.
+     */
+    private static String numberToHex(double number) {
+      number *= 255;
+      if (number <= 0) {
+        return "00";
+      } else if (number >= 255) {
+        return "FF";
+      }
+
+      String toReturn = VALUES[(int) number / 16] + VALUES[(int) number % 16];
+      return toReturn;
+    }
+
+    /**
+     * Utility class, no public constructor.
+     */
+    private HSVUtils() {
+    }
+  }
+
+  private static class HtmlTextOutput implements TextOutput {
+    private final DefaultTextOutput out;
+
+    public HtmlTextOutput(boolean compact) {
+      out = new DefaultTextOutput(compact);
+    }
+
+    public void indentIn() {
+      out.indentIn();
+    }
+
+    public void indentOut() {
+      out.indentOut();
+    }
+
+    public void newline() {
+      out.newline();
+    }
+
+    public void newlineOpt() {
+      out.newlineOpt();
+    }
+
+    public void print(char c) {
+      print(String.valueOf(c));
+    }
+
+    public void print(char[] s) {
+      print(String.valueOf(s));
+    }
+
+    public void print(String s) {
+      out.print(Util.escapeXml(s));
+    }
+
+    public void printOpt(char c) {
+      printOpt(String.valueOf(c));
+    }
+
+    public void printOpt(char[] s) {
+      printOpt(String.valueOf(s));
+    }
+
+    public void printOpt(String s) {
+      out.printOpt(Util.escapeXml(s));
+    }
+
+    public void printRaw(String s) {
+      out.print(s);
+    }
+
+    @Override
+    public String toString() {
+      return out.toString();
+    }
+  }
+
+  private static class JavaNormalReportVisitor extends
+      JsSourceGenerationVisitor {
+    Stack<HasSourceInfo> stack = new Stack<HasSourceInfo>();
+    int total = 0;
+    Map<String, Integer> totalsByFileName = new HashMap<String, Integer>();
+
+    private final SortedMap<SourceInfo, StringBuilder> infoToSource = new TreeMap<SourceInfo, StringBuilder>(
+        SOURCE_INFO_COMPARATOR);
+    private final SwitchTextOutput out;
+
+    public JavaNormalReportVisitor(SwitchTextOutput out) {
+      super(out);
+      this.out = out;
+    }
+
+    @Override
+    protected <T extends JsVisitable<T>> T doAccept(T node) {
+      boolean openContext = node instanceof HasSourceInfo;
+      SourceInfo sourceInfo = null;
+      try {
+        if (openContext) {
+          sourceInfo = ((HasSourceInfo) node).getSourceInfo();
+          if (!stack.isEmpty()) {
+            int count = commit(stack.peek(), true);
+            accumulateTotal(sourceInfo, count);
+          }
+          stack.push((HasSourceInfo) node);
+          out.begin();
+        }
+        return super.doAccept(node);
+      } finally {
+        if (openContext) {
+          int count = commit((HasSourceInfo) node, false);
+          accumulateTotal(sourceInfo, count);
+          if (stack.pop() != node) {
+            throw new RuntimeException("Unexpected node popped");
+          }
+        }
+      }
+    }
+
+    @Override
+    protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
+      for (T node : collection) {
+        doAccept(node);
+      }
+    }
+
+    @Override
+    protected JsExpression doAcceptLvalue(JsExpression expr) {
+      return doAccept(expr);
+    }
+
+    @Override
+    protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
+        List<T> collection) {
+      doAcceptList(collection);
+    }
+
+    private void accumulateTotal(SourceInfo sourceInfo, int count) {
+      for (SourceInfo root : sourceInfo.getRoots()) {
+        String fileName = root.getFileName();
+        Integer sourceTotal = totalsByFileName.get(fileName);
+        if (sourceTotal == null) {
+          totalsByFileName.put(fileName, count);
+        } else {
+          totalsByFileName.put(fileName, sourceTotal + count);
+        }
+      }
+      total += count;
+    }
+
+    private int commit(HasSourceInfo x, boolean expectMore) {
+      StringBuilder builder = infoToSource.get(x.getSourceInfo());
+      if (builder == null) {
+        builder = new StringBuilder();
+        infoToSource.put(x.getSourceInfo(), builder);
+      }
+      if (expectMore) {
+        return out.flush(builder);
+      } else {
+        return out.commit(builder);
+      }
+    }
+  }
+
+  private static class JsNormalReportVisitor extends JsSourceGenerationVisitor {
+    private final HtmlTextOutput out;
+    private final Stack<SourceInfo> context = new Stack<SourceInfo>();
+
+    public JsNormalReportVisitor(HtmlTextOutput out) {
+      super(out);
+      this.out = out;
+    }
+
+    @Override
+    protected <T extends JsVisitable<T>> T doAccept(T node) {
+      boolean openNode = false;
+      if (node instanceof HasSourceInfo) {
+        SourceInfo info = ((HasSourceInfo) node).getSourceInfo();
+        openNode = context.isEmpty()
+            || SOURCE_INFO_COMPARATOR.compare(context.peek(), info) != 0;
+        if (openNode) {
+          String color;
+          if (context.contains(info)) {
+            color = HSVUtils.color(context.indexOf(info));
+          } else {
+            color = HSVUtils.color(context.size());
+          }
+          context.push(info);
+          out.printRaw("<div class=\"node\" style=\"background:#" + color
+              + ";\">");
+          out.printRaw("<div class=\"story\">");
+          out.print(info.getStory());
+          out.printRaw("</div>");
+        }
+      }
+      T toReturn = super.doAccept(node);
+      if (openNode) {
+        out.printRaw("</div>");
+        context.pop();
+      }
+      return toReturn;
+    }
+
+    @Override
+    protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
+      for (T node : collection) {
+        doAccept(node);
+      }
+    }
+
+    @Override
+    protected JsExpression doAcceptLvalue(JsExpression expr) {
+      return doAccept(expr);
+    }
+
+    @Override
+    protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
+        List<T> collection) {
+      doAcceptList(collection);
+    }
+  }
+
+  private static class SwitchTextOutput implements TextOutput {
+    private Stack<DefaultTextOutput> outs = new Stack<DefaultTextOutput>();
+
+    public void begin() {
+      outs.push(new DefaultTextOutput(true));
+    }
+
+    public int commit(StringBuilder build) {
+      String string = outs.pop().toString();
+      build.append(string);
+      return string.length();
+    }
+
+    public int flush(StringBuilder build) {
+      int toReturn = commit(build);
+      begin();
+      return toReturn;
+    }
+
+    public void indentIn() {
+      // outs.peek().indentIn();
+    }
+
+    public void indentOut() {
+      // outs.peek().indentOut();
+    }
+
+    public void newline() {
+      outs.peek().newline();
+    }
+
+    public void newlineOpt() {
+      outs.peek().newlineOpt();
+    }
+
+    public void print(char c) {
+      outs.peek().print(c);
+    }
+
+    public void print(char[] s) {
+      outs.peek().print(s);
+    }
+
+    public void print(String s) {
+      outs.peek().print(s);
+    }
+
+    public void printOpt(char c) {
+      outs.peek().printOpt(c);
+    }
+
+    public void printOpt(char[] s) {
+      outs.peek().printOpt(s);
+    }
+
+    public void printOpt(String s) {
+      outs.peek().printOpt(s);
+    }
+  }
+
+  private static final Comparator<SourceInfo> SOURCE_INFO_COMPARATOR = new Comparator<SourceInfo>() {
+    public int compare(SourceInfo o1, SourceInfo o2) {
+      int toReturn = o1.getFileName().compareTo(o2.getFileName());
+      if (toReturn != 0) {
+        return toReturn;
+      }
+
+      toReturn = o1.getStartLine() - o2.getStartLine();
+      if (toReturn != 0) {
+        return toReturn;
+      }
+
+      // TODO need a counter in SourceInfos
+      return o1.getStory().compareTo(o2.getStory());
+    }
+  };
+
+  public static HistogramData exec(JProgram program) {
+    return new HistogramData();
+  }
+
+  public static void exec(JsProgram program, HistogramData data,
+      String outputPath) {
+    writeJavaNormalReport(program, outputPath);
+    writeJsNormalReport(program, outputPath);
+  }
+
+  private static void writeJavaNormalReport(JsProgram program, String outputPath) {
+    JavaNormalReportVisitor v = new JavaNormalReportVisitor(
+        new SwitchTextOutput());
+    v.accept(program);
+
+    // Concatenate the per-SourceInfo data into per-file contents
+    Map<String, StringBuffer> contentsByFile = new TreeMap<String, StringBuffer>();
+    for (Map.Entry<SourceInfo, StringBuilder> contents : v.infoToSource.entrySet()) {
+      SourceInfo sourceInfo = contents.getKey();
+      String currentFile = sourceInfo.getFileName();
+      StringBuffer buffer = contentsByFile.get(currentFile);
+      if (buffer == null) {
+        buffer = new StringBuffer();
+        contentsByFile.put(currentFile, buffer);
+        buffer.append("<div class=\"fileHeader\">\n");
+        buffer.append(Util.escapeXml(String.format("%s : %2.1f%%", currentFile,
+            (100.0 * v.totalsByFileName.get(currentFile) / v.total))));
+        buffer.append("</div>\n");
+      }
+
+      buffer.append("<div class=\"jsLine\">");
+      buffer.append("<div class=\"story\">");
+      buffer.append(Util.escapeXml(sourceInfo.getStory()));
+      buffer.append("</div>");
+      buffer.append(Util.escapeXml(contents.getValue().toString()));
+      buffer.append("</div>\n");
+    }
+
+    // Order the contents based on file size
+    Map<Integer, StringBuffer> orderedContents = new TreeMap<Integer, StringBuffer>();
+    for (Map.Entry<String, StringBuffer> entry : contentsByFile.entrySet()) {
+      int size = -v.totalsByFileName.get(entry.getKey());
+      StringBuffer appendTo = orderedContents.get(size);
+      if (appendTo != null) {
+        appendTo.append(entry.getValue());
+      } else {
+        orderedContents.put(size, entry.getValue());
+      }
+    }
+
+    PrintWriter out;
+    try {
+      File outputPathDir = new File(outputPath);
+      outputPathDir.mkdirs();
+      out = new PrintWriter(new FileWriter(File.createTempFile("soyc",
+          "-java.html", outputPathDir)));
+    } catch (IOException e) {
+      out = null;
+    }
+
+    out.println("<html><head>");
+    out.println("<style>"
+        + "* {font-family: monospace;}"
+        + ".file {clear: both;}"
+        + ".file * {display: none;}"
+        + ".file .fileHeader {display: block; cursor: pointer;}"
+        + ".fileOpen .fileHeader {clear: both;}"
+        + ".fileOpen .javaLine {clear: both; float: left; white-space: pre; background: #efe;}"
+        + ".fileOpen .jsLine {outline: thin solid black; float: right; clear: right; white-space: pre; background: #ddd;}"
+        + ".story {display:none;}"
+        + "div.jsLine:hover .story{display:block; position: absolute; left:0; background: #eef;}"
+        + "</style>");
+
+    out.println("</head><body>");
+
+    out.println(String.format("<h1>Total bytes: %d</h1>", v.total));
+    for (StringBuffer buffer : orderedContents.values()) {
+      out.println("<div class=\"file\" onclick=\"this.className=(this.className=='file'?'fileOpen':'file')\">");
+      out.println(buffer.toString());
+      out.println("</div>");
+    }
+
+    out.println("<h1>Done</h1>");
+    out.println("</body></html>");
+    out.close();
+  }
+
+  private static void writeJsNormalReport(JsProgram program, String outputPath) {
+    HtmlTextOutput htmlOut = new HtmlTextOutput(false);
+    JsNormalReportVisitor v = new JsNormalReportVisitor(htmlOut);
+    v.accept(program);
+
+    PrintWriter out;
+    try {
+      File outputPathDir = new File(outputPath);
+      outputPathDir.mkdirs();
+      out = new PrintWriter(new FileWriter(File.createTempFile("soyc",
+          "-js.html", outputPathDir)));
+    } catch (IOException e) {
+      out = null;
+    }
+
+    out.println("<html><head>");
+    out.println("<style>" + "* {white-space: pre; font-family: monospace;}"
+        + ".node {display:inline; z-index: 0;}" + ".story {display: none;}"
+        + "div.node:hover > .story {"
+        + "  display:block; float:right; clear: right; background: inherit; "
+        + "  position: relative; border-left: 8px solid white; z-index: 1;}"
+        + "</style>");
+    out.println("</head><body>");
+    out.println(htmlOut.toString());
+    out.println("</body></html>");
+    out.close();
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsArrayAccess.java b/dev/core/src/com/google/gwt/dev/js/ast/JsArrayAccess.java
index 2628f2e..77f6604 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsArrayAccess.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsArrayAccess.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a javascript expression for array access.
  */
@@ -24,10 +26,13 @@
 
   private JsExpression indexExpr;
 
-  public JsArrayAccess() {
+  public JsArrayAccess(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsArrayAccess(JsExpression arrayExpr, JsExpression indexExpr) {
+  public JsArrayAccess(SourceInfo sourceInfo, JsExpression arrayExpr,
+      JsExpression indexExpr) {
+    super(sourceInfo);
     this.arrayExpr = arrayExpr;
     this.indexExpr = indexExpr;
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsArrayLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsArrayLiteral.java
index aa2e978..8bcaeb9 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsArrayLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsArrayLiteral.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,7 +27,8 @@
 
   private final List<JsExpression> exprs = new ArrayList<JsExpression>();
 
-  public JsArrayLiteral() {
+  public JsArrayLiteral(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsExpression> getExpressions() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java
index ce14a46..12a8767 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript binary operation.
  */
@@ -26,12 +28,13 @@
 
   private final JsBinaryOperator op;
 
-  public JsBinaryOperation(JsBinaryOperator op) {
-    this(op, null, null);
+  public JsBinaryOperation(SourceInfo sourceInfo, JsBinaryOperator op) {
+    this(sourceInfo, op, null, null);
   }
 
-  public JsBinaryOperation(JsBinaryOperator op, JsExpression arg1,
-      JsExpression arg2) {
+  public JsBinaryOperation(SourceInfo sourceInfo, JsBinaryOperator op,
+      JsExpression arg1, JsExpression arg2) {
+    super(sourceInfo);
     this.op = op;
     this.arg1 = arg1;
     this.arg2 = arg2;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsBlock.java b/dev/core/src/com/google/gwt/dev/js/ast/JsBlock.java
index 10df903..a4a06c8 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsBlock.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsBlock.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,7 +27,8 @@
 
   private final List<JsStatement> stmts = new ArrayList<JsStatement>();
 
-  public JsBlock() {
+  public JsBlock(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsStatement> getStatements() {
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 c38b8f0..06d6a0f 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript literal boolean expression.
  */
@@ -23,7 +25,8 @@
   private final boolean value;
 
   // Should be interned by JsProgram
-  JsBooleanLiteral(boolean value) {
+  JsBooleanLiteral(SourceInfo sourceInfo, boolean value) {
+    super(sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsBreak.java b/dev/core/src/com/google/gwt/dev/js/ast/JsBreak.java
index 204e94d..638029e 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsBreak.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsBreak.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents the JavaScript break statement.
  */
@@ -22,11 +24,12 @@
 
   private final JsNameRef label;
 
-  public JsBreak() {
-    this(null);
+  public JsBreak(SourceInfo sourceInfo) {
+    this(sourceInfo, null);
   }
 
-  public JsBreak(JsNameRef label) {
+  public JsBreak(SourceInfo sourceInfo, JsNameRef label) {
+    super(sourceInfo);
     this.label = label;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsCase.java b/dev/core/src/com/google/gwt/dev/js/ast/JsCase.java
index 979891d..a1c7006 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsCase.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents the JavaScript case statement.
  */
@@ -22,7 +24,8 @@
 
   private JsExpression caseExpr;
 
-  public JsCase() {
+  public JsCase(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public JsExpression getCaseExpr() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsCatch.java b/dev/core/src/com/google/gwt/dev/js/ast/JsCatch.java
index 855300b..2a46d21 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsCatch.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsCatch.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript catch clause.
  */
@@ -28,10 +30,11 @@
 
   private final JsParameter param;
 
-  public JsCatch(JsScope parent, String ident) {
+  public JsCatch(SourceInfo sourceInfo, JsScope parent, String ident) {
+    super(sourceInfo);
     assert (parent != null);
     scope = new JsCatchScope(parent, ident);
-    param = new JsParameter(scope.findExistingName(ident));
+    param = new JsParameter(sourceInfo, scope.findExistingName(ident));
   }
 
   public JsBlock getBody() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsConditional.java b/dev/core/src/com/google/gwt/dev/js/ast/JsConditional.java
index e10127c..abd9d43 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsConditional.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsConditional.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript conditional expression.
  */
@@ -26,11 +28,13 @@
 
   private JsExpression thenExpr;
 
-  public JsConditional() {
+  public JsConditional(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsConditional(JsExpression testExpr, JsExpression thenExpr,
-      JsExpression elseExpr) {
+  public JsConditional(SourceInfo sourceInfo, JsExpression testExpr,
+      JsExpression thenExpr, JsExpression elseExpr) {
+    super(sourceInfo);
     this.testExpr = testExpr;
     this.thenExpr = thenExpr;
     this.elseExpr = elseExpr;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsContinue.java b/dev/core/src/com/google/gwt/dev/js/ast/JsContinue.java
index 192aecf..8efb802 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsContinue.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsContinue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents the JavaScript continue statement.
  */
@@ -22,11 +24,12 @@
 
   private final JsNameRef label;
 
-  public JsContinue() {
-    this(null);
+  public JsContinue(SourceInfo sourceInfo) {
+    this(sourceInfo, null);
   }
 
-  public JsContinue(JsNameRef label) {
+  public JsContinue(SourceInfo sourceInfo, JsNameRef label) {
+    super(sourceInfo);
     this.label = label;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsDebugger.java b/dev/core/src/com/google/gwt/dev/js/ast/JsDebugger.java
index 667ffa7..5f0186c 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsDebugger.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsDebugger.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,11 +15,17 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript debugger statement.
  */
 public class JsDebugger extends JsStatement {
 
+  public JsDebugger(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
+
   public void traverse(JsVisitor v, JsContext<JsStatement> ctx) {
     v.visit(this, ctx);
     v.endVisit(this, ctx);
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsDefault.java b/dev/core/src/com/google/gwt/dev/js/ast/JsDefault.java
index ca9b658..9ff557f 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsDefault.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsDefault.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents the default option in a JavaScript swtich statement.
  */
 public final class JsDefault extends JsSwitchMember {
 
-  public JsDefault() {
+  public JsDefault(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public void traverse(JsVisitor v, JsContext<JsSwitchMember> ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsDoWhile.java b/dev/core/src/com/google/gwt/dev/js/ast/JsDoWhile.java
index cd6d934..4ce7128 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsDoWhile.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsDoWhile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript do..while statement.
  */
@@ -24,10 +26,13 @@
 
   private JsExpression condition;
 
-  public JsDoWhile() {
+  public JsDoWhile(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsDoWhile(JsExpression condition, JsStatement body) {
+  public JsDoWhile(SourceInfo sourceInfo, JsExpression condition,
+      JsStatement body) {
+    super(sourceInfo);
     this.condition = condition;
     this.body = body;
   }
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 00379fc..097e31e 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,13 +15,16 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents an empty statement in JavaScript.
  */
 public class JsEmpty extends JsStatement {
 
   // Interned by JsProgram
-  JsEmpty() {
+  JsEmpty(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public void traverse(JsVisitor v, JsContext<JsStatement> ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsExprStmt.java b/dev/core/src/com/google/gwt/dev/js/ast/JsExprStmt.java
index ec5c33d..b4ddcf0 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsExprStmt.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsExprStmt.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript expression statement.
  */
@@ -22,7 +24,8 @@
 
   private JsExpression expr;
 
-  public JsExprStmt(JsExpression expr) {
+  public JsExprStmt(SourceInfo sourceInfo, JsExpression expr) {
+    super(sourceInfo);
     this.expr = expr;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsExpression.java b/dev/core/src/com/google/gwt/dev/js/ast/JsExpression.java
index 3f5b26d..13cf5c4 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsExpression.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsExpression.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,10 +15,16 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * An abstract base class for all JavaScript expressions.
  */
 public abstract class JsExpression extends JsNode<JsExpression> {
+  
+  protected JsExpression(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
 
   /**
    * Determines whether the expression can cause side effects.
@@ -47,6 +53,6 @@
   }
 
   public JsExprStmt makeStmt() {
-    return new JsExprStmt(this);
+    return new JsExprStmt(getSourceInfo(), this);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsFor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsFor.java
index 85fe8c1..c2f002b 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsFor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsFor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A <code>for</code> statement. If specified at all, the initializer part is
  * either a declaration of one or more variables, in which case
@@ -38,7 +40,8 @@
 
   private JsVars initVars;
 
-  public JsFor() {
+  public JsFor(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public JsStatement getBody() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsForIn.java b/dev/core/src/com/google/gwt/dev/js/ast/JsForIn.java
index feab502..4358782 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsForIn.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsForIn.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript for..in statement.
  */
@@ -29,11 +31,12 @@
 
   private JsExpression objExpr;
 
-  public JsForIn() {
-    this(null);
+  public JsForIn(SourceInfo sourceInfo) {
+    this(sourceInfo, null);
   }
 
-  public JsForIn(JsName iterVarName) {
+  public JsForIn(SourceInfo sourceInfo, JsName iterVarName) {
+    super(sourceInfo);
     this.iterVarName = iterVarName;
   }
 
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 047ece4..3ae9c2f 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
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -43,21 +45,23 @@
   /**
    * Creates an anonymous function.
    */
-  public JsFunction(JsScope parent) {
-    this(parent, null, false);
+  public JsFunction(SourceInfo sourceInfo, JsScope parent) {
+    this(sourceInfo, parent, null, false);
   }
 
   /**
    * Creates a function that is not derived from Java source.
    */
-  public JsFunction(JsScope parent, JsName name) {
-    this(parent, name, false);
+  public JsFunction(SourceInfo sourceInfo, JsScope parent, JsName name) {
+    this(sourceInfo, parent, name, false);
   }
 
   /**
    * Creates a named function, possibly derived from Java source.
    */
-  public JsFunction(JsScope parent, JsName name, boolean fromJava) {
+  public JsFunction(SourceInfo sourceInfo, JsScope parent, JsName name,
+      boolean fromJava) {
+    super(sourceInfo);
     assert (parent != null);
     this.fromJava = fromJava;
     setName(name);
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsGlobalBlock.java b/dev/core/src/com/google/gwt/dev/js/ast/JsGlobalBlock.java
index 883674d..f6dfb25 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsGlobalBlock.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsGlobalBlock.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,14 +15,20 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represnts a JavaScript block in the global scope.
  */
 public class JsGlobalBlock extends JsBlock {
 
+  public JsGlobalBlock(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
+
   @Override
   public boolean isGlobalBlock() {
     return true;
   }
-  
+
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsIf.java b/dev/core/src/com/google/gwt/dev/js/ast/JsIf.java
index fccefae..2440155 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsIf.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsIf.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript if statement.
  */
@@ -26,10 +28,13 @@
 
   private JsStatement elseStmt;
 
-  public JsIf() {
+  public JsIf(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsIf(JsExpression ifExpr, JsStatement thenStmt, JsStatement elseStmt) {
+  public JsIf(SourceInfo sourceInfo, JsExpression ifExpr, JsStatement thenStmt,
+      JsStatement elseStmt) {
+    super(sourceInfo);
     this.ifExpr = ifExpr;
     this.thenStmt = thenStmt;
     this.elseStmt = elseStmt;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsInvocation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsInvocation.java
index 2e1838b..ac7fdac 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsInvocation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsInvocation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -27,7 +29,8 @@
 
   private JsExpression qualifier;
 
-  public JsInvocation() {
+  public JsInvocation(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsExpression> getArguments() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsLabel.java b/dev/core/src/com/google/gwt/dev/js/ast/JsLabel.java
index 74af6de..c733b6e 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsLabel.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsLabel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript label statement.
  */
@@ -24,7 +26,8 @@
 
   private JsStatement stmt;
 
-  public JsLabel(JsName label) {
+  public JsLabel(SourceInfo sourceInfo, JsName label) {
+    super(sourceInfo);
     this.label = label;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsLiteral.java
index 6deb149..6fc345d 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsLiteral.java
@@ -15,8 +15,13 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript string literal expression.
  */
 public abstract class JsLiteral extends JsExpression implements CanBooleanEval {
+  protected JsLiteral(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsName.java b/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
index 8321727..0b605e9 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.io.Serializable;
 
 /**
@@ -62,8 +64,8 @@
     return isObfuscatable;
   }
 
-  public JsNameRef makeRef() {
-    return new JsNameRef(this);
+  public JsNameRef makeRef(SourceInfo sourceInfo) {
+    return new JsNameRef(sourceInfo, this);
   }
 
   public void setObfuscatable(boolean isObfuscatable) {
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 93d9159..42466fc 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript expression that references a name.
  */
@@ -25,11 +27,13 @@
   private JsName name;
   private JsExpression qualifier;
 
-  public JsNameRef(JsName name) {
+  public JsNameRef(SourceInfo sourceInfo, JsName name) {
+    super(sourceInfo);
     this.name = name;
   }
 
-  public JsNameRef(String ident) {
+  public JsNameRef(SourceInfo sourceInfo, String ident) {
+    super(sourceInfo);
     this.ident = ident;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNew.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNew.java
index 577ceb8..19f8a51 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNew.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNew.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -27,7 +29,8 @@
 
   private JsExpression ctorExpr;
 
-  public JsNew() {
+  public JsNew(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsExpression> getArguments() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java
index 6ca9618..f370ee4 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java
@@ -30,10 +30,16 @@
  */
 public abstract class JsNode<T extends JsVisitable<T>> implements
     JsVisitable<T>, HasSourceInfo, Serializable {
+  
+  private final SourceInfo sourceInfo;
+  
+  protected JsNode(SourceInfo sourceInfo) {
+    assert sourceInfo != null : "SourceInfo must be provided for JsNodes";
+    this.sourceInfo = sourceInfo;
+  }
 
   public SourceInfo getSourceInfo() {
-    // TODO: make this real
-    return null;
+    return sourceInfo;
   }
 
   // Causes source generation to delegate to the one visitor
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 5a63acc..f960539 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
@@ -15,13 +15,16 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript null literal.
  */
 public final class JsNullLiteral extends JsValueLiteral {
 
   // Should only be instantiated in JsProgram
-  JsNullLiteral() {
+  JsNullLiteral(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public boolean isBooleanFalse() {
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 3de6de8..cef02c6 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
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Represents a JavaScript literal decimal expression.
  */
@@ -23,7 +25,8 @@
   private final double value;
 
   // Should be interned by JsProgram
-  JsNumberLiteral(double value) {
+  JsNumberLiteral(SourceInfo sourceInfo, double value) {
+    super(sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java
index 393fa17..a81377a 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,7 +27,8 @@
 
   private final List<JsPropertyInitializer> props = new ArrayList<JsPropertyInitializer>();
 
-  public JsObjectLiteral() {
+  public JsObjectLiteral(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsPropertyInitializer> getPropertyInitializers() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsParameter.java b/dev/core/src/com/google/gwt/dev/js/ast/JsParameter.java
index af2e667..b934353 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsParameter.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsParameter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript parameter.
  */
@@ -22,7 +24,8 @@
 
   private final JsName name;
 
-  public JsParameter(JsName name) {
+  public JsParameter(SourceInfo sourceInfo, JsName name) {
+    super(sourceInfo);
     this.name = name;
     name.setStaticRef(this);
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsPostfixOperation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsPostfixOperation.java
index ded8be4..b4ec4bf 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsPostfixOperation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsPostfixOperation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,17 +15,20 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript postfix operation.
  */
 public final class JsPostfixOperation extends JsUnaryOperation {
 
-  public JsPostfixOperation(JsUnaryOperator op) {
-    this(op, null);
+  public JsPostfixOperation(SourceInfo sourceInfo, JsUnaryOperator op) {
+    this(sourceInfo, op, null);
   }
 
-  public JsPostfixOperation(JsUnaryOperator op, JsExpression arg) {
-    super(op, arg);
+  public JsPostfixOperation(SourceInfo sourceInfo, JsUnaryOperator op,
+      JsExpression arg) {
+    super(sourceInfo, op, arg);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsPrefixOperation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsPrefixOperation.java
index bf5956a..ff41d04 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsPrefixOperation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsPrefixOperation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,18 +15,21 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript prefix operation.
  */
 public final class JsPrefixOperation extends JsUnaryOperation implements
     CanBooleanEval {
 
-  public JsPrefixOperation(JsUnaryOperator op) {
-    this(op, null);
+  public JsPrefixOperation(SourceInfo sourceInfo, JsUnaryOperator op) {
+    this(sourceInfo, op, null);
   }
 
-  public JsPrefixOperation(JsUnaryOperator op, JsExpression arg) {
-    super(op, arg);
+  public JsPrefixOperation(SourceInfo sourceInfo, JsUnaryOperator op,
+      JsExpression arg) {
+    super(sourceInfo, op, arg);
   }
 
   public boolean isBooleanFalse() {
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 7abc651..1677b1b 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -23,15 +25,19 @@
  */
 public final class JsProgram extends JsNode<JsProgram> {
 
-  private final JsStatement debuggerStmt = new JsDebugger();
+  private final JsStatement debuggerStmt = new JsDebugger(
+      createSourceInfoSynthetic("debugger statement"));
 
-  private final JsEmpty emptyStmt = new JsEmpty();
+  private final JsEmpty emptyStmt = new JsEmpty(
+      createSourceInfoSynthetic("Empty statement"));
 
-  private final JsBooleanLiteral falseLiteral = new JsBooleanLiteral(false);
+  private final JsBooleanLiteral falseLiteral = new JsBooleanLiteral(
+      createSourceInfoSynthetic("false literal"), false);
 
   private final JsGlobalBlock globalBlock;
 
-  private final JsNullLiteral nullLiteral = new JsNullLiteral();
+  private final JsNullLiteral nullLiteral = new JsNullLiteral(
+      createSourceInfoSynthetic("null literal"));
 
   private final Map<Double, JsNumberLiteral> numberLiteralMap = new HashMap<Double, JsNumberLiteral>();
 
@@ -43,18 +49,31 @@
 
   private final JsScope topScope;
 
-  private final JsBooleanLiteral trueLiteral = new JsBooleanLiteral(true);
+  private final JsBooleanLiteral trueLiteral = new JsBooleanLiteral(
+      createSourceInfoSynthetic("true literal"), true);
+
+  private boolean enableSourceInfoDescendants;
 
   /**
    * Constructs a JavaScript program object.
    */
   public JsProgram() {
+    super(SourceInfoJs.INTRINSIC.makeChild("JavaScript program"));
     rootScope = new JsRootScope(this);
-    globalBlock = new JsGlobalBlock();
+    globalBlock = new JsGlobalBlock(createSourceInfoSynthetic("global block"));
     topScope = new JsScope(rootScope, "Global");
     objectScope = new JsScope(rootScope, "Object");
   }
 
+  public SourceInfo createSourceInfo(int lineNumber, String location) {
+    return new SourceInfoJs(-1, -1, lineNumber, location,
+        enableSourceInfoDescendants);
+  }
+
+  public SourceInfo createSourceInfoSynthetic(String description) {
+    return createSourceInfo(0, SourceInfoJs.findCaller()).makeChild(description);
+  }
+
   public JsBooleanLiteral getBooleanLiteral(boolean truth) {
     if (truth) {
       return getTrueLiteral();
@@ -94,7 +113,8 @@
   public JsNumberLiteral getNumberLiteral(double value) {
     JsNumberLiteral lit = numberLiteralMap.get(value);
     if (lit == null) {
-      lit = new JsNumberLiteral(value);
+      lit = new JsNumberLiteral(createSourceInfoSynthetic("Number literal "
+          + value), value);
       numberLiteralMap.put(value, lit);
     }
     return lit;
@@ -121,11 +141,13 @@
     return topScope;
   }
 
-  public JsStringLiteral getStringLiteral(String value) {
+  public JsStringLiteral getStringLiteral(SourceInfo sourceInfo, String value) {
     JsStringLiteral lit = stringLiteralMap.get(value);
     if (lit == null) {
-      lit = new JsStringLiteral(value);
+      lit = new JsStringLiteral(sourceInfo, value);
       stringLiteralMap.put(value, lit);
+    } else {
+      lit.getSourceInfo().addAdditonalAncestors(sourceInfo);
     }
     return lit;
   }
@@ -135,7 +157,18 @@
   }
 
   public JsNameRef getUndefinedLiteral() {
-    return rootScope.findExistingName("undefined").makeRef();
+    return rootScope.findExistingName("undefined").makeRef(
+        createSourceInfoSynthetic("undefined reference"));
+  }
+
+  /**
+   * Controls whether or not SourceInfo nodes created via the JsProgram will
+   * record descendant information. Enabling this feature will collect extra
+   * data during the compilation cycle, but at a cost of memory and object
+   * allocations.
+   */
+  public void setEnableSourceInfoDescendants(boolean enable) {
+    enableSourceInfoDescendants = enable;
   }
 
   public void traverse(JsVisitor v, JsContext<JsProgram> ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsPropertyInitializer.java b/dev/core/src/com/google/gwt/dev/js/ast/JsPropertyInitializer.java
index 3b429b2..de18cca 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsPropertyInitializer.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsPropertyInitializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Used in object literals to specify property values by name.
  */
@@ -24,10 +26,12 @@
 
   private JsExpression valueExpr;
 
-  public JsPropertyInitializer() {
+  public JsPropertyInitializer(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsPropertyInitializer(JsExpression labelExpr, JsExpression valueExpr) {
+  public JsPropertyInitializer(SourceInfo sourceInfo, JsExpression labelExpr, JsExpression valueExpr) {
+    super(sourceInfo);
     this.labelExpr = labelExpr;
     this.valueExpr = valueExpr;
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRegExp.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRegExp.java
index 62aec87..c367597 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsRegExp.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRegExp.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript regular expression.
  */
@@ -24,7 +26,8 @@
 
   private String pattern;
 
-  public JsRegExp() {
+  public JsRegExp(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public String getFlags() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsReturn.java b/dev/core/src/com/google/gwt/dev/js/ast/JsReturn.java
index e9272b4..0176340 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsReturn.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsReturn.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript return statement.
  */
@@ -22,10 +24,12 @@
 
   private JsExpression expr;
 
-  public JsReturn() {
+  public JsReturn(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsReturn(JsExpression expr) {
+  public JsReturn(SourceInfo sourceInfo, JsExpression expr) {
+    super(sourceInfo);
     this.expr = expr;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsStatement.java b/dev/core/src/com/google/gwt/dev/js/ast/JsStatement.java
index e1db178..3043cc1 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsStatement.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsStatement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,11 +15,17 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * Abstract base class for JavaScript statement objects.
  */
 public abstract class JsStatement extends JsNode<JsStatement> {
 
+  protected JsStatement(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
+
   /**
    * Returns true if this statement definitely causes an abrupt change in flow
    * control.
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 799195f..c553f2c 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
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript string literal expression.
  */
@@ -23,7 +25,8 @@
   private final String value;
 
   // These only get created by JsProgram so that they can be interned.
-  JsStringLiteral(String value) {
+  JsStringLiteral(SourceInfo sourceInfo, String value) {
+    super(sourceInfo);
     this.value = value;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsSwitch.java b/dev/core/src/com/google/gwt/dev/js/ast/JsSwitch.java
index 8d9c521..6e4d038 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsSwitch.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSwitch.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -27,7 +29,8 @@
 
   private JsExpression expr;
 
-  public JsSwitch() {
+  public JsSwitch(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsSwitchMember> getCases() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsSwitchMember.java b/dev/core/src/com/google/gwt/dev/js/ast/JsSwitchMember.java
index 1080890..26bee44 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsSwitchMember.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSwitchMember.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,6 +27,10 @@
 
   protected final List<JsStatement> stmts = new ArrayList<JsStatement>();
 
+  protected JsSwitchMember(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
+
   public List<JsStatement> getStmts() {
     return stmts;
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsThisRef.java b/dev/core/src/com/google/gwt/dev/js/ast/JsThisRef.java
index ee5c739..c67af2e 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsThisRef.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsThisRef.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript <code>this</code> reference.
  */
 public final class JsThisRef extends JsValueLiteral {
 
-  public JsThisRef() {
+  public JsThisRef(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public boolean isBooleanFalse() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsThrow.java b/dev/core/src/com/google/gwt/dev/js/ast/JsThrow.java
index 010ec10..2f25bd2 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsThrow.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsThrow.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript <code>throw</code> statement.
  */
@@ -22,10 +24,12 @@
 
   private JsExpression expr;
 
-  public JsThrow() {
+  public JsThrow(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsThrow(JsExpression expr) {
+  public JsThrow(SourceInfo sourceInfo, JsExpression expr) {
+    super(sourceInfo);
     this.expr = expr;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsTry.java b/dev/core/src/com/google/gwt/dev/js/ast/JsTry.java
index 737fff0..ae7ec34 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsTry.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsTry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,7 +31,8 @@
 
   private JsBlock tryBlock;
 
-  public JsTry() {
+  public JsTry(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public List<JsCatch> getCatches() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java
index 6358f15..4feadb6 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript prefix or postfix operation.
  */
@@ -24,11 +26,13 @@
 
   private final JsUnaryOperator op;
 
-  public JsUnaryOperation(JsUnaryOperator op) {
-    this(op, null);
+  public JsUnaryOperation(SourceInfo sourceInfo, JsUnaryOperator op) {
+    this(sourceInfo, op, null);
   }
 
-  public JsUnaryOperation(JsUnaryOperator op, JsExpression arg) {
+  public JsUnaryOperation(SourceInfo sourceInfo, JsUnaryOperator op,
+      JsExpression arg) {
+    super(sourceInfo);
     this.op = op;
     this.arg = arg;
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsValueLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsValueLiteral.java
index f3e4af4..ba6dcd3 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsValueLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsValueLiteral.java
@@ -15,11 +15,17 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript string literal expression.
  */
 public abstract class JsValueLiteral extends JsLiteral {
 
+  protected JsValueLiteral(SourceInfo sourceInfo) {
+    super(sourceInfo);
+  }
+
   @Override
   public final boolean hasSideEffects() {
     return false;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
index c23e5ff..86e1e41 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -33,7 +35,8 @@
 
     private final JsName name;
 
-    public JsVar(JsName name) {
+    public JsVar(SourceInfo sourceInfo, JsName name) {
+      super(sourceInfo);
       this.name = name;
     }
 
@@ -61,7 +64,8 @@
 
   private final List<JsVar> vars = new ArrayList<JsVar>();
 
-  public JsVars() {
+  public JsVars(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
   public void add(JsVar var) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsWhile.java b/dev/core/src/com/google/gwt/dev/js/ast/JsWhile.java
index 799e594..5bed233 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsWhile.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsWhile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.js.ast;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+
 /**
  * A JavaScript <code>while</code> statement.
  */
@@ -24,10 +26,12 @@
 
   private JsExpression condition;
 
-  public JsWhile() {
+  public JsWhile(SourceInfo sourceInfo) {
+    super(sourceInfo);
   }
 
-  public JsWhile(JsExpression condition, JsStatement body) {
+  public JsWhile(SourceInfo sourceInfo, JsExpression condition, JsStatement body) {
+    super(sourceInfo);
     this.condition = condition;
     this.body = body;
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/SourceInfoJs.java b/dev/core/src/com/google/gwt/dev/js/ast/SourceInfoJs.java
new file mode 100644
index 0000000..a465edb
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/SourceInfoJs.java
@@ -0,0 +1,41 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.js.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * An implementation of SourceInfo representing SourceInfo nodes derived from
+ * the JavaScript AST. Instances of this class should only be constructed by
+ * JsProgram.
+ */
+public class SourceInfoJs extends SourceInfo {
+  /**
+   * Indicates that an AST element is an intrinsic element of the AST and has no
+   * meaningful source location. This is typically used by singleton AST
+   * elements or for literal values.
+   */
+  public static final SourceInfo INTRINSIC = new SourceInfoJs(0, 0, 0,
+      "Js intrinsics", true);
+
+  /**
+   * Called only from JsProgram.
+   */
+  SourceInfoJs(int startPos, int endPos, int startLine, String fileName,
+      boolean createDescendants) {
+    super(startPos, endPos, startLine, fileName, createDescendants);
+  }
+}