Submitting changes/scottb/compiler_refactor into the trunk.

MAJOR compiler refactor:
- Java AST visitation/traversal was refactored
- Java AST changes are applied using the new JModVistor
- Removed the whole idea of change lists
- Java AST now contains file/line source info from JDT

New DeadCodeElimination visitor base on patch by sandymac:
- Optimizes if (booleanConstant); while (false); for (; false; ); do while (false)
- Optimizes !booleanConstant
- Short-circuit eval of binary operators && and ||
- Removes unnecessary try blocks
- Removes empty blocks

TODO:
- Revisit design of JVisitor/JModVisitor
- Implement InternalCompilerException.addNode() and log much better info when an ICE occurs

Patch by: sandymac (just DeadCodeElimination.java; adapted by me)
Reviewed by: mmendez



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@340 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 e0503cb..262ca4d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -36,6 +36,7 @@
 import com.google.gwt.dev.jjs.impl.CastOptimizer;
 import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
 import com.google.gwt.dev.jjs.impl.CompoundAssignmentNormalizer;
+import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
 import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
 import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
 import com.google.gwt.dev.jjs.impl.JavaScriptObjectCaster;
@@ -75,8 +76,8 @@
   private static void findEntryPoints(TreeLogger logger,
       String[] mainClassNames, JProgram program)
       throws UnableToCompleteException {
-    JMethod bootStrapMethod = program.createMethod("init".toCharArray(), null,
-        program.getTypeVoid(), false, true, true, false, false);
+    JMethod bootStrapMethod = program.createMethod(null, "init".toCharArray(),
+        null, program.getTypeVoid(), false, true, true, false, false);
     bootStrapMethod.freezeParamTypes();
 
     for (int i = 0; i < mainClassNames.length; ++i) {
@@ -153,14 +154,14 @@
         }
 
         // Construct a new instance of the class to qualify the non-static call
-        JNewInstance newInstance = new JNewInstance(program, mainClass);
-        qualifier = new JMethodCall(program, newInstance, noArgCtor);
+        JNewInstance newInstance = new JNewInstance(program, null, mainClass);
+        qualifier = new JMethodCall(program, null, newInstance, noArgCtor);
       }
 
-      JMethodCall onModuleLoadCall = new JMethodCall(program, qualifier,
+      JMethodCall onModuleLoadCall = new JMethodCall(program, null, qualifier,
           mainMethod);
       bootStrapMethod.body.statements.add(new JExpressionStatement(program,
-          onModuleLoadCall));
+          null, onModuleLoadCall));
     }
     program.addEntryMethod(bootStrapMethod);
   }
@@ -339,6 +340,7 @@
         didChange = CastOptimizer.exec(jprogram) || didChange;
 
         // dead code removal??
+        didChange = DeadCodeElimination.exec(jprogram) || didChange;
 
         // inlining
         didChange = MethodInliner.exec(jprogram) || didChange;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/Mutator.java b/dev/core/src/com/google/gwt/dev/jjs/ast/Context.java
similarity index 64%
rename from dev/core/src/com/google/gwt/dev/jjs/ast/Mutator.java
rename to dev/core/src/com/google/gwt/dev/jjs/ast/Context.java
index ced4091..0062716 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/Mutator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/Context.java
@@ -16,18 +16,21 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Abstracts the process of modifying an AST node.
+ * The context in which a JNode visitation occurs. This represents the set of
+ * possible operations a JVisitor subclass can perform on the currently visited
+ * node.
  */
-public abstract class Mutator {
+public interface Context {
 
-  public abstract JExpression get();
+  boolean canInsert();
 
-  public abstract void insertAfter(JExpression node);
+  boolean canRemove();
 
-  public abstract void insertBefore(JExpression node);
+  void insertAfter(JNode node);
 
-  public abstract void remove();
+  void insertBefore(JNode node);
 
-  public abstract JExpression set(JExpression value);
+  void removeMe();
 
+  void replaceMe(JNode node);
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/Holder.java b/dev/core/src/com/google/gwt/dev/jjs/ast/Holder.java
deleted file mode 100644
index cb7dc51..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/Holder.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast;
-
-/**
- * A mutable reference to an AST node.
- */
-public class Holder extends Mutator implements JVisitable {
-
-  private JExpression value;
-
-  public Holder() {
-  }
-
-  public Holder(JExpression value) {
-    this.value = value;
-  }
-
-  public JExpression get() {
-    return value;
-  }
-
-  public void insertAfter(JExpression node) {
-    throw new UnsupportedOperationException();
-  }
-
-  public void insertBefore(JExpression node) {
-    throw new UnsupportedOperationException();
-  }
-
-  public void remove() {
-    throw new UnsupportedOperationException();
-  }
-
-  public JExpression set(JExpression value) {
-    return this.value = value;
-  }
-
-  public void traverse(JVisitor visitor) {
-    if (value != null) {
-      value.traverse(visitor, this);
-    }
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HolderList.java b/dev/core/src/com/google/gwt/dev/jjs/ast/HolderList.java
deleted file mode 100644
index 7893e8d..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HolderList.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * A list of {@link Holder} objects.
- */
-public class HolderList/* <T extends JNode> */implements List/* <T> */,
-    JVisitable {
-
-  private final class ListIt implements ListIterator/* <T> */ {
-
-    private final ListIterator/* <Mutator<T>> */itImpl;
-    private final ListIterator/* <T> */itPeer;
-
-    private ListIt(ListIterator/* <Mutator<T>> */itImpl,
-        ListIterator/* <T> */itPeer) {
-      this.itImpl = itImpl;
-      this.itPeer = itPeer;
-    }
-
-    public void add(Object o) {
-      itImpl.add(new MutatorImpl());
-      itPeer.add(o);
-    }
-
-    public boolean hasNext() {
-      return itPeer.hasNext();
-    }
-
-    public boolean hasPrevious() {
-      return itPeer.hasPrevious();
-    }
-
-    public Object next() {
-      itImpl.next();
-      return itPeer.next();
-    }
-
-    public int nextIndex() {
-      return itPeer.nextIndex();
-    }
-
-    public Object previous() {
-      itImpl.previous();
-      return itPeer.previous();
-    }
-
-    public int previousIndex() {
-      return itPeer.previousIndex();
-    }
-
-    public void remove() {
-      itImpl.remove();
-      itPeer.remove();
-    }
-
-    public void set(Object o) {
-      itPeer.set(o);
-    }
-  }
-
-  private class MutatorImpl extends Mutator {
-
-    public JExpression get() {
-      int pos = impl.indexOf(this);
-      if (pos < 0) {
-        throw new IndexOutOfBoundsException();
-      }
-      return (JExpression) HolderList.this.get(pos);
-    }
-
-    public void insertAfter(JExpression node) {
-      int pos = impl.indexOf(this);
-      if (pos < 0) {
-        throw new IndexOutOfBoundsException();
-      }
-      HolderList.this.add(pos, node);
-    }
-
-    public void insertBefore(JExpression node) {
-      int pos = impl.indexOf(this);
-      if (pos < 0) {
-        throw new IndexOutOfBoundsException();
-      }
-      HolderList.this.add(pos + 1, node);
-    }
-
-    public void remove() {
-      int pos = impl.indexOf(this);
-      if (pos < 0) {
-        throw new IndexOutOfBoundsException();
-      }
-      HolderList.this.remove(pos);
-    }
-
-    public JExpression set(JExpression value) {
-      int pos = impl.indexOf(this);
-      if (pos < 0) {
-        throw new IndexOutOfBoundsException();
-      }
-      return (JExpression) HolderList.this.set(pos, value);
-    }
-  }
-
-  private final ArrayList/* <Mutator<T>> */impl = new ArrayList/* <Mutator<T>> */();
-  private final ArrayList/* <T> */peer = new ArrayList/* <T> */();
-
-  public void add(int index, Object element) {
-    impl.add(index, new MutatorImpl());
-    peer.add(index, element);
-  }
-
-  public boolean add(Object o) {
-    impl.add(new MutatorImpl());
-    return peer.add(o);
-  }
-
-  public boolean addAll(Collection/* <? extends T> */c) {
-    boolean result = false;
-    for (Iterator it = c.iterator(); it.hasNext();) {
-      JNode item = (JNode) it.next();
-      this.add(item);
-      result = true;
-    }
-    return result;
-  }
-
-  public boolean addAll(int index, Collection/* <? extends T> */c) {
-    boolean result = false;
-    for (Iterator it = c.iterator(); it.hasNext();) {
-      JNode item = (JNode) it.next();
-      this.add(index++, item);
-      result = true;
-    }
-    return result;
-  }
-
-  public void clear() {
-    peer.clear();
-    impl.clear();
-  }
-
-  public boolean contains(Object o) {
-    return peer.contains(o);
-  }
-
-  public boolean containsAll(Collection/* <?> */c) {
-    throw new UnsupportedOperationException();
-  }
-
-  public Object get(int index) {
-    return peer.get(index);
-  }
-
-  public JExpression getExpr(int index) {
-    return (JExpression) peer.get(index);
-  }
-
-  public Mutator getMutator(int index) {
-    return (Mutator) impl.get(index);
-  }
-
-  public List/* <Mutator<T>> */getMutators() {
-    return impl;
-  }
-
-  public int indexOf(Object o) {
-    return peer.indexOf(o);
-  }
-
-  public boolean isEmpty() {
-    return peer.isEmpty();
-  }
-
-  public Iterator/* <T> */iterator() {
-    return new ListIt(impl.listIterator(), peer.listIterator());
-  }
-
-  public int lastIndexOf(Object o) {
-    return peer.lastIndexOf(o);
-  }
-
-  public ListIterator/* <T> */listIterator() {
-    return new ListIt(impl.listIterator(), peer.listIterator());
-  }
-
-  public ListIterator/* <T> */listIterator(int index) {
-    return new ListIt(impl.listIterator(index), peer.listIterator(index));
-  }
-
-  public Object remove(int index) {
-    impl.remove(index);
-    return peer.remove(index);
-  }
-
-  public boolean remove(Object o) {
-    int i = peer.indexOf(o);
-    if (i < 0) {
-      return false;
-    }
-    impl.remove(i);
-    peer.remove(i);
-    return true;
-  }
-
-  public boolean removeAll(Collection/* <?> */c) {
-    throw new UnsupportedOperationException();
-  }
-
-  public boolean retainAll(Collection/* <?> */c) {
-    throw new UnsupportedOperationException();
-  }
-
-  public Object set(int index, Object element) {
-    return peer.set(index, element);
-  }
-
-  public int size() {
-    return peer.size();
-  }
-
-  public List/* <T> */subList(int fromIndex, int toIndex) {
-    throw new UnsupportedOperationException();
-  }
-
-  public Object[] toArray() {
-    return peer.toArray();
-  }
-
-  public/* <T> */Object[] toArray(Object[] a) {
-    return peer.toArray(a);
-  }
-
-  public void traverse(JVisitor visitor) {
-    for (int i = 0, c = impl.size(); i < c; ++i) {
-      JExpression value = (JExpression) peer.get(i);
-      value.traverse(visitor, (Mutator) impl.get(i));
-    }
-  }
-
-}
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 009116a..0c402fa 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
@@ -32,14 +32,10 @@
     return program.getTypeVoid();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
index bae50f1..e3cc019 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
@@ -20,25 +20,26 @@
  */
 public class JArrayRef extends JExpression {
 
-  public final Holder instance = new Holder();
-  public final Holder indexExpr = new Holder();
+  private JExpression instance;
+  private JExpression indexExpr;
 
-  public JArrayRef(JProgram program, JExpression instance, JExpression indexExpr) {
-    super(program);
-    this.instance.set(instance);
-    this.indexExpr.set(indexExpr);
+  public JArrayRef(JProgram program, JSourceInfo info, JExpression instance,
+      JExpression indexExpr) {
+    super(program, info);
+    this.instance = instance;
+    this.indexExpr = indexExpr;
   }
 
   public JExpression getIndexExpr() {
-    return indexExpr.get();
+    return indexExpr;
   }
 
   public JExpression getInstance() {
-    return instance.get();
+    return instance;
   }
 
   public JType getType() {
-    JType type = instance.get().getType();
+    JType type = instance.getType();
     if (type == program.getTypeNull()) {
       return type;
     }
@@ -47,19 +48,15 @@
   }
 
   public boolean hasSideEffects() {
-    return instance.get().hasSideEffects() || indexExpr.get().hasSideEffects();
+    return instance.hasSideEffects() || indexExpr.hasSideEffects();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      this.instance.traverse(visitor);
-      this.indexExpr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      instance = visitor.accept(instance);
+      indexExpr = visitor.accept(indexExpr);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 4c4e244..035a5d0 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
@@ -28,15 +28,14 @@
     return name;
   }
 
-  public JType leafType;
-
-  public int dims;
+  private int dims;
+  private JType leafType;
 
   /**
    * These are only supposed to be constructed by JProgram.
    */
   JArrayType(JProgram program, JType leafType, int dims) {
-    super(program, calcName(leafType, dims), false, false);
+    super(program, null, calcName(leafType, dims), false, false);
     this.leafType = leafType;
     this.dims = dims;
   }
@@ -80,10 +79,10 @@
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JAssertStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JAssertStatement.java
index c8448ae..d474694 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JAssertStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JAssertStatement.java
@@ -20,29 +20,31 @@
  */
 public class JAssertStatement extends JStatement {
 
-  private final Holder testExpr = new Holder();
-  private final Holder arg = new Holder();
+  private JExpression testExpr;
+  private JExpression arg;
 
-  public JAssertStatement(JProgram program, JExpression testExpr, JExpression arg) {
-    super(program);
-    this.testExpr.set(testExpr);
-    this.arg.set(arg);
+  public JAssertStatement(JProgram program, JSourceInfo info,
+      JExpression testExpr, JExpression arg) {
+    super(program, info);
+    this.testExpr = testExpr;
+    this.arg = arg;
   }
 
   public JExpression getArg() {
-    return arg.get();
+    return arg;
   }
 
   public JExpression getTestExpr() {
-    return testExpr.get();
+    return testExpr;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      testExpr.traverse(visitor);
-      arg.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      testExpr = visitor.accept(testExpr);
+      if (arg != null) {
+        arg = visitor.accept(arg);
+      }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
index 3beeb72..5908bf3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
@@ -20,26 +20,30 @@
  */
 public class JBinaryOperation extends JExpression implements HasSettableType {
 
-  public JBinaryOperator op;
-  public final Holder lhs = new Holder();
-  public final Holder rhs = new Holder();
+  private JExpression lhs;
+  private final JBinaryOperator op;
+  private JExpression rhs;
   private JType type;
 
-  public JBinaryOperation(JProgram program, JType type, JBinaryOperator op,
-      JExpression lhs, JExpression rhs) {
-    super(program);
+  public JBinaryOperation(JProgram program, JSourceInfo info, JType type,
+      JBinaryOperator op, JExpression lhs, JExpression rhs) {
+    super(program, info);
     this.op = op;
     this.type = type;
-    this.lhs.set(lhs);
-    this.rhs.set(rhs);
+    this.lhs = lhs;
+    this.rhs = rhs;
   }
 
   public JExpression getLhs() {
-    return lhs.get();
+    return lhs;
+  }
+
+  public JBinaryOperator getOp() {
+    return op;
   }
 
   public JExpression getRhs() {
-    return rhs.get();
+    return rhs;
   }
 
   public JType getType() {
@@ -68,16 +72,12 @@
     type = newType;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      lhs.traverse(visitor);
-      rhs.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      lhs = visitor.accept(lhs);
+      rhs = visitor.accept(rhs);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBlock.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBlock.java
index 21de0a8..34def70 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBlock.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBlock.java
@@ -23,20 +23,17 @@
  */
 public class JBlock extends JStatement {
 
-  public List/*<JStatement>*/ statements = new ArrayList/*<JStatement>*/();
+  public List/* <JStatement> */statements = new ArrayList/* <JStatement> */();
 
-  public JBlock(JProgram program) {
-    super(program);
+  public JBlock(JProgram program, JSourceInfo info) {
+    super(program, info);
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < statements.size(); ++i) {
-        JStatement stmt = (JStatement) statements.get(i);
-        stmt.traverse(visitor);
-      }
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.acceptWithInsertRemove(statements);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 c710210..6232752 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
@@ -20,7 +20,7 @@
  */
 public class JBooleanLiteral extends JLiteral {
 
-  public final boolean value;
+  private final boolean value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,14 +34,14 @@
     return program.getTypePrimitiveBoolean();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public boolean getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBreakStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBreakStatement.java
index 0d6dc16..8bd64b5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBreakStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBreakStatement.java
@@ -20,20 +20,23 @@
  */
 public class JBreakStatement extends JStatement {
 
-  public JLabel label;
+  private final JLabel label;
 
-  public JBreakStatement(JProgram program, JLabel label) {
-    super(program);
+  public JBreakStatement(JProgram program, JSourceInfo info, JLabel label) {
+    super(program, info);
     this.label = label;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      if (label != null) {
-        label.traverse(visitor);
-      }
-    }
-    visitor.endVisit(this);
+  public JLabel getLabel() {
+    return label;
   }
 
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      if (label != null) {
+        visitor.accept(label);
+      }
+    }
+    visitor.endVisit(this, ctx);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JCaseStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JCaseStatement.java
index 4677505..82cefb0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JCaseStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JCaseStatement.java
@@ -16,26 +16,28 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java case statement. 
+ * Java case statement.
  */
 public class JCaseStatement extends JStatement {
 
-  private final Holder/*<JLiteral>*/ expr = new Holder/*<JLiteral>*/();
+  private final JLiteral expr;
 
-  public JCaseStatement(JProgram program, JLiteral expr) {
-    super(program);
-    this.expr.set(expr);
+  public JCaseStatement(JProgram program, JSourceInfo info, JLiteral expr) {
+    super(program, info);
+    this.expr = expr;
   }
 
-  public JLiteral getExpression() {
-    return (JLiteral) expr.get();
+  public JLiteral getExpr() {
+    return expr;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      expr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      if (expr != null) {
+        visitor.accept(expr);
+      }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JCastOperation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JCastOperation.java
index b5d1eb3..2adf61d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JCastOperation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JCastOperation.java
@@ -20,17 +20,22 @@
  */
 public class JCastOperation extends JExpression {
 
-  public final JType castType;
-  public final Holder expr = new Holder();
+  private JExpression expr;
+  private final JType castType;
 
-  public JCastOperation(JProgram program, JType castType, JExpression expression) {
-    super(program);
+  public JCastOperation(JProgram program, JSourceInfo info, JType castType,
+      JExpression expr) {
+    super(program, info);
     this.castType = castType;
-    this.expr.set(expression);
+    this.expr = expr;
   }
 
-  public JExpression getExpression() {
-    return expr.get();
+  public JType getCastType() {
+    return castType;
+  }
+
+  public JExpression getExpr() {
+    return expr;
   }
 
   public JType getType() {
@@ -41,18 +46,14 @@
     // technically this isn't true, but since the same cast on the same
     // expression always evaluates the same way, it effectively has no side
     // effects
-    return getExpression().hasSideEffects();
+    return getExpr().hasSideEffects();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      expr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      expr = visitor.accept(expr);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 35b40b0..6df96b8 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
@@ -20,7 +20,7 @@
  */
 public class JCharLiteral extends JLiteral {
 
-  public final char value;
+  private final char value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,14 +34,14 @@
     return program.getTypePrimitiveChar();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public char getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 7f78e2c..079aaa2 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
@@ -16,11 +16,11 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java class literal expression. 
+ * Java class literal expression.
  */
 public class JClassLiteral extends JLiteral {
 
-  public final JType refType;
+  private final JType refType;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -30,17 +30,17 @@
     refType = type;
   }
 
+  public JType getRefType() {
+    return refType;
+  }
+
   public JType getType() {
     return program.getTypeJavaLangClass();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
index b82d908..1f7776e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
@@ -16,15 +16,16 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java class type reference expression. 
+ * Java class type reference expression.
  */
 public class JClassType extends JReferenceType implements CanBeSetFinal {
 
   private final boolean isAbstract;
   private boolean isFinal;
 
-  public JClassType(JProgram program, String name, boolean isAbstract, boolean isFinal) {
-    super(program, name);
+  public JClassType(JProgram program, JSourceInfo info, String name,
+      boolean isAbstract, boolean isFinal) {
+    super(program, info, name);
     this.isAbstract = isAbstract;
     this.isFinal = isFinal;
   }
@@ -41,18 +42,12 @@
     isFinal = b;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < fields.size(); ++i) {
-        JField field = (JField) fields.get(i);
-        field.traverse(visitor);
-      }
-      for (int i = 0; i < methods.size(); ++i) {
-        JMethod method = (JMethod) methods.get(i);
-        method.traverse(visitor);
-      }
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.acceptWithInsertRemove(fields);
+      visitor.acceptWithInsertRemove(methods);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java
index fc8bc37..39b9287 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java
@@ -20,59 +20,54 @@
  */
 public class JConditional extends JExpression {
 
-  public final Holder thenExpr = new Holder();
-  public final Holder elseExpr = new Holder();
+  private JExpression elseExpr;
+  private JExpression ifTest;
+  private JExpression thenExpr;
   private final JType type;
-  private final Holder ifTest = new Holder();
 
-  public JConditional(JProgram program, JType type, JExpression ifTest,
-      JExpression thenExpr, JExpression elseExpr) {
-    super(program);
+  public JConditional(JProgram program, JSourceInfo info, JType type,
+      JExpression ifTest, JExpression thenExpr, JExpression elseExpr) {
+    super(program, info);
     this.type = type;
-    this.ifTest.set(ifTest);
-    this.thenExpr.set(thenExpr);
-    this.elseExpr.set(elseExpr);
+    this.ifTest = ifTest;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
   }
 
   public JExpression getElseExpr() {
-    return elseExpr.get();
+    return elseExpr;
   }
 
   public JExpression getIfTest() {
-    return ifTest.get();
+    return ifTest;
   }
 
   public JExpression getThenExpr() {
-    return thenExpr.get();
+    return thenExpr;
   }
 
   public JType getType() {
     // TODO(later): allow multiple types for Type Flow?
     if (type instanceof JReferenceType) {
-      return program.generalizeTypes(
-          (JReferenceType) thenExpr.get().getType(),
-          (JReferenceType) elseExpr.get().getType());
+      return program.generalizeTypes((JReferenceType) thenExpr.getType(),
+          (JReferenceType) elseExpr.getType());
     } else {
       return type;
     }
   }
 
   public boolean hasSideEffects() {
-    return ifTest.get().hasSideEffects() || thenExpr.get().hasSideEffects()
-        || elseExpr.get().hasSideEffects();
+    return ifTest.hasSideEffects() || thenExpr.hasSideEffects()
+        || elseExpr.hasSideEffects();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      ifTest.traverse(visitor);
-      thenExpr.traverse(visitor);
-      elseExpr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      ifTest = visitor.accept(ifTest);
+      thenExpr = visitor.accept(thenExpr);
+      elseExpr = visitor.accept(elseExpr);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JContinueStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JContinueStatement.java
index 395f31e..d6d93df 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JContinueStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JContinueStatement.java
@@ -20,20 +20,24 @@
  */
 public class JContinueStatement extends JStatement {
 
-  public JLabel label;
+  private final JLabel label;
 
-  public JContinueStatement(JProgram program, JLabel label) {
-    super(program);
+  public JContinueStatement(JProgram program, JSourceInfo info, JLabel label) {
+    super(program, info);
     this.label = label;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public JLabel getLabel() {
+    return label;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
       if (label != null) {
-        label.traverse(visitor);
+        visitor.accept(label);
       }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDoStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDoStatement.java
index 76282d2..8adc008 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDoStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDoStatement.java
@@ -20,27 +20,32 @@
  */
 public class JDoStatement extends JStatement {
 
-  private final Holder testExpr = new Holder();
-  public JStatement body;
+  private JStatement body;
+  private JExpression testExpr;
 
-  public JDoStatement(JProgram program, JExpression testExpr, JStatement body) {
-    super(program);
-    this.testExpr.set(testExpr);
+  public JDoStatement(JProgram program, JSourceInfo info, JExpression testExpr,
+      JStatement body) {
+    super(program, info);
+    this.testExpr = testExpr;
     this.body = body;
   }
 
-  public JExpression getTestExpr() {
-    return testExpr.get();
+  public JStatement getBody() {
+    return body;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      testExpr.traverse(visitor);
+  public JExpression getTestExpr() {
+    return testExpr;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      testExpr = visitor.accept(testExpr);
       if (body != null) {
-        body.traverse(visitor);
+        body = visitor.accept(body);
       }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 5d8f4fb..6116d6a 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
@@ -20,7 +20,7 @@
  */
 public class JDoubleLiteral extends JLiteral {
 
-  public final double value;
+  private final double value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,13 +34,13 @@
     return program.getTypePrimitiveDouble();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public double getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JExpression.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JExpression.java
index 9be3120..7f8c18d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JExpression.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JExpression.java
@@ -16,16 +16,14 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Base class for all Java expressions. 
+ * Base class for all Java expressions.
  */
 public abstract class JExpression extends JNode implements HasType {
 
-  public JExpression(JProgram program) {
-    super(program);
+  public JExpression(JProgram program, JSourceInfo info) {
+    super(program, info);
   }
 
   public abstract boolean hasSideEffects();
 
-  public abstract void traverse(JVisitor visitor, Mutator mutator);
-  
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JExpressionStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JExpressionStatement.java
index bb16984..6b921cd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JExpressionStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JExpressionStatement.java
@@ -20,22 +20,23 @@
  */
 public class JExpressionStatement extends JStatement {
 
-  private final Holder expr = new Holder();
+  private JExpression expr;
 
-  public JExpressionStatement(JProgram program, JExpression expr) {
-    super(program);
-    this.expr.set(expr);
+  public JExpressionStatement(JProgram program, JSourceInfo info,
+      JExpression expr) {
+    super(program, info);
+    this.expr = expr;
   }
 
-  public JExpression getExpression() {
-    return expr.get();
+  public JExpression getExpr() {
+    return expr;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      expr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      expr = visitor.accept(expr);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
index 3b768ee..0611e7c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
@@ -20,14 +20,14 @@
  */
 public class JField extends JVariable implements CanBeStatic, HasEnclosingType {
 
-  public JReferenceType enclosingType;
+  private JReferenceType enclosingType;
   public JLiteral constInitializer;
   private final boolean isStatic;
   private final boolean hasInitializer;
 
-  JField(JProgram program, String name, JReferenceType enclosingType,
-      JType type, boolean isStatic, boolean isFinal, boolean hasInitializer) {
-    super(program, name, type, isFinal);
+  JField(JProgram program, JSourceInfo info, String name,
+      JReferenceType enclosingType, JType type, boolean isStatic, boolean isFinal, boolean hasInitializer) {
+    super(program, info, name, type, isFinal);
     this.enclosingType = enclosingType;
     this.isStatic = isStatic;
     this.hasInitializer  = hasInitializer;
@@ -45,10 +45,10 @@
     return isStatic;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
index 6806db7..96b3e43 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
@@ -21,24 +21,25 @@
 public class JFieldRef extends JVariableRef implements HasEnclosingType {
 
   /**
-   * This can only be null if the referenced field is static.
-   */
-  public final Holder instance = new Holder();
-
-  /**
-   * The referenced field.
-   */
-  public JField field;
-
-  /**
    * The enclosing type of this reference.
    */
   private final JReferenceType enclosingType;
 
-  public JFieldRef(JProgram program, JExpression instance, JField field,
-      JReferenceType enclosingType) {
-    super(program, field);
-    this.instance.set(instance);
+  /**
+   * The referenced field.
+   */
+  private JField field;
+
+  /**
+   * This can only be null if the referenced field is static.
+   */
+  private JExpression instance;
+
+  public JFieldRef(JProgram program, JSourceInfo info, JExpression instance,
+      JField field, JReferenceType enclosingType) {
+    super(program, info, field);
+    assert (instance != null || field.isStatic());
+    this.instance = instance;
     this.field = field;
     this.enclosingType = enclosingType;
   }
@@ -52,37 +53,35 @@
   }
 
   public JExpression getInstance() {
-    return instance.get();
+    return instance;
   }
 
   public boolean hasSideEffects() {
     // A cross-class reference to a static, non constant field forces clinit
     if (field.isStatic()
-      && (!field.isFinal() || field.constInitializer == null)) {
+        && (!field.isFinal() || field.constInitializer == null)) {
       JReferenceType fieldEncloser = field.getEnclosingType();
       if (enclosingType != fieldEncloser
-        && program.typeOracle.hasClinit(fieldEncloser)) {
+          && program.typeOracle.hasClinit(fieldEncloser)) {
         // Therefore, we have side effects
         return true;
       }
     }
 
-    JExpression expr = instance.get();
+    JExpression expr = instance;
     if (expr == null) {
       return false;
     }
     return expr.hasSideEffects();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      instance.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      if (instance != null) {
+        instance = visitor.accept(instance);
+      }
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 da647c3..1ff769e 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
@@ -20,7 +20,7 @@
  */
 public class JFloatLiteral extends JLiteral {
 
-  public final float value;
+  private final float value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,13 +34,13 @@
     return program.getTypePrimitiveFloat();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public float getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JForStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JForStatement.java
index 4a63f35..082677b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JForStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JForStatement.java
@@ -22,21 +22,25 @@
  */
 public class JForStatement extends JStatement {
 
-  public final JStatement body;
-  private final List/* <JStatement> */initializers;
-  private final Holder testExpr = new Holder();
+  private JStatement body;
   private final List/* <JExpressionStatement> */increments;
+  private final List/* <JStatement> */initializers;
+  private JExpression testExpr;
 
-  public JForStatement(JProgram program, List/* <JStatement> */initializers,
-      JExpression testExpr, List/* <JExpressionStatement> */increments,
-      JStatement body) {
-    super(program);
+  public JForStatement(JProgram program, JSourceInfo info,
+      List/* <JStatement> */initializers, JExpression testExpr,
+      List/* <JExpressionStatement> */increments, JStatement body) {
+    super(program, info);
     this.initializers = initializers;
-    this.testExpr.set(testExpr);
+    this.testExpr = testExpr;
     this.increments = increments;
     this.body = body;
   }
 
+  public JStatement getBody() {
+    return body;
+  }
+
   public List/* <JExpressionStatement> */getIncrements() {
     return increments;
   }
@@ -46,25 +50,21 @@
   }
 
   public JExpression getTestExpr() {
-    return testExpr.get();
+    return testExpr;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < initializers.size(); ++i) {
-        JStatement stmt = (JStatement) initializers.get(i);
-        stmt.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.acceptWithInsertRemove(initializers);
+      if (testExpr != null) {
+        testExpr = visitor.accept(testExpr);
       }
-      testExpr.traverse(visitor);
-      for (int i = 0; i < increments.size(); ++i) {
-        JExpressionStatement stmt = (JExpressionStatement) increments.get(i);
-        stmt.traverse(visitor);
-      }
+      visitor.acceptWithInsertRemove(increments);
       if (body != null) {
-        body.traverse(visitor);
+        body = visitor.accept(body);
       }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java
index ea03bed..38389a9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java
@@ -16,38 +16,45 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java if statement. 
+ * Java if statement.
  */
 public class JIfStatement extends JStatement {
 
-  private final Holder ifExpr = new Holder();
-  public JStatement thenStmt;
-  public JStatement elseStmt;
+  private JStatement elseStmt;
+  private JExpression ifExpr;
+  private JStatement thenStmt;
 
-  public JIfStatement(JProgram program, JExpression ifExpr,
-      JStatement thenStmt, JStatement elseStmt) {
-    super(program);
-    this.ifExpr.set(ifExpr);
+  public JIfStatement(JProgram program, JSourceInfo info,
+      JExpression ifExpr, JStatement thenStmt, JStatement elseStmt) {
+    super(program, info);
+    this.ifExpr = ifExpr;
     this.thenStmt = thenStmt;
     this.elseStmt = elseStmt;
   }
 
-  public JExpression getIfExpr() {
-    return ifExpr.get();
+  public JStatement getElseStmt() {
+    return elseStmt;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      ifExpr.traverse(visitor);
+  public JExpression getIfExpr() {
+    return ifExpr;
+  }
+
+  public JStatement getThenStmt() {
+    return thenStmt;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      ifExpr = visitor.accept(ifExpr);
       if (thenStmt != null) {
-        thenStmt.traverse(visitor);
+        thenStmt = visitor.accept(thenStmt);
       }
-      
       if (elseStmt != null) {
-        elseStmt.traverse(visitor);
+        elseStmt = visitor.accept(elseStmt);
       }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JInstanceOf.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JInstanceOf.java
index d9d4fd6..d7cd5f5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JInstanceOf.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JInstanceOf.java
@@ -16,22 +16,26 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java instance of expression. 
+ * Java instance of expression.
  */
 public class JInstanceOf extends JExpression {
 
-  public final JReferenceType testType;
-  public final Holder expr = new Holder();
+  private JExpression expr;
+  private final JReferenceType testType;
 
-  public JInstanceOf(JProgram program, JReferenceType testType,
-      JExpression expression) {
-    super(program);
+  public JInstanceOf(JProgram program, JSourceInfo info,
+      JReferenceType testType, JExpression expression) {
+    super(program, info);
     this.testType = testType;
-    this.expr.set(expression);
+    this.expr = expression;
   }
 
-  public JExpression getExpression() {
-    return expr.get();
+  public JExpression getExpr() {
+    return expr;
+  }
+
+  public JReferenceType getTestType() {
+    return testType;
   }
 
   public JType getType() {
@@ -42,15 +46,11 @@
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      expr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      expr = visitor.accept(expr);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 f6d3599..37832fd 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
@@ -20,7 +20,7 @@
  */
 public class JIntLiteral extends JLiteral {
 
-  public final int value;
+  private final int value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,14 +34,14 @@
     return program.getTypePrimitiveInt();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public int getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
index 6b18f58..ab0db6d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
@@ -16,12 +16,12 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java interface type definition. 
+ * Java interface type definition.
  */
 public class JInterfaceType extends JReferenceType {
 
-  JInterfaceType(JProgram program, String name) {
-    super(program, name);
+  JInterfaceType(JProgram program, JSourceInfo info, String name) {
+    super(program, info, name);
   }
 
   public boolean isAbstract() {
@@ -32,17 +32,11 @@
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < fields.size(); ++i) {
-        JField field = (JField) fields.get(i);
-        field.traverse(visitor);
-      }
-      for (int i = 0; i < methods.size(); ++i) {
-        JMethod method = (JMethod) methods.get(i);
-        method.traverse(visitor);
-      }
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.acceptWithInsertRemove(fields);
+      visitor.acceptWithInsertRemove(methods);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLabel.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLabel.java
index 76c20cb..b0ae9eb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLabel.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLabel.java
@@ -20,10 +20,10 @@
  */
 public class JLabel extends JNode implements HasName {
 
-  public final String name;
-  
-  public JLabel(JProgram program, String name) {
-    super(program);
+  private final String name;
+
+  public JLabel(JProgram program, JSourceInfo info, String name) {
+    super(program, info);
     this.name = name;
   }
 
@@ -31,10 +31,10 @@
     return name;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLabeledStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLabeledStatement.java
index 0b77a29..3bdcdf1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLabeledStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLabeledStatement.java
@@ -20,20 +20,29 @@
  */
 public class JLabeledStatement extends JStatement {
 
-  public final JLabel label;
-  public JStatement body;
-  
-  public JLabeledStatement(JProgram program, JLabel label, JStatement body) {
-    super(program);
+  private JStatement body;
+  private final JLabel label;
+
+  public JLabeledStatement(JProgram program, JSourceInfo info, JLabel label,
+      JStatement body) {
+    super(program, info);
     this.label = label;
     this.body = body;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      label.traverse(visitor);
-      body.traverse(visitor);
+  public JStatement getBody() {
+    return body;
+  }
+
+  public JLabel getLabel() {
+    return label;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(label);
+      body = visitor.accept(body);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 }
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 837b14f..6d1f498 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
@@ -21,7 +21,7 @@
 public abstract class JLiteral extends JExpression {
 
   public JLiteral(JProgram program) {
-    super(program);
+    super(program, null);
   }
 
   public boolean hasSideEffects() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
index c5d10ec..0b83cbb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
@@ -16,15 +16,15 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java local variable definition. 
+ * Java local variable definition.
  */
 public class JLocal extends JVariable implements HasEnclosingMethod {
 
   private final JMethod enclosingMethod;
 
-  JLocal(JProgram program, String name, JType type, boolean isFinal,
-      JMethod enclosingMethod) {
-    super(program, name, type, isFinal);
+  JLocal(JProgram program, JSourceInfo info, String name, JType type,
+      boolean isFinal, JMethod enclosingMethod) {
+    super(program, info, name, type, isFinal);
     this.enclosingMethod = enclosingMethod;
   }
 
@@ -32,10 +32,10 @@
     return enclosingMethod;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java
index 90d7df4..2cb070e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java
@@ -20,30 +20,32 @@
  */
 public class JLocalDeclarationStatement extends JStatement {
 
-  public final Holder initializer = new Holder();
-  private final Holder/* <JLocalRef> */localRef = new Holder/* <JLocalRef> */();
+  public JExpression initializer;
+  private JLocalRef localRef;
 
-  public JLocalDeclarationStatement(JProgram program, JLocalRef localRef,
-      JExpression intializer) {
-    super(program);
-    this.localRef.set(localRef);
-    this.initializer.set(intializer);
+  public JLocalDeclarationStatement(JProgram program, JSourceInfo info,
+      JLocalRef localRef, JExpression intializer) {
+    super(program, info);
+    this.localRef = localRef;
+    this.initializer = intializer;
   }
 
   public JExpression getInitializer() {
-    return initializer.get();
+    return initializer;
   }
 
   public JLocalRef getLocalRef() {
-    return (JLocalRef) localRef.get();
+    return localRef;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      localRef.traverse(visitor);
-      initializer.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      localRef = (JLocalRef) visitor.accept(localRef);
+      if (initializer != null) {
+        initializer = visitor.accept(initializer);
+      }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalRef.java
index 2222746..fddabe0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalRef.java
@@ -23,28 +23,24 @@
   /**
    * The referenced local.
    */
-  public JLocal local;
+  private JLocal local;
 
-  public JLocalRef(JProgram program, JLocal local) {
-    super(program, local);
+  public JLocalRef(JProgram program, JSourceInfo info, JLocal local) {
+    super(program, info, local);
     this.local = local;
   }
 
   public JLocal getLocal() {
     return local;
   }
-  
+
   public boolean hasSideEffects() {
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
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 03df222..dc0ecee 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
@@ -20,7 +20,7 @@
  */
 public class JLongLiteral extends JLiteral {
 
-  public final long value;
+  private final long value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,13 +34,13 @@
     return program.getTypePrimitiveLong();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public long getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 4637c7c..d655577 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -49,14 +49,14 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  public JMethod(JProgram program, String name, JReferenceType enclosingType,
-      JType returnType, boolean isAbstract, boolean isStatic, boolean isFinal,
-      boolean isPrivate) {
-    super(program);
+  public JMethod(JProgram program, JSourceInfo info, String name,
+      JReferenceType enclosingType, JType returnType, boolean isAbstract,
+      boolean isStatic, boolean isFinal, boolean isPrivate) {
+    super(program, info);
     this.name = name;
     this.enclosingType = enclosingType;
     this.returnType = returnType;
-    this.body = new JBlock(program);
+    this.body = new JBlock(program, info);
     this.isAbstract = isAbstract;
     this.isStatic = isStatic;
     this.isFinal = isFinal;
@@ -121,19 +121,13 @@
     returnType = newType;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < params.size(); ++i) {
-        JParameter param = (JParameter) params.get(i);
-        param.traverse(visitor);
-      }
-      for (int i = 0; i < locals.size(); ++i) {
-        JLocal local = (JLocal) locals.get(i);
-        local.traverse(visitor);
-      }
-      body.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(params);
+      visitor.accept(locals);
+      visitor.accept(body);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
index 5f75fdc..cb878f6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
@@ -15,20 +15,23 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import java.util.ArrayList;
+
 /**
  * Java method call expression.
  */
 public class JMethodCall extends JExpression {
 
-  public HolderList args = new HolderList();
-  public final Holder instance = new Holder();
+  private ArrayList args = new ArrayList();
+  private JExpression instance;
   private final JMethod method;
   private final JType overrideReturnType;
   private boolean staticDispatchOnly = false;
 
-  public JMethodCall(JProgram program, JExpression instance, JMethod method) {
-    super(program);
-    this.instance.set(instance);
+  public JMethodCall(JProgram program, JSourceInfo info, JExpression instance,
+      JMethod method) {
+    super(program, info);
+    this.instance = instance;
     this.method = method;
     this.staticDispatchOnly = false;
     this.overrideReturnType = null;
@@ -45,21 +48,34 @@
    * allows us to preserve type information during the latter phases of
    * compilation.
    */
-  public JMethodCall(JProgram program, JExpression instance, JMethod method,
-      JType overrideReturnType) {
-    super(program);
-    this.instance.set(instance);
+  public JMethodCall(JProgram program, JSourceInfo info, JExpression instance,
+      JMethod method, JType overrideReturnType) {
+    super(program, info);
+    this.instance = instance;
     this.method = method;
     assert (overrideReturnType != null);
     this.overrideReturnType = overrideReturnType;
   }
 
+  public JMethodCall(JProgram program, JSourceInfo info, JExpression instance,
+      JMethod method, boolean staticDispatchOnly) {
+    super(program, info);
+    this.instance = instance;
+    this.method = method;
+    this.staticDispatchOnly = staticDispatchOnly;
+    this.overrideReturnType = null;
+  }
+
   public boolean canBePolymorphic() {
     return !staticDispatchOnly && !method.isFinal() && !method.isStatic();
   }
 
+  public ArrayList getArgs() {
+    return args;
+  }
+
   public JExpression getInstance() {
-    return instance.get();
+    return instance;
   }
 
   public JMethod getTarget() {
@@ -87,16 +103,14 @@
     this.staticDispatchOnly = true;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      instance.traverse(visitor);
-      args.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      if (instance != null) {
+        instance = visitor.accept(instance);
+      }
+      visitor.accept(args);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java
new file mode 100644
index 0000000..59721d2
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.ast;
+
+import com.google.gwt.dev.jjs.impl.InternalCompilerException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A visitor for iterating through and modifying an AST.
+ */
+public class JModVisitor extends JVisitor {
+
+  private interface ContextFactory {
+    Context create();
+  }
+
+  private class ContextPool extends ArrayList {
+
+    private ContextFactory factory;
+    private int pos = 0;
+
+    public ContextPool(ContextFactory factory) {
+      this.factory = factory;
+    }
+
+    public void release(Context ctx) {
+      if (get(--pos) != ctx) {
+        throw new InternalCompilerException(
+            "Tried to release the wrong context");
+      }
+    }
+
+    public Context take() {
+      if (pos == size()) {
+        add(factory.create());
+      }
+      return (Context) get(pos++);
+    }
+  }
+
+  private class ListContext implements Context {
+    private int index;
+    private List list;
+    private boolean removed;
+    private boolean replaced;
+
+    public boolean canInsert() {
+      return true;
+    }
+
+    public boolean canRemove() {
+      return true;
+    }
+
+    public void insertAfter(JNode node) {
+      checkRemoved();
+      list.add(index + 1, node);
+      didChange = true;
+    }
+
+    public void insertBefore(JNode node) {
+      checkRemoved();
+      list.add(index++, node);
+      didChange = true;
+    }
+
+    public void removeMe() {
+      checkState();
+      list.remove(index--);
+      didChange = removed = true;
+    }
+
+    public void replaceMe(JNode node) {
+      checkState();
+      checkReplacement((JNode) list.get(index), node);
+      list.set(index, node);
+      didChange = replaced = true;
+    }
+
+    protected void doReplace(Class targetClass, JNode x) {
+      checkState();
+      checkReplacement((JNode) list.get(index), x);
+      list.set(index, x);
+      didChange = replaced = true;
+    }
+
+    protected void traverse(List list) {
+      this.list = list;
+      for (index = 0; index < list.size(); ++index) {
+        removed = replaced = false;
+        doTraverse((JNode) list.get(index), this);
+      }
+    }
+
+    private void checkRemoved() {
+      if (removed) {
+        throw new InternalCompilerException("Node was already removed");
+      }
+    }
+
+    private void checkState() {
+      checkRemoved();
+      if (replaced) {
+        throw new InternalCompilerException("Node was already replaced");
+      }
+    }
+  }
+
+  private class NodeContext implements Context {
+    private JNode node;
+    private boolean replaced;
+
+    public boolean canInsert() {
+      return false;
+    }
+
+    public boolean canRemove() {
+      return false;
+    }
+
+    public void insertAfter(JNode node) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void insertBefore(JNode node) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void removeMe() {
+      throw new UnsupportedOperationException();
+    }
+
+    public void replaceMe(JNode node) {
+      if (replaced) {
+        throw new InternalCompilerException("Node was already replaced");
+      }
+      checkReplacement(this.node, node);
+      this.node = node;
+      didChange = replaced = true;
+    }
+
+    protected JNode traverse(JNode node) {
+      this.node = node;
+      replaced = false;
+      doTraverse(node, this);
+      return this.node;
+    }
+  }
+
+  protected static void checkReplacement(JNode origNode, JNode newNode) {
+    if (newNode == null) {
+      throw new InternalCompilerException("Cannot replace with null");
+    }
+    if (newNode == origNode) {
+      throw new InternalCompilerException(
+          "The replacement is the same as the original");
+    }
+  }
+
+  protected boolean didChange = false;
+
+  private final ContextPool listContextPool = new ContextPool(
+      new ContextFactory() {
+        public Context create() {
+          return new ListContext();
+        }
+      });
+
+  private final ContextPool nodeContextPool = new ContextPool(
+      new ContextFactory() {
+        public Context create() {
+          return new NodeContext();
+        }
+      });
+
+  public boolean didChange() {
+    return didChange;
+  }
+
+  protected JNode doAccept(JNode node) {
+    NodeContext ctx = (NodeContext) nodeContextPool.take();
+    try {
+      return ctx.traverse(node);
+    } finally {
+      nodeContextPool.release(ctx);
+    }
+  }
+
+  protected void doAccept(List list) {
+    NodeContext ctx = (NodeContext) nodeContextPool.take();
+    try {
+      for (int i = 0, c = list.size(); i < c; ++i) {
+        list.set(i, ctx.traverse((JNode) list.get(i)));
+      }
+    } finally {
+      nodeContextPool.release(ctx);
+    }
+  }
+
+  protected void doAcceptWithInsertRemove(List list) {
+    ListContext ctx = (ListContext) listContextPool.take();
+    try {
+      ctx.traverse(list);
+    } finally {
+      listContextPool.release(ctx);
+    }
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
index f415e30..06ad7bc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
@@ -15,17 +15,19 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import java.util.ArrayList;
+
 /**
  * New array experssion.
  */
 public class JNewArray extends JExpression implements HasSettableType {
 
-  public HolderList dims = null;
-  public HolderList initializers = null;
+  public ArrayList dims = null;
+  public ArrayList initializers = null;
   private JArrayType arrayType;
 
-  public JNewArray(JProgram program, JArrayType arrayType) {
-    super(program);
+  public JNewArray(JProgram program, JSourceInfo info, JArrayType arrayType) {
+    super(program, info);
     this.arrayType = arrayType;
   }
 
@@ -45,22 +47,18 @@
     this.arrayType = (JArrayType) arrayType;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
       assert ((dims != null) ^ (initializers != null));
 
       if (dims != null) {
-        dims.traverse(visitor);
+        visitor.accept(dims);
       }
 
       if (initializers != null) {
-        initializers.traverse(visitor);
+        visitor.accept(initializers);
       }
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
index e8387b6..b12751d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
@@ -22,8 +22,8 @@
 
   private final JClassType classType;
 
-  public JNewInstance(JProgram program, JClassType classType) {
-    super(program);
+  public JNewInstance(JProgram program, JSourceInfo info, JClassType classType) {
+    super(program, info);
     this.classType = classType;
   }
 
@@ -39,14 +39,10 @@
     return true;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 4dc4152..d5fbf6c 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.impl.SourceGenerationVisitor;
 import com.google.gwt.dev.jjs.impl.ToStringGenerationVisitor;
 import com.google.gwt.dev.util.TextOutputOnCharArray;
 
@@ -22,26 +23,37 @@
  * Base class for all visitable AST nodes.
  */
 public abstract class JNode implements JVisitable {
-  /*
-   * The current visitor pattern uses reflection, but it could be 
-   * replaced by direct code for better performance.
-   */
-  protected final JProgram program;
 
-  protected JNode(JProgram program) {
+  protected final JProgram program;
+  private final JSourceInfo info;
+
+  protected JNode(JProgram program, JSourceInfo info) {
     if (program == null) {
       assert (this instanceof JProgram);
       this.program = (JProgram) this;
     } else {
       this.program = program;
     }
+    this.info = info;
+  }
+
+  public JSourceInfo getSourceInfo() {
+    return info;
+  }
+
+  // Causes source generation to delegate to the one visitor
+  public final String toSource() {
+    TextOutputOnCharArray p = new TextOutputOnCharArray(false);
+    SourceGenerationVisitor v = new SourceGenerationVisitor(p);
+    v.accept(this);
+    return new String(p.getText());
   }
 
   // Causes source generation to delegate to the one visitor
   public final String toString() {
     TextOutputOnCharArray p = new TextOutputOnCharArray(false);
     ToStringGenerationVisitor v = new ToStringGenerationVisitor(p);
-    traverse(v);
+    v.accept(this);
     return new String(p.getText());
   }
 }
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 f70d3a3..d775268 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
@@ -31,13 +31,9 @@
     return program.getTypeNull();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
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 69c29a5..76f3d76 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
@@ -21,7 +21,7 @@
 public class JNullType extends JReferenceType {
 
   public JNullType(JProgram program) {
-    super(program, "<null>");
+    super(program, null, "<null>");
   }
 
   public String getJavahSignatureName() {
@@ -40,10 +40,10 @@
     return true;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
index 1e09853..ef3ab83 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
@@ -22,9 +22,9 @@
 
   private final JMethod enclosingMethod;
 
-  JParameter(JProgram program, String name, JType type, boolean isFinal,
-      JMethod enclosingMethod) {
-    super(program, name, type, isFinal);
+  JParameter(JProgram program, JSourceInfo info, String name, JType type,
+      boolean isFinal, JMethod enclosingMethod) {
+    super(program, info, name, type, isFinal);
     this.enclosingMethod = enclosingMethod;
   }
 
@@ -32,9 +32,9 @@
     return enclosingMethod;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameterRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameterRef.java
index d6334d0..914f4c9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameterRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameterRef.java
@@ -25,8 +25,8 @@
    */
   private final JParameter param;
 
-  public JParameterRef(JProgram program, JParameter param) {
-    super(program, param);
+  public JParameterRef(JProgram program, JSourceInfo info, JParameter param) {
+    super(program, info, param);
     this.param = param;
   }
 
@@ -38,13 +38,9 @@
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JPostfixOperation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JPostfixOperation.java
index cde0eec..05c55d8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JPostfixOperation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JPostfixOperation.java
@@ -16,42 +16,43 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java postfix expression. 
+ * Java postfix expression.
  */
 public class JPostfixOperation extends JExpression {
 
-  private final Holder arg = new Holder();
-  public JUnaryOperator op;
+  private JExpression arg;
+  private final JUnaryOperator op;
 
-  public JPostfixOperation(JProgram program, JUnaryOperator op, JExpression arg) {
-    super(program);
+  public JPostfixOperation(JProgram program, JSourceInfo info,
+      JUnaryOperator op, JExpression arg) {
+    super(program, info);
     this.op = op;
-    this.arg.set(arg);
+    this.arg = arg;
   }
 
   public JExpression getArg() {
-    return arg.get();
+    return arg;
+  }
+
+  public JUnaryOperator getOp() {
+    return op;
   }
 
   public JType getType() {
     // Unary operators don't change the type of their expression
-    return arg.get().getType();
+    return arg.getType();
   }
 
   public boolean hasSideEffects() {
     return op == JUnaryOperator.DEC || op == JUnaryOperator.INC
-      || arg.get().hasSideEffects();
+        || arg.hasSideEffects();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      arg.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      arg = visitor.accept(arg);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrefixOperation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrefixOperation.java
index e228190..e550cbd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrefixOperation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrefixOperation.java
@@ -20,38 +20,39 @@
  */
 public class JPrefixOperation extends JExpression {
 
-  private final Holder arg = new Holder();
-  public JUnaryOperator op;
+  private JExpression arg;
+  private final JUnaryOperator op;
 
-  public JPrefixOperation(JProgram program, JUnaryOperator op, JExpression arg) {
-    super(program);
+  public JPrefixOperation(JProgram program, JSourceInfo info, JUnaryOperator op,
+      JExpression arg) {
+    super(program, info);
     this.op = op;
-    this.arg.set(arg);
+    this.arg = arg;
   }
 
   public JExpression getArg() {
-    return arg.get();
+    return arg;
+  }
+
+  public JUnaryOperator getOp() {
+    return op;
   }
 
   public JType getType() {
     // Unary operators don't change the type of their expression
-    return arg.get().getType();
+    return arg.getType();
   }
 
   public boolean hasSideEffects() {
-    return op == JUnaryOperator.DEC || op == JUnaryOperator.INC
-      || arg.get().hasSideEffects();
+    return getOp() == JUnaryOperator.DEC || getOp() == JUnaryOperator.INC
+        || arg.hasSideEffects();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      arg.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      arg = visitor.accept(arg);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 f43dece..6a16876 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
@@ -16,7 +16,7 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Base class for all Java primitive types.  
+ * Base class for all Java primitive types.
  */
 public class JPrimitiveType extends JType {
 
@@ -25,8 +25,9 @@
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JPrimitiveType(JProgram program, String name, String signatureName, JLiteral defaultValue) {
-    super(program, name, defaultValue);
+  JPrimitiveType(JProgram program, String name, String signatureName,
+      JLiteral defaultValue) {
+    super(program, null, name, defaultValue);
     this.signatureName = signatureName;
   }
 
@@ -38,10 +39,10 @@
     return signatureName;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 7ea27b8..a56a874 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
@@ -204,7 +204,7 @@
       null);
 
   public JProgram(TreeLogger logger, RebindOracle rebindOracle) {
-    super(null);
+    super(null, null);
     this.logger = logger;
     this.rebindOracle = rebindOracle;
   }
@@ -218,17 +218,17 @@
   /**
    * Helper to create an assignment, used to initalize fields, etc.
    */
-  public JExpressionStatement createAssignmentStmt(JExpression lhs,
-      JExpression rhs) {
-    JBinaryOperation assign = new JBinaryOperation(this, lhs.getType(),
+  public JExpressionStatement createAssignmentStmt(JSourceInfo info,
+      JExpression lhs, JExpression rhs) {
+    JBinaryOperation assign = new JBinaryOperation(this, info, lhs.getType(),
         JBinaryOperator.ASG, lhs, rhs);
-    return new JExpressionStatement(this, assign);
+    return new JExpressionStatement(this, info, assign);
   }
 
-  public JClassType createClass(char[][] name, boolean isAbstract,
-      boolean isFinal) {
+  public JClassType createClass(JSourceInfo info, char[][] name,
+      boolean isAbstract, boolean isFinal) {
     String sname = dotify(name);
-    JClassType x = new JClassType(this, sname, isAbstract, isFinal);
+    JClassType x = new JClassType(this, info, sname, isAbstract, isFinal);
 
     allTypes.add(x);
     putIntoTypeMap(sname, x);
@@ -256,8 +256,9 @@
     return x;
   }
 
-  public JField createField(char[] name, JReferenceType enclosingType,
-      JType type, boolean isStatic, boolean isFinal, boolean hasInitializer) {
+  public JField createField(JSourceInfo info, char[] name,
+      JReferenceType enclosingType, JType type, boolean isStatic,
+      boolean isFinal, boolean hasInitializer) {
     assert (name != null);
     assert (enclosingType != null);
     assert (type != null);
@@ -273,8 +274,8 @@
     }
 
     String sname = String.valueOf(name);
-    JField x = new JField(this, sname, enclosingType, type, isStatic, isFinal,
-        hasInitializer);
+    JField x = new JField(this, info, sname, enclosingType, type, isStatic,
+        isFinal, hasInitializer);
 
     if (isSpecialField) {
       specialFields.put(enclosingType.getShortName() + '.' + sname, x);
@@ -285,9 +286,9 @@
     return x;
   }
 
-  public JInterfaceType createInterface(char[][] name) {
+  public JInterfaceType createInterface(JSourceInfo info, char[][] name) {
     String sname = dotify(name);
-    JInterfaceType x = new JInterfaceType(this, sname);
+    JInterfaceType x = new JInterfaceType(this, info, sname);
 
     allTypes.add(x);
     putIntoTypeMap(sname, x);
@@ -295,13 +296,13 @@
     return x;
   }
 
-  public JLocal createLocal(char[] name, JType type, boolean isFinal,
-      JMethod enclosingMethod) {
+  public JLocal createLocal(JSourceInfo info, char[] name, JType type,
+      boolean isFinal, JMethod enclosingMethod) {
     assert (name != null);
     assert (type != null);
     assert (enclosingMethod != null);
 
-    JLocal x = new JLocal(this, String.valueOf(name), type, isFinal,
+    JLocal x = new JLocal(this, info, String.valueOf(name), type, isFinal,
         enclosingMethod);
 
     enclosingMethod.locals.add(x);
@@ -309,9 +310,9 @@
     return x;
   }
 
-  public JMethod createMethod(char[] name, JReferenceType enclosingType,
-      JType returnType, boolean isAbstract, boolean isStatic, boolean isFinal,
-      boolean isPrivate, boolean isNative) {
+  public JMethod createMethod(JSourceInfo info, char[] name,
+      JReferenceType enclosingType, JType returnType, boolean isAbstract,
+      boolean isStatic, boolean isFinal, boolean isPrivate, boolean isNative) {
     assert (name != null);
     assert (returnType != null);
     assert (!isAbstract || !isNative);
@@ -319,10 +320,10 @@
     JMethod x;
     String sname = String.valueOf(name);
     if (isNative) {
-      x = new JsniMethod(this, sname, enclosingType, returnType, isStatic,
-          isFinal, isPrivate);
+      x = new JsniMethod(this, info, sname, enclosingType, returnType,
+          isStatic, isFinal, isPrivate);
     } else {
-      x = new JMethod(this, sname, enclosingType, returnType, isAbstract,
+      x = new JMethod(this, info, sname, enclosingType, returnType, isAbstract,
           isStatic, isFinal, isPrivate);
     }
 
@@ -341,14 +342,14 @@
     return x;
   }
 
-  public JParameter createParameter(char[] name, JType type, boolean isFinal,
-      JMethod enclosingMethod) {
+  public JParameter createParameter(JSourceInfo info, char[] name, JType type,
+      boolean isFinal, JMethod enclosingMethod) {
     assert (name != null);
     assert (type != null);
     assert (enclosingMethod != null);
 
-    JParameter x = new JParameter(this, String.valueOf(name), type, isFinal,
-        enclosingMethod);
+    JParameter x = new JParameter(this, info, String.valueOf(name), type,
+        isFinal, enclosingMethod);
 
     enclosingMethod.params.add(x);
 
@@ -374,8 +375,8 @@
     return allTypes;
   }
 
-  public JThisRef getExpressionThisRef(JClassType enclosingType) {
-    return new JThisRef(this, enclosingType);
+  public JThisRef getExprThisRef(JSourceInfo info, JClassType enclosingType) {
+    return new JThisRef(this, info, enclosingType);
   }
 
   public JReferenceType getFromTypeMap(String qualifiedBinaryOrSourceName) {
@@ -449,16 +450,16 @@
 
   public JField getNullField() {
     if (nullField == null) {
-      nullField = new JField(this, "nullField", null, typeNull, false, true,
-          true);
+      nullField = new JField(this, null, "nullField", null, typeNull, false,
+          true, true);
     }
     return nullField;
   }
 
   public JMethod getNullMethod() {
     if (nullMethod == null) {
-      nullMethod = new JsniMethod(this, "nullMethod", null, typeNull, false,
-          true, true);
+      nullMethod = new JsniMethod(this, null, "nullMethod", null, typeNull,
+          false, true, true);
     }
     return nullMethod;
   }
@@ -688,18 +689,12 @@
     return type1;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < entryMethods.size(); ++i) {
-        JMethod method = (JMethod) entryMethods.get(i);
-        method.traverse(visitor);
-      }
-      for (int i = 0; i < allTypes.size(); ++i) {
-        JReferenceType type = (JReferenceType) allTypes.get(i);
-        type.traverse(visitor);
-      }
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(entryMethods);
+      visitor.accept(allTypes);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
   JReferenceType generalizeTypes(JReferenceType type1, JReferenceType type2) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
index 11b60fd..9548010 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
@@ -24,13 +24,13 @@
 public abstract class JReferenceType extends JType implements CanBeAbstract,
     CanBeFinal {
 
-  public List/*<JField>*/ fields = new ArrayList/*<JField>*/();
-  public List/*<JMethod>*/ methods = new ArrayList/*<JMethod>*/();
+  public List/* <JField> */fields = new ArrayList/* <JField> */();
+  public List/* <JMethod> */methods = new ArrayList/* <JMethod> */();
   public JClassType extnds;
-  public List/*<JInterfaceType>*/ implments = new ArrayList/*<JInterfaceType>*/();
+  public List/* <JInterfaceType> */implments = new ArrayList/* <JInterfaceType> */();
 
-  public JReferenceType(JProgram program, String name) {
-    super(program, name, program.getLiteralNull());
+  public JReferenceType(JProgram program, JSourceInfo info, String name) {
+    super(program, info, name, program.getLiteralNull());
   }
 
   public String getJavahSignatureName() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReturnStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReturnStatement.java
index 0b84cb1..5662f7f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JReturnStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReturnStatement.java
@@ -20,22 +20,24 @@
  */
 public class JReturnStatement extends JStatement {
 
-  public final Holder expr = new Holder();
+  private JExpression expr;
 
-  public JReturnStatement(JProgram program, JExpression expr) {
-    super(program);
-    this.expr.set(expr);
+  public JReturnStatement(JProgram program, JSourceInfo info, JExpression expr) {
+    super(program, info);
+    this.expr = expr;
   }
 
-  public JExpression getExpression() {
-    return expr.get();
+  public JExpression getExpr() {
+    return expr;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      expr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      if (expr != null) {
+        expr = visitor.accept(expr);
+      }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JSourceInfo.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JSourceInfo.java
new file mode 100644
index 0000000..62b14ac
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JSourceInfo.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.ast;
+
+/**
+ * Tracks file and line information for AST nodes.
+ */
+public class JSourceInfo {
+
+  private final int endPos;
+  private final String fileName;
+  private final int startLine;
+  private final int startPos;
+
+  public JSourceInfo(int startPos, int endPos, int startLine, String fileName) {
+    this.startPos = startPos;
+    this.endPos = endPos;
+    this.startLine = startLine;
+    this.fileName = fileName;
+  }
+
+  public int getEndPos() {
+    return endPos;
+  }
+
+  public String getFileName() {
+    return fileName;
+  }
+
+  public int getStartLine() {
+    return startLine;
+  }
+
+  public int getStartPos() {
+    return startPos;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JStatement.java
index 48f6afd..26a5f3b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JStatement.java
@@ -20,8 +20,8 @@
  */
 public abstract class JStatement extends JNode {
 
-  public JStatement(JProgram program) {
-    super(program);
+  public JStatement(JProgram program, JSourceInfo info) {
+    super(program, info);
   }
 
 }
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 9e57356..e3f7da3 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
@@ -20,7 +20,7 @@
  */
 public class JStringLiteral extends JLiteral {
 
-  public final String value;
+  private final String value;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -34,13 +34,13 @@
     return program.getTypeJavaLangString();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
+  public String getValue() {
+    return value;
   }
 
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JSwitchStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JSwitchStatement.java
index f408c12..1b2fd20 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JSwitchStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JSwitchStatement.java
@@ -16,29 +16,34 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Java switch statement. 
+ * Java switch statement.
  */
 public class JSwitchStatement extends JStatement {
 
-  private final Holder expr = new Holder();
-  public JBlock body;
+  private final JBlock body;
+  private JExpression expr;
 
-  public JSwitchStatement(JProgram program, JExpression expr, JBlock body) {
-    super(program);
-    this.expr.set(expr);
+  public JSwitchStatement(JProgram program, JSourceInfo info, JExpression expr,
+      JBlock body) {
+    super(program, info);
+    this.expr = expr;
     this.body = body;
   }
 
-  public JExpression getExpression() {
-    return expr.get();
+  public JBlock getBody() {
+    return body;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      expr.traverse(visitor);
-      body.traverse(visitor);
+  public JExpression getExpr() {
+    return expr;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      expr = visitor.accept(expr);
+      visitor.accept(body);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
index ce0181d..5d3c9bd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
@@ -20,13 +20,13 @@
  */
 public class JThisRef extends JExpression {
 
-  public JClassType classType;
+  private final JClassType classType;
 
   /**
    * These are only supposed to be constructed by JProgram.
    */
-  JThisRef(JProgram program, JClassType classType) {
-    super(program);
+  JThisRef(JProgram program, JSourceInfo info, JClassType classType) {
+    super(program, info);
     this.classType = classType;
   }
 
@@ -42,14 +42,10 @@
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JThrowStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JThrowStatement.java
index 83230b6..9645be7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JThrowStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JThrowStatement.java
@@ -20,22 +20,22 @@
  */
 public class JThrowStatement extends JStatement {
 
-  private final Holder expr = new Holder();
+  private JExpression expr;
 
-  public JThrowStatement(JProgram program, JExpression expr) {
-    super(program);
-    this.expr.set(expr);
+  public JThrowStatement(JProgram program, JSourceInfo info, JExpression expr) {
+    super(program, info);
+    this.expr = expr;
   }
 
-  public JExpression getExpression() {
-    return expr.get();
+  public JExpression getExpr() {
+    return expr;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      expr.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      expr = visitor.accept(expr);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java
index 8c174bb..38a4fd3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java
@@ -22,32 +22,48 @@
  */
 public class JTryStatement extends JStatement {
 
-  public JBlock tryBlock;
-  public final HolderList catchArgs = new HolderList();
-  public List/*<JBlock>*/ catchBlocks;
-  public JBlock finallyBlock;
+  private final List catchArgs;
+  private final List catchBlocks;
+  private final JBlock finallyBlock;
+  private final JBlock tryBlock;
 
-  public JTryStatement(JProgram program, JBlock tryBlock, List/*<JLocalRef>*/ catchArgs,
-      List/*<JBlock>*/ catchBlocks, JBlock finallyBlock) {
-    super(program);
+  public JTryStatement(JProgram program, JSourceInfo info, JBlock tryBlock,
+      List/* <JLocalRef> */catchArgs, List/* <JBlock> */catchBlocks,
+      JBlock finallyBlock) {
+    super(program, info);
+    assert (catchArgs.size() == catchBlocks.size());
     this.tryBlock = tryBlock;
-    this.catchArgs.addAll(catchArgs);
+    this.catchArgs = catchArgs;
     this.catchBlocks = catchBlocks;
     this.finallyBlock = finallyBlock;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      tryBlock.traverse(visitor);
-      catchArgs.traverse(visitor);
-      for (int i = 0; i < catchBlocks.size(); ++i) {
-        JBlock block = (JBlock) catchBlocks.get(i);
-        block.traverse(visitor);
-      }
+  public List getCatchArgs() {
+    return catchArgs;
+  }
+
+  public List/* <JBlock> */getCatchBlocks() {
+    return catchBlocks;
+  }
+
+  public JBlock getFinallyBlock() {
+    return finallyBlock;
+  }
+
+  public JBlock getTryBlock() {
+    return tryBlock;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(tryBlock);
+      visitor.accept(catchArgs);
+      visitor.accept(catchBlocks);
+      // TODO: normalize this so it's never null?
       if (finallyBlock != null) {
-        finallyBlock.traverse(visitor);
+        visitor.accept(finallyBlock);
       }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
index 6b864bd..3a35958 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
@@ -20,11 +20,12 @@
  */
 public abstract class JType extends JNode implements HasName {
 
-  public String name;
-  public JLiteral defaultValue;
+  protected final String name;
+  private final JLiteral defaultValue;
 
-  public JType(JProgram program, String name, JLiteral defaultValue) {
-    super(program);
+  public JType(JProgram program, JSourceInfo info, String name,
+      JLiteral defaultValue) {
+    super(program, info);
     this.name = name;
     this.defaultValue = defaultValue;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
index 75f5265..c8b27c8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
@@ -21,12 +21,13 @@
 public abstract class JVariable extends JNode implements CanBeFinal, HasName,
     HasType, HasSettableType {
 
-  public JType type;
-  public String name;
-  public boolean isFinal;
+  private boolean isFinal;
+  private final String name;
+  private JType type;
 
-  JVariable(JProgram program, String name, JType type, boolean isFinal) {
-    super(program);
+  JVariable(JProgram program, JSourceInfo info, String name, JType type,
+      boolean isFinal) {
+    super(program, info);
     this.name = name;
     this.type = type;
     this.isFinal = isFinal;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariableRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariableRef.java
index 4c0f6a3..9a7e302 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariableRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariableRef.java
@@ -20,17 +20,17 @@
  */
 public abstract class JVariableRef extends JExpression {
 
-  public JVariable target;
-  
-  public JVariableRef(JProgram program, JVariable target) {
-    super(program);
+  protected JVariable target;
+
+  public JVariableRef(JProgram program, JSourceInfo info, JVariable target) {
+    super(program, info);
     this.target = target;
   }
 
   public JVariable getTarget() {
     return target;
   }
-  
+
   public JType getType() {
     return target.getType();
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitable.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitable.java
index 87d2425..d18836a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitable.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitable.java
@@ -19,5 +19,12 @@
  * Abstracts the idea that a class can be traversed.
  */
 public interface JVisitable {
-  void traverse(JVisitor visitor);
+
+  /**
+   * Causes this object to have the visitor visit itself and its children.
+   * 
+   * @param visitor the visitor that should traverse this node
+   * @param ctx the context of an existing traversal
+   */
+  void traverse(JVisitor visitor, Context ctx);
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index 1fb1f5b..fc9c259 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -23,423 +23,533 @@
 import com.google.gwt.dev.jjs.ast.js.JsonArray;
 import com.google.gwt.dev.jjs.ast.js.JsonObject;
 import com.google.gwt.dev.jjs.ast.js.JsonObject.JsonPropInit;
+import com.google.gwt.dev.jjs.impl.InternalCompilerException;
+
+import java.util.Iterator;
+import java.util.List;
 
 /**
- * A visitor for iterating through the parse tree.
+ * A visitor for iterating through an AST.
  */
 public class JVisitor {
 
-  public void endVisit(JAbsentArrayDimension x, Mutator m) {
+  protected static final Context UNMODIFIABLE_CONTEXT = new Context() {
+
+    public boolean canInsert() {
+      return false;
+    }
+
+    public boolean canRemove() {
+      return false;
+    }
+
+    public void insertAfter(JNode node) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void insertBefore(JNode node) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void removeMe() {
+      throw new UnsupportedOperationException();
+    }
+
+    public void replaceMe(JNode node) {
+      throw new UnsupportedOperationException();
+    }
+
+  };
+
+  public final JExpression accept(JExpression node) {
+    return (JExpression) doAccept(node);
   }
 
-  public void endVisit(JArrayRef x, Mutator m) {
+  public final JNode accept(JNode node) {
+    return doAccept(node);
   }
 
-  public void endVisit(JArrayType x) {
+  public final JStatement accept(JStatement node) {
+    return (JStatement) doAccept(node);
   }
 
-  public void endVisit(JAssertStatement x) {
+  public final void accept(List list) {
+    doAccept(list);
   }
 
-  public void endVisit(JBinaryOperation x, Mutator m) {
+  public final void acceptWithInsertRemove(List list) {
+    doAcceptWithInsertRemove(list);
   }
 
-  public void endVisit(JBlock x) {
+  public boolean didChange() {
+    throw new UnsupportedOperationException();
   }
 
-  public void endVisit(JBooleanLiteral x, Mutator m) {
+  public void endVisit(JAbsentArrayDimension x, Context ctx) {
   }
 
-  public void endVisit(JBreakStatement x) {
+  public void endVisit(JArrayRef x, Context ctx) {
   }
 
-  public void endVisit(JCaseStatement x) {
+  public void endVisit(JArrayType x, Context ctx) {
   }
 
-  public void endVisit(JCastOperation x, Mutator m) {
+  public void endVisit(JAssertStatement x, Context ctx) {
   }
 
-  public void endVisit(JCharLiteral x, Mutator m) {
+  public void endVisit(JBinaryOperation x, Context ctx) {
   }
 
-  public void endVisit(JClassLiteral x, Mutator m) {
+  public void endVisit(JBlock x, Context ctx) {
   }
 
-  public void endVisit(JClassSeed x, Mutator m) {
+  public void endVisit(JBooleanLiteral x, Context ctx) {
   }
 
-  public void endVisit(JClassType x) {
+  public void endVisit(JBreakStatement x, Context ctx) {
   }
 
-  public void endVisit(JConditional x, Mutator m) {
+  public void endVisit(JCaseStatement x, Context ctx) {
   }
 
-  public void endVisit(JContinueStatement x) {
+  public void endVisit(JCastOperation x, Context ctx) {
   }
 
-  public void endVisit(JDoStatement x) {
+  public void endVisit(JCharLiteral x, Context ctx) {
   }
 
-  public void endVisit(JDoubleLiteral x, Mutator m) {
+  public void endVisit(JClassLiteral x, Context ctx) {
   }
 
-  public void endVisit(JExpressionStatement x) {
+  public void endVisit(JClassSeed x, Context ctx) {
   }
 
-  public void endVisit(JField x) {
+  public void endVisit(JClassType x, Context ctx) {
   }
 
-  public void endVisit(JFieldRef x, Mutator m) {
+  public void endVisit(JConditional x, Context ctx) {
   }
 
-  public void endVisit(JFloatLiteral x, Mutator m) {
+  public void endVisit(JContinueStatement x, Context ctx) {
   }
 
-  public void endVisit(JForStatement x) {
+  public void endVisit(JDoStatement x, Context ctx) {
   }
 
-  public void endVisit(JIfStatement x) {
+  public void endVisit(JDoubleLiteral x, Context ctx) {
   }
 
-  public void endVisit(JInstanceOf x, Mutator m) {
+  public void endVisit(JExpressionStatement x, Context ctx) {
   }
 
-  public void endVisit(JInterfaceType x) {
+  public void endVisit(JField x, Context ctx) {
   }
 
-  public void endVisit(JIntLiteral x, Mutator m) {
+  public void endVisit(JFieldRef x, Context ctx) {
   }
 
-  public void endVisit(JLabel x) {
+  public void endVisit(JFloatLiteral x, Context ctx) {
   }
 
-  public void endVisit(JLabeledStatement x) {
+  public void endVisit(JForStatement x, Context ctx) {
   }
 
-  public void endVisit(JLocal x) {
+  public void endVisit(JIfStatement x, Context ctx) {
   }
 
-  public void endVisit(JLocalDeclarationStatement x) {
+  public void endVisit(JInstanceOf x, Context ctx) {
   }
 
-  public void endVisit(JLocalRef x, Mutator m) {
+  public void endVisit(JInterfaceType x, Context ctx) {
   }
 
-  public void endVisit(JLongLiteral x, Mutator m) {
+  public void endVisit(JIntLiteral x, Context ctx) {
   }
 
-  public void endVisit(JMethod x) {
+  public void endVisit(JLabel x, Context ctx) {
   }
 
-  public void endVisit(JMethodCall x, Mutator m) {
+  public void endVisit(JLabeledStatement x, Context ctx) {
   }
 
-  public void endVisit(JMultiExpression x, Mutator m) {
+  public void endVisit(JLocal x, Context ctx) {
   }
 
-  public void endVisit(JNewArray x, Mutator m) {
+  public void endVisit(JLocalDeclarationStatement x, Context ctx) {
   }
 
-  public void endVisit(JNewInstance x, Mutator m) {
+  public void endVisit(JLocalRef x, Context ctx) {
   }
 
-  public void endVisit(JNullLiteral x, Mutator m) {
+  public void endVisit(JLongLiteral x, Context ctx) {
   }
 
-  public void endVisit(JNullType x) {
+  public void endVisit(JMethod x, Context ctx) {
   }
 
-  public void endVisit(JParameter x) {
+  public void endVisit(JMethodCall x, Context ctx) {
   }
 
-  public void endVisit(JParameterRef x, Mutator m) {
+  public void endVisit(JMultiExpression x, Context ctx) {
   }
 
-  public void endVisit(JPostfixOperation x, Mutator m) {
+  public void endVisit(JNewArray x, Context ctx) {
   }
 
-  public void endVisit(JPrefixOperation x, Mutator m) {
+  public void endVisit(JNewInstance x, Context ctx) {
   }
 
-  public void endVisit(JPrimitiveType x) {
+  public void endVisit(JNullLiteral x, Context ctx) {
   }
 
-  public void endVisit(JProgram x) {
+  public void endVisit(JNullType x, Context ctx) {
   }
 
-  public void endVisit(JReturnStatement x) {
+  public void endVisit(JParameter x, Context ctx) {
   }
 
-  public void endVisit(JsniFieldRef x) {
+  public void endVisit(JParameterRef x, Context ctx) {
   }
 
-  public void endVisit(JsniMethod x) {
+  public void endVisit(JPostfixOperation x, Context ctx) {
   }
 
-  public void endVisit(JsniMethodRef x) {
+  public void endVisit(JPrefixOperation x, Context ctx) {
   }
 
-  public void endVisit(JsonArray x, Mutator m) {
+  public void endVisit(JPrimitiveType x, Context ctx) {
   }
 
-  public void endVisit(JsonObject x, Mutator m) {
+  public void endVisit(JProgram x, Context ctx) {
   }
 
-  public void endVisit(JsonPropInit x) {
+  public void endVisit(JReturnStatement x, Context ctx) {
   }
 
-  public void endVisit(JStringLiteral x, Mutator m) {
+  public void endVisit(JsniFieldRef x, Context ctx) {
   }
 
-  public void endVisit(JSwitchStatement x) {
+  public void endVisit(JsniMethod x, Context ctx) {
   }
 
-  public void endVisit(JThisRef x, Mutator m) {
+  public void endVisit(JsniMethodRef x, Context ctx) {
   }
 
-  public void endVisit(JThrowStatement x) {
+  public void endVisit(JsonArray x, Context ctx) {
   }
 
-  public void endVisit(JTryStatement x) {
+  public void endVisit(JsonObject x, Context ctx) {
   }
 
-  public void endVisit(JWhileStatement x) {
+  public void endVisit(JsonPropInit x, Context ctx) {
   }
 
-  public boolean visit(JAbsentArrayDimension x, Mutator m) {
+  public void endVisit(JStringLiteral x, Context ctx) {
+  }
+
+  public void endVisit(JSwitchStatement x, Context ctx) {
+  }
+
+  public void endVisit(JThisRef x, Context ctx) {
+  }
+
+  public void endVisit(JThrowStatement x, Context ctx) {
+  }
+
+  public void endVisit(JTryStatement x, Context ctx) {
+  }
+
+  public void endVisit(JWhileStatement x, Context ctx) {
+  }
+
+  public boolean visit(JAbsentArrayDimension x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JArrayRef x, Mutator m) {
+  public boolean visit(JArrayRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JArrayType x) {
+  public boolean visit(JArrayType x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JAssertStatement x) {
+  public boolean visit(JAssertStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JBinaryOperation x, Mutator m) {
+  public boolean visit(JBinaryOperation x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JBlock x) {
+  public boolean visit(JBlock x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JBooleanLiteral x, Mutator m) {
+  public boolean visit(JBooleanLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JBreakStatement x) {
+  public boolean visit(JBreakStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JCaseStatement x) {
+  public boolean visit(JCaseStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JCastOperation x, Mutator m) {
+  public boolean visit(JCastOperation x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JCharLiteral x, Mutator m) {
+  public boolean visit(JCharLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JClassLiteral x, Mutator m) {
+  public boolean visit(JClassLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JClassSeed x, Mutator m) {
+  public boolean visit(JClassSeed x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JClassType x) {
+  public boolean visit(JClassType x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JConditional x, Mutator m) {
+  public boolean visit(JConditional x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JContinueStatement x) {
+  public boolean visit(JContinueStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JDoStatement x) {
+  public boolean visit(JDoStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JDoubleLiteral x, Mutator m) {
+  public boolean visit(JDoubleLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JExpressionStatement x) {
+  public boolean visit(JExpressionStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JField x) {
+  public boolean visit(JField x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JFieldRef x, Mutator m) {
+  public boolean visit(JFieldRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JFloatLiteral x, Mutator m) {
+  public boolean visit(JFloatLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JForStatement x) {
+  public boolean visit(JForStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JIfStatement x) {
+  public boolean visit(JIfStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JInstanceOf x, Mutator m) {
+  public boolean visit(JInstanceOf x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JInterfaceType x) {
+  public boolean visit(JInterfaceType x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JIntLiteral x, Mutator m) {
+  public boolean visit(JIntLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JLabel x) {
+  public boolean visit(JLabel x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JLabeledStatement x) {
+  public boolean visit(JLabeledStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JLocal x) {
+  public boolean visit(JLocal x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JLocalDeclarationStatement x) {
+  public boolean visit(JLocalDeclarationStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JLocalRef x, Mutator m) {
+  public boolean visit(JLocalRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JLongLiteral x, Mutator m) {
+  public boolean visit(JLongLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JMethod x) {
+  public boolean visit(JMethod x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JMethodCall x, Mutator m) {
+  public boolean visit(JMethodCall x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JMultiExpression x, Mutator m) {
+  public boolean visit(JMultiExpression x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JNewArray x, Mutator m) {
+  public boolean visit(JNewArray x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JNewInstance x, Mutator m) {
+  public boolean visit(JNewInstance x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JNullLiteral x, Mutator m) {
+  public boolean visit(JNullLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JNullType x) {
+  public boolean visit(JNullType x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JParameter x) {
+  public boolean visit(JParameter x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JParameterRef x, Mutator m) {
+  public boolean visit(JParameterRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JPostfixOperation x, Mutator m) {
+  public boolean visit(JPostfixOperation x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JPrefixOperation x, Mutator m) {
+  public boolean visit(JPrefixOperation x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JPrimitiveType x) {
+  public boolean visit(JPrimitiveType x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JProgram x) {
+  public boolean visit(JProgram x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JReturnStatement x) {
+  public boolean visit(JReturnStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JsniFieldRef x) {
+  public boolean visit(JsniFieldRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JsniMethod x) {
+  public boolean visit(JsniMethod x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JsniMethodRef x) {
+  public boolean visit(JsniMethodRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JsonArray x, Mutator m) {
+  public boolean visit(JsonArray x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JsonObject x, Mutator m) {
+  public boolean visit(JsonObject x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JsonPropInit x) {
+  public boolean visit(JsonPropInit x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JStringLiteral x, Mutator m) {
+  public boolean visit(JStringLiteral x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JSwitchStatement x) {
+  public boolean visit(JSwitchStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JThisRef x, Mutator m) {
+  public boolean visit(JThisRef x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JThrowStatement x) {
+  public boolean visit(JThrowStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JTryStatement x) {
+  public boolean visit(JTryStatement x, Context ctx) {
     return true;
   }
 
-  public boolean visit(JWhileStatement x) {
+  public boolean visit(JWhileStatement x, Context ctx) {
     return true;
   }
 
+  protected JNode doAccept(JNode node) {
+    doTraverse(node, UNMODIFIABLE_CONTEXT);
+    return node;
+  }
+
+  protected void doAccept(List list) {
+    for (Iterator it = list.iterator(); it.hasNext();) {
+      doTraverse((JNode) it.next(), UNMODIFIABLE_CONTEXT);
+    }
+  }
+
+  protected void doAcceptWithInsertRemove(List list) {
+    for (Iterator it = list.iterator(); it.hasNext();) {
+      doTraverse((JNode) it.next(), UNMODIFIABLE_CONTEXT);
+    }
+  }
+
+  protected final void doTraverse(JNode node, Context ctx) {
+    // boolean trace = false;
+    // String before = null;
+    try {
+      // trace = !(this instanceof ToStringGenerationVisitor)
+      // && (node instanceof JTryStatement);
+      // if (trace) {
+      // before = node.toSource();
+      // }
+      node.traverse(this, ctx);
+      // if (trace) {
+      // String after = node.toSource();
+      // if (!before.equals(after)) {
+      // System.out.println(this.getClass().getName() + ":");
+      // System.out.println("--");
+      // System.out.println(before);
+      // System.out.println("VV");
+      // System.out.println(after);
+      // System.out.println("---------------------------------------------------------");
+      // }
+      // }
+    } catch (InternalCompilerException ice) {
+      ice.addNode(node);
+      throw ice;
+    } catch (Throwable e) {
+      // if (trace) {
+      // System.out.println(this.getClass().getName() + ":");
+      // System.out.println("--");
+      // System.out.println(before);
+      // System.out.println(e);
+      // }
+      InternalCompilerException ice = new InternalCompilerException(
+          "Unexpected error during visit.", e);
+      ice.addNode(node);
+      throw ice;
+    }
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JWhileStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JWhileStatement.java
index 646d0c1..39a11d6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JWhileStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JWhileStatement.java
@@ -20,27 +20,32 @@
  */
 public class JWhileStatement extends JStatement {
 
-  private final Holder testExpr = new Holder();
-  public JStatement body;
+  private JStatement body;
+  private JExpression testExpr;
 
-  public JWhileStatement(JProgram program, JExpression testExpr, JStatement body) {
-    super(program);
-    this.testExpr.set(testExpr);
+  public JWhileStatement(JProgram program, JSourceInfo info,
+      JExpression testExpr, JStatement body) {
+    super(program, info);
+    this.testExpr = testExpr;
     this.body = body;
   }
 
-  public JExpression getTestExpr() {
-    return testExpr.get();
+  public JStatement getBody() {
+    return body;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      testExpr.traverse(visitor);
+  public JExpression getTestExpr() {
+    return testExpr;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      testExpr = visitor.accept(testExpr);
       if (body != null) {
-        body.traverse(visitor);
+        body = visitor.accept(body);
       }
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddAll.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddAll.java
deleted file mode 100644
index 73cc64d..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddAll.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-
-import java.util.List;
-
-class AddAll/* <N extends JNode> */extends ChangeBase {
-
-  final List/* <N> */source;
-  final List/* <N> */list;
-  private final int index;
-
-  public AddAll(List/* <N> */source, int index, List/* <N> */list) {
-    this.source = source;
-    this.index = index;
-    this.list = list;
-  }
-
-  public void apply() {
-    if (index < 0) {
-      list.addAll(source);
-    } else {
-      list.addAll(index, source);
-    }
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    if (index < 0) {
-      logger.log(type, "Add a list to a list", null);
-    } else {
-      logger.log(type, "Add a list to a list at index " + index, null);
-    }
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddNode.java
deleted file mode 100644
index 509a382..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddNode.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.JNode;
-
-import java.util.List;
-
-class AddNode/* <N extends JNode> */extends ChangeBase {
-
-  final List/* <N> */list;
-  private final JNode node;
-  private final int index;
-
-  public AddNode(JNode node, int index, List/* <N> */list) {
-    this.node = node;
-    this.index = index;
-    this.list = list;
-    assert (!list.contains(node));
-  }
-
-  public void apply() {
-    if (index < 0) {
-      list.add(node);
-    } else {
-      list.add(index, node);
-    }
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    if (index < 0) {
-      logger.log(type, "Add " + ChangeList.getNodeString(node)
-          + ChangeList.getEnclosingTypeString(" to the end of", node), null);
-    } else {
-      logger.log(type, "Add " + ChangeList.getNodeString(node)
-          + ChangeList.getEnclosingTypeString(" to", node) + " at index "
-          + index, null);
-    }
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddNodeMutator.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddNodeMutator.java
deleted file mode 100644
index 4f06e28..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/AddNodeMutator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.Mutator;
-
-import java.util.List;
-
-class AddNodeMutator/* <N extends JNode> */extends ChangeBase {
-
-  final List/* <N> */list;
-  private final Mutator/* <N> */node;
-  private final int index;
-
-  public AddNodeMutator(Mutator/* <N> */node, int index, List/* <N> */list) {
-    this.node = node;
-    this.index = index;
-    this.list = list;
-    assert (!list.contains(node));
-  }
-
-  public void apply() {
-    assert (!list.contains(node));
-    if (index < 0) {
-      list.add(node.get());
-    } else {
-      list.add(index, node.get());
-    }
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    if (index < 0) {
-      logger.log(type, "Add the eventual value of "
-          + ChangeList.getNodeString(node.get()) + " to a list", null);
-    } else {
-      logger.log(type, "Add the eventual value of "
-          + ChangeList.getNodeString(node.get()) + " to a list at index "
-          + index, null);
-    }
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ChangeBase.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/ChangeBase.java
deleted file mode 100644
index 979bb92..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ChangeBase.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-abstract class ChangeBase implements Change {
-  
-  public String toString() {
-    StringWriter sw = new StringWriter();
-    PrintWriter pw = new PrintWriter(sw, true);
-    PrintWriterTreeLogger logger = new PrintWriterTreeLogger(pw);
-    logger.setMaxDetail(TreeLogger.INFO);
-    describe(logger, TreeLogger.INFO);
-    return sw.toString();
-  }
-  
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ChangeList.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/ChangeList.java
deleted file mode 100644
index dfea792..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ChangeList.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.CanBeSetFinal;
-import com.google.gwt.dev.jjs.ast.HasEnclosingType;
-import com.google.gwt.dev.jjs.ast.HasSettableType;
-import com.google.gwt.dev.jjs.ast.JBlock;
-import com.google.gwt.dev.jjs.ast.JExpression;
-import com.google.gwt.dev.jjs.ast.JField;
-import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JNode;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JStatement;
-import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.Mutator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manages a list of {@link Change} objects to apply to the AST.
- */
-public class ChangeList extends ChangeBase {
-
-  static String getEnclosingTypeString(String prefix, Object x) {
-    if (x instanceof HasEnclosingType) {
-      JReferenceType enclosingType = ((HasEnclosingType) x).getEnclosingType();
-      if (enclosingType != null) {
-        return prefix + " type '" + enclosingType.getName() + "'";
-      }
-    }
-    return "";
-  }
-
-  static String getNodeString(JNode x) {
-    if (x == null) {
-      return String.valueOf(x);
-    }
-    return x.getClass().getName() + " '" + x + "'";
-  }
-
-  private final List/* <Change> */changes = new ArrayList/* <Change> */();
-
-  private final String description;
-
-  public ChangeList(String description) {
-    this.description = description;
-  }
-
-  public void add(Change change) {
-    changes.add(change);
-  }
-
-  public/* <N extends JNode> */void addAll(List/* <N> */x, int index,
-      List/* <N> */list) {
-    AddAll change = new AddAll/* <N> */(x, index, list);
-    changes.add(change);
-  }
-
-  public void addExpression(JExpression x, List/* <JExpression> */list) {
-    addNode(x, -1, list);
-  }
-
-  public void addExpression(Mutator x, List/* <JExpression> */list) {
-    addNode(x, -1, list);
-  }
-
-  public void addMethod(JMethod x) {
-    addNode(x, -1, x.getEnclosingType().methods);
-  }
-
-  public/* <N extends JNode> */void addNode(JNode x, int index,
-      List/* <N> */list) {
-    AddNode change = new AddNode/* <N> */(x, index, list);
-    changes.add(change);
-  }
-
-  public/* <N extends JNode> */void addNode(Mutator/* <N> */x, int index,
-      List/* <N> */list) {
-    AddNodeMutator change = new AddNodeMutator/* <N> */(x, index, list);
-    changes.add(change);
-  }
-
-  public void addStatement(JStatement x, int index, JBlock body) {
-    addNode(x, index, body.statements);
-  }
-
-  public void addStatement(JStatement x, JBlock body) {
-    addNode(x, -1, body.statements);
-  }
-
-  public void apply() {
-    for (int i = 0; i < changes.size(); ++i) {
-      Change change = (Change) changes.get(i);
-      change.apply();
-    }
-  }
-
-  public void changeType(HasSettableType x, JType type) {
-    TypeChange change = new TypeChange(x, type);
-    changes.add(change);
-  }
-
-  public/* <N extends JNode> */void clear(List/* <N> */list) {
-    ClearList/* <N> */change = new ClearList/* <N> */(list);
-    changes.add(change);
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    TreeLogger branch = logger.branch(type, description, null);
-    for (int i = 0; i < changes.size(); ++i) {
-      Change change = (Change) changes.get(i);
-      change.describe(branch, type);
-    }
-  }
-
-  public boolean empty() {
-    return changes.size() == 0;
-  }
-
-  public void makeFinal(CanBeSetFinal x) {
-    MakeFinal change = new MakeFinal(x);
-    changes.add(change);
-  }
-
-  public void moveBody(JMethod sourceMethod, JMethod targetMethod) {
-    JBlock source = sourceMethod.body;
-    JBlock target = targetMethod.body;
-    MoveBlock change = new MoveBlock(source, target);
-    changes.add(change);
-  }
-
-  public void removeField(JField x) {
-    removeNode(x, x.getEnclosingType().fields);
-  }
-
-  public void removeMethod(JMethod x) {
-    removeNode(x, x.getEnclosingType().methods);
-  }
-
-  public/* <N extends JNode> */void removeNode(JNode x, List/* <N> */list) {
-    RemoveNode/* <N> */change = new RemoveNode/* <N> */(x, list);
-    changes.add(change);
-  }
-
-  public/* <N extends JNode> */void removeNode(Mutator/* <N> */x,
-      List/* <N> */list) {
-    RemoveNodeMutator/* <N> */change = new RemoveNodeMutator/* <N> */(x, list);
-    changes.add(change);
-  }
-
-  public void removeType(JReferenceType x) {
-    removeNode(x, x.getProgram().getDeclaredTypes());
-  }
-
-  public void replaceExpression(Mutator original, JExpression replace) {
-    ReplaceNode change = new ReplaceNode/* <JExpression> */(original, replace);
-    changes.add(change);
-  }
-
-  public void replaceExpression(Mutator original, Mutator replace) {
-    ReplaceNodeMutator change = new ReplaceNodeMutator(original, replace);
-    changes.add(change);
-  }
-
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ClearList.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/ClearList.java
deleted file mode 100644
index 3172358..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ClearList.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-
-import java.util.List;
-
-class ClearList/* <N extends JNode> */extends ChangeBase {
-
-  private final List/* <N> */list;
-
-  public ClearList(List/* <N> */list) {
-    this.list = list;
-  }
-
-  public void apply() {
-    list.clear();
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Clear a list", null);
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/MakeFinal.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/MakeFinal.java
deleted file mode 100644
index 679a9a7..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/MakeFinal.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.CanBeSetFinal;
-import com.google.gwt.dev.jjs.ast.JNode;
-
-class MakeFinal implements Change {
-  private final CanBeSetFinal x;
-
-  public MakeFinal(CanBeSetFinal x) {
-    this.x = x;
-  }
-
-  public void apply() {
-    x.setFinal(true);
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Make final " + ChangeList.getNodeString((JNode) x)
-        + ChangeList.getEnclosingTypeString(" from", x), null);
-  }
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/MoveBlock.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/MoveBlock.java
deleted file mode 100644
index 9e92dff..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/MoveBlock.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.JBlock;
-
-class MoveBlock implements Change {
-  final JBlock source;
-  final JBlock target;
-
-  public MoveBlock(JBlock source, JBlock target) {
-    super();
-    this.source = source;
-    this.target = target;
-  }
-
-  public void apply() {
-    assert (target.statements.size() == 0);
-    target.statements.addAll(source.statements);
-    source.statements.clear();
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Move the body of " + ChangeList.getNodeString(source)
-        + " to " + ChangeList.getNodeString(target), null);
-  }
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/RemoveNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/RemoveNode.java
deleted file mode 100644
index 7d73e7e..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/RemoveNode.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.JNode;
-
-import java.util.List;
-
-class RemoveNode/* <N extends JNode> */extends ChangeBase {
-
-  final List/* <N> */list;
-  private final JNode node;
-
-  public RemoveNode(JNode node, List/* <N> */list) {
-    this.node = node;
-    this.list = list;
-    assert (list.contains(node));
-  }
-
-  public void apply() {
-    boolean removed = list.remove(node);
-    assert (removed);
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Remove " + ChangeList.getNodeString(node)
-        + ChangeList.getEnclosingTypeString(" from", node), null);
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/RemoveNodeMutator.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/RemoveNodeMutator.java
deleted file mode 100644
index afa7e7d..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/RemoveNodeMutator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.Mutator;
-
-import java.util.List;
-
-class RemoveNodeMutator/* <N extends JNode> */extends ChangeBase {
-
-  final List/* <N> */list;
-  private final Mutator/* <N> */node;
-
-  public RemoveNodeMutator(Mutator/* <N> */node, List/* <N> */list) {
-    this.node = node;
-    this.list = list;
-    assert (list.contains(node.get()));
-  }
-
-  public void apply() {
-    boolean removed = list.remove(node.get());
-    assert (removed);
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Remove " + ChangeList.getNodeString(node.get())
-        + ChangeList.getEnclosingTypeString(" from", node.get()), null);
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ReplaceNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/ReplaceNode.java
deleted file mode 100644
index fc0100a..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ReplaceNode.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.JExpression;
-import com.google.gwt.dev.jjs.ast.Mutator;
-
-class ReplaceNode/* <N extends JNode> */extends ChangeBase {
-
-  final Mutator/* <N> */original;
-  final JExpression replace;
-
-  public ReplaceNode(Mutator/* <N> */original, JExpression replace) {
-    this.original = original;
-    this.replace = replace;
-  }
-
-  public void apply() {
-    original.set(replace);
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Replace " + ChangeList.getNodeString(original.get())
-        + " with " + ChangeList.getNodeString(replace), null);
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ReplaceNodeMutator.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/ReplaceNodeMutator.java
deleted file mode 100644
index 70f0668..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/ReplaceNodeMutator.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.Mutator;
-
-class ReplaceNodeMutator/* <N extends JNode> */extends ChangeBase {
-
-  final Mutator/* <N> */original;
-  final Mutator/* <N> */replace;
-
-  public ReplaceNodeMutator(Mutator/* <N> */original, Mutator/* <N> */replace) {
-    this.original = original;
-    this.replace = replace;
-  }
-
-  public void apply() {
-    original.set(replace.get());
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Replace " + ChangeList.getNodeString(original.get())
-        + " with " + ChangeList.getNodeString(replace.get()), null);
-  }
-
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/TypeChange.java b/dev/core/src/com/google/gwt/dev/jjs/ast/change/TypeChange.java
deleted file mode 100644
index d6f8812..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/TypeChange.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.HasSettableType;
-import com.google.gwt.dev.jjs.ast.JNode;
-import com.google.gwt.dev.jjs.ast.JType;
-
-/**
- * Changes the <code>JType</code> of a <code>JNode</code>.
- */
-public class TypeChange extends ChangeBase {
-
-  final JType targetType;
-  private final HasSettableType node;
-  private final JType oldType;
-
-  public TypeChange(HasSettableType node, JType targetType) {
-    this.node = node;
-    this.oldType = node.getType();
-    this.targetType = targetType;
-  }
-
-  public void apply() {
-    assert (oldType == node.getType());
-    node.setType(targetType);
-  }
-
-  public void describe(TreeLogger logger, TreeLogger.Type type) {
-    logger.log(type, "Change type of " + ChangeList.getNodeString((JNode) node)
-        + " from " + ChangeList.getNodeString(node.getType()) + " to "
-        + ChangeList.getNodeString(targetType), null);
-  }
-
-}
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 6571691..cc02da5 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
@@ -15,12 +15,12 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JLiteral;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
 
 /**
  * An AST node representing a class's constructor function. Only used by
@@ -31,24 +31,24 @@
   /**
    * The class being referred to.
    */
-  public final JClassType refType;
+  private final JClassType refType;
 
   public JClassSeed(JProgram program, JClassType type) {
     super(program);
     refType = type;
   }
 
+  public JClassType getRefType() {
+    return refType;
+  }
+
   public JType getType() {
     return program.getTypeJavaLangObject();
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JMultiExpression.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JMultiExpression.java
index c167090..dd93820 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JMultiExpression.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JMultiExpression.java
@@ -15,22 +15,24 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
-import com.google.gwt.dev.jjs.ast.HolderList;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
+
+import java.util.ArrayList;
 
 /**
  * Represents multiple ordered expressions as a single compound expression.
  */
 public class JMultiExpression extends JExpression {
 
-  public HolderList exprs = new HolderList();
+  public ArrayList exprs = new ArrayList();
 
-  public JMultiExpression(JProgram program) {
-    super(program);
+  public JMultiExpression(JProgram program, JSourceInfo info) {
+    super(program, info);
   }
 
   public JType getType() {
@@ -38,14 +40,13 @@
     if (c == 0) {
       return program.getTypeVoid();
     } else {
-      return exprs.getExpr(c - 1).getType();
+      return ((JExpression) exprs.get(c - 1)).getType();
     }
   }
 
   public boolean hasSideEffects() {
-
     for (int i = 0; i < exprs.size(); ++i) {
-      JExpression expr = exprs.getExpr(i);
+      JExpression expr = (JExpression) exprs.get(i);
       if (expr.hasSideEffects()) {
         return true;
       }
@@ -53,15 +54,11 @@
     return false;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      exprs.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(exprs);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
index 2675676..32764df 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
@@ -15,25 +15,27 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 
 /**
  * JSNI reference to a Java field.
  */
 public class JsniFieldRef extends JFieldRef {
 
-  public JsniFieldRef(JProgram program, JField field,
-      JReferenceType enclosingType) {
-    super(program, null, field, enclosingType);
+  public JsniFieldRef(JProgram program, JSourceInfo info,
+      JField field, JReferenceType enclosingType) {
+    super(program, info, null, field, enclosingType);
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethod.java
index 1c8a700..17b393c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethod.java
@@ -15,12 +15,13 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 import com.google.gwt.dev.js.ast.JsFunction;
 
 import java.util.ArrayList;
@@ -35,11 +36,11 @@
   public final List/* <JsniMethodRef> */jsniMethodRefs = new ArrayList/* <JsniMethodRef> */();
   private JsFunction jsFunction = null;
 
-  public JsniMethod(JProgram program, String name,
-      JReferenceType enclosingType, JType returnType, boolean isStatic,
-      boolean isFinal, boolean isPrivate) {
-    super(program, name, enclosingType, returnType, false, isStatic, isFinal,
-        isPrivate);
+  public JsniMethod(JProgram program, JSourceInfo info,
+      String name, JReferenceType enclosingType, JType returnType,
+      boolean isStatic, boolean isFinal, boolean isPrivate) {
+    super(program, info, name, enclosingType, returnType, false, isStatic,
+        isFinal, isPrivate);
   }
 
   public JsFunction getFunc() {
@@ -56,22 +57,13 @@
     this.jsFunction = jsFunction;
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
-      for (int i = 0; i < params.size(); ++i) {
-        JParameter param = (JParameter) params.get(i);
-        param.traverse(visitor);
-      }
-      for (int i = 0; i < jsniFieldRefs.size(); ++i) {
-        JsniFieldRef fieldRef = (JsniFieldRef) jsniFieldRefs.get(i);
-        fieldRef.traverse(visitor);
-      }
-      for (int i = 0; i < jsniMethodRefs.size(); ++i) {
-        JsniMethodRef methodRef = (JsniMethodRef) jsniMethodRefs.get(i);
-        methodRef.traverse(visitor);
-      }
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(params);
+      visitor.accept(jsniFieldRefs);
+      visitor.accept(jsniMethodRefs);
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java
index f479a99..b5c20b8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java
@@ -15,23 +15,25 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 
 /**
  * A call to a JSNI method.
  */
 public class JsniMethodRef extends JMethodCall {
 
-  public JsniMethodRef(JProgram program, JMethod method) {
-    super(program, null, method);
+  public JsniMethodRef(JProgram program, JSourceInfo info, JMethod method) {
+    super(program, info, null, method);
   }
 
-  public void traverse(JVisitor visitor) {
-    if (visitor.visit(this)) {
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
     }
-    visitor.endVisit(this);
+    visitor.endVisit(this, ctx);
   }
 }
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 36f731a..6eae09a 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
@@ -15,41 +15,41 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
-import com.google.gwt.dev.jjs.ast.HolderList;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
+
+import java.util.ArrayList;
 
 /**
  * A JSON-style list of JS expressions.
  */
 public class JsonArray extends JExpression {
 
-  public HolderList exprs = new HolderList();
+  public ArrayList exprs = new ArrayList();
 
   public JsonArray(JProgram program) {
-    super(program);
+    super(program, null);
   }
 
   public JType getType() {
-    return program.getTypeVoid();
+    // If JavaScriptObject type is not available, just return the Object type
+    JClassType jsoType = program.getSpecialJavaScriptObject();
+    return (jsoType != null) ? jsoType : program.getTypeJavaLangObject();
   }
 
   public boolean hasSideEffects() {
     return true;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      exprs.traverse(visitor);
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(exprs);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 e514c33..08ef343 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
@@ -15,13 +15,12 @@
  */
 package com.google.gwt.dev.jjs.ast.js;
 
-import com.google.gwt.dev.jjs.ast.Holder;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,29 +35,29 @@
    */
   public static class JsonPropInit extends JNode {
 
-    public final Holder labelExpr = new Holder();
-    public final Holder valueExpr = new Holder();
+    public JExpression labelExpr;
+    public JExpression valueExpr;
 
     public JsonPropInit(JProgram program, JExpression labelExpr,
         JExpression valueExpr) {
-      super(program);
-      this.labelExpr.set(labelExpr);
-      this.valueExpr.set(valueExpr);
+      super(program, null);
+      this.labelExpr = labelExpr;
+      this.valueExpr = valueExpr;
     }
 
-    public void traverse(JVisitor visitor) {
-      if (visitor.visit(this)) {
-        labelExpr.traverse(visitor);
-        valueExpr.traverse(visitor);
+    public void traverse(JVisitor visitor, Context ctx) {
+      if (visitor.visit(this, ctx)) {
+        labelExpr = visitor.accept(labelExpr);
+        valueExpr = visitor.accept(valueExpr);
       }
-      visitor.endVisit(this);
+      visitor.endVisit(this, ctx);
     }
   }
 
   public final List/* <JsonPropInit> */propInits = new ArrayList/* <JsonPropInit> */();
 
   public JsonObject(JProgram program) {
-    super(program);
+    super(program, null);
   }
 
   public JType getType() {
@@ -69,18 +68,11 @@
     return true;
   }
 
-  public void traverse(JVisitor visitor) {
-    traverse(visitor, null);
-  }
-
-  public void traverse(JVisitor visitor, Mutator mutator) {
-    if (visitor.visit(this, mutator)) {
-      for (int i = 0; i < propInits.size(); ++i) {
-        JsonPropInit propInit = (JsonPropInit) propInits.get(i);
-        propInit.traverse(visitor);
-      }
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      visitor.accept(propInits);
     }
-    visitor.endVisit(this, mutator);
+    visitor.endVisit(this, ctx);
   }
 
 }
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 fc5e56e..6663635 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,22 +15,22 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNewArray;
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 import com.google.gwt.dev.jjs.ast.js.JsonArray;
 
 /**
@@ -40,34 +40,15 @@
  */
 public class ArrayNormalizer {
 
-  private class ArrayVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Transform array accesses to check bounds.");
+  private class ArrayVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JNewArray x, Mutator m) {
-      JArrayType type = x.getArrayType();
-      JLiteral litTypeName = program.getLiteralString(calcClassName(type));
-
-      if (x.initializers != null) {
-        processInitializers(x, m, type, litTypeName);
-      } else {
-        processDims(x, m, type, litTypeName);
-      }
-    }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    // @Override
-    public boolean visit(JBinaryOperation x, Mutator m) {
-      if (x.op == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
+      if (x.getOp() == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
         JArrayRef arrayRef = (JArrayRef) x.getLhs();
         if (arrayRef.getType() instanceof JNullType) {
           // will generate a null pointer exception instead
-          return false;
+          return;
         }
         JArrayType arrayType = (JArrayType) arrayRef.getInstance().getType();
         JType elementType = arrayType.getElementType();
@@ -78,23 +59,26 @@
             && !((JReferenceType) elementType).isFinal()) {
           // replace this assignment with a call to setCheck()
 
-          // DON'T VISIT ARRAYREF, but do visit its children
-          arrayRef.instance.traverse(this);
-          arrayRef.indexExpr.traverse(this);
-          x.rhs.traverse(this);
-
-          JMethodCall call = new JMethodCall(program, null, setCheckMethod);
-          ChangeList myChanges = new ChangeList("Replace " + x
-              + " with a call to Array.setCheck()");
-          myChanges.replaceExpression(m, call);
-          myChanges.addExpression(arrayRef.instance, call.args);
-          myChanges.addExpression(arrayRef.indexExpr, call.args);
-          myChanges.addExpression(x.rhs, call.args);
-          changeList.add(myChanges);
-          return false;
+          JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+              setCheckMethod);
+          call.getArgs().add(arrayRef.getInstance());
+          call.getArgs().add(arrayRef.getIndexExpr());
+          call.getArgs().add(x.getRhs());
+          ctx.replaceMe(call);
         }
       }
-      return true;
+    }
+
+    // @Override
+    public void endVisit(JNewArray x, Context ctx) {
+      JArrayType type = x.getArrayType();
+      JLiteral litTypeName = program.getLiteralString(calcClassName(type));
+
+      if (x.initializers != null) {
+        processInitializers(x, ctx, type, litTypeName);
+      } else {
+        processDims(x, ctx, type, litTypeName);
+      }
     }
 
     private char[] calcClassName(JArrayType type) {
@@ -111,20 +95,19 @@
       return className;
     }
 
-    private void processDims(JNewArray x, Mutator m, JArrayType arrayType,
+    private void processDims(JNewArray x, Context ctx, JArrayType arrayType,
         JLiteral litTypeName) {
-      ChangeList myChanges = new ChangeList("Replace " + x
-          + " with a call to Array.initDims()");
       // override the type of the called method with the array's type
-      JMethodCall call = new JMethodCall(program, null, initDims, arrayType);
+      JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+          initDims, arrayType);
       JsonArray typeIdList = new JsonArray(program);
       JsonArray queryIdList = new JsonArray(program);
       JsonArray dimList = new JsonArray(program);
       JType leafType = arrayType.getLeafType();
-      int outstandingDims = arrayType.dims;
+      int outstandingDims = arrayType.getDims();
       for (int i = 0; i < x.dims.size(); ++i) {
-        Mutator dim = x.dims.getMutator(i);
-        if (dim.get() instanceof JAbsentArrayDimension) {
+        JExpression dim = (JExpression) x.dims.get(i);
+        if (dim instanceof JAbsentArrayDimension) {
           break;
         }
 
@@ -141,44 +124,40 @@
          */
         JArrayType cur = program.getTypeArray(leafType, outstandingDims--);
         JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(cur));
-        myChanges.addExpression(typeIdLit, typeIdList.exprs);
+        typeIdList.exprs.add(typeIdLit);
         JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(cur));
-        myChanges.addExpression(queryIdLit, queryIdList.exprs);
-        myChanges.addExpression(dim, dimList.exprs);
+        queryIdList.exprs.add(queryIdLit);
+        dimList.exprs.add(dim);
       }
       JType targetType = leafType;
       if (outstandingDims > 0) {
         targetType = program.getTypeArray(targetType, outstandingDims);
       }
 
-      myChanges.addExpression(litTypeName, call.args);
-      myChanges.addExpression(typeIdList, call.args);
-      myChanges.addExpression(queryIdList, call.args);
-      myChanges.addExpression(dimList, call.args);
-      myChanges.addExpression(targetType.getDefaultValue(), call.args);
-      myChanges.replaceExpression(m, call);
-      changeList.add(myChanges);
+      call.getArgs().add(litTypeName);
+      call.getArgs().add(typeIdList);
+      call.getArgs().add(queryIdList);
+      call.getArgs().add(dimList);
+      call.getArgs().add(targetType.getDefaultValue());
+      ctx.replaceMe(call);
     }
 
-    private void processInitializers(JNewArray x, Mutator m,
+    private void processInitializers(JNewArray x, Context ctx,
         JArrayType arrayType, JLiteral litTypeName) {
       // override the type of the called method with the array's type
-      JMethodCall call = new JMethodCall(program, null, initValues, arrayType);
+      JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+          initValues, arrayType);
       JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
       JsonArray initList = new JsonArray(program);
-      ChangeList myChanges = new ChangeList("Replace " + x
-          + " with a call to Array.initValues()");
       for (int i = 0; i < x.initializers.size(); ++i) {
-        Mutator initializer = x.initializers.getMutator(i);
-        myChanges.addExpression(initializer, initList.exprs);
+        initList.exprs.add(x.initializers.get(i));
       }
-      myChanges.addExpression(litTypeName, call.args);
-      myChanges.addExpression(typeIdLit, call.args);
-      myChanges.addExpression(queryIdLit, call.args);
-      myChanges.addExpression(initList, call.args);
-      myChanges.replaceExpression(m, call);
-      changeList.add(myChanges);
+      call.getArgs().add(litTypeName);
+      call.getArgs().add(typeIdLit);
+      call.getArgs().add(queryIdLit);
+      call.getArgs().add(initList);
+      ctx.replaceMe(call);
     }
 
     private int tryGetQueryId(JArrayType type) {
@@ -195,13 +174,10 @@
     new ArrayNormalizer(program).execImpl();
   }
 
-  private final JMethod setCheckMethod;
-
   private final JMethod initDims;
-
   private final JMethod initValues;
-
   private final JProgram program;
+  private final JMethod setCheckMethod;
 
   private ArrayNormalizer(JProgram program) {
     this.program = program;
@@ -212,11 +188,7 @@
 
   private void execImpl() {
     ArrayVisitor visitor = new ArrayVisitor();
-    program.traverse(visitor);
-    ChangeList changes = visitor.getChangeList();
-    if (!changes.empty()) {
-      changes.apply();
-    }
+    visitor.accept(program);
   }
 
 }
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 326fb6a..7f5dd22 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
@@ -23,6 +23,7 @@
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
 import com.google.gwt.dev.js.JsParser;
@@ -34,6 +35,7 @@
 import com.google.gwt.dev.js.ast.JsStatements;
 
 import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.Argument;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -41,6 +43,7 @@
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.env.IGenericType;
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
@@ -55,6 +58,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
+import org.eclipse.jdt.internal.compiler.problem.ProblemHandler;
 
 import java.io.IOException;
 import java.io.StringReader;
@@ -89,11 +93,25 @@
    */
   private static class BuildDeclMapVisitor extends ASTVisitor {
 
-    private ArrayList/* <TypeDeclaration> */typeDecls = new ArrayList/* <TypeDeclaration> */();
-    private final TypeMap typeMap;
-    private JProgram program;
-    private final JsProgram jsProgram;
+    private static JSourceInfo makeSourceInfo(
+        AbstractMethodDeclaration methodDecl) {
+      CompilationResult compResult = methodDecl.compilationResult;
+      int[] indexes = compResult.lineSeparatorPositions;
+      String fileName = String.valueOf(compResult.fileName);
+      int startLine = ProblemHandler.searchLineNumber(indexes,
+          methodDecl.sourceStart);
+      return new JSourceInfo(methodDecl.sourceStart, methodDecl.bodyEnd,
+          startLine, fileName);
+    }
+
+    private String currentFileName;
+    private int[] currentSeparatorPositions;
     private final JsParser jsParser = new JsParser();
+    private final JsProgram jsProgram;
+    private JProgram program;
+    private ArrayList/* <TypeDeclaration> */typeDecls = new ArrayList/* <TypeDeclaration> */();
+
+    private final TypeMap typeMap;
 
     public BuildDeclMapVisitor(TypeMap typeMap, JsProgram jsProgram) {
       this.typeMap = typeMap;
@@ -110,10 +128,11 @@
         return true;
       }
 
+      JSourceInfo info = makeInfo(argument);
       LocalVariableBinding b = argument.binding;
       JType localType = (JType) typeMap.get(b.type);
       JMethod enclosingMethod = findEnclosingMethod(scope);
-      JLocal newLocal = program.createLocal(argument.name, localType,
+      JLocal newLocal = program.createLocal(info, argument.name, localType,
           b.isFinal(), enclosingMethod);
       typeMap.put(b, newLocal);
       return true;
@@ -130,7 +149,8 @@
       MethodBinding b = ctorDecl.binding;
       JClassType enclosingType = (JClassType) typeMap.get(scope.enclosingSourceType());
       String name = enclosingType.getShortName();
-      JMethod newMethod = program.createMethod(name.toCharArray(),
+      JSourceInfo info = makeSourceInfo(ctorDecl);
+      JMethod newMethod = program.createMethod(info, name.toCharArray(),
           enclosingType, enclosingType, false, false, true, b.isPrivate(),
           false);
       mapThrownExceptions(newMethod, ctorDecl);
@@ -182,8 +202,10 @@
 
     public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
       FieldBinding b = fieldDeclaration.binding;
+      JSourceInfo info = makeInfo(fieldDeclaration);
       JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
-      createField(b, enclosingType, fieldDeclaration.initialization != null);
+      createField(info, b, enclosingType,
+          fieldDeclaration.initialization != null);
       return true;
     }
 
@@ -191,19 +213,22 @@
       LocalVariableBinding b = localDeclaration.binding;
       JType localType = (JType) typeMap.get(localDeclaration.type.resolvedType);
       JMethod enclosingMethod = findEnclosingMethod(scope);
-      JLocal newLocal = program.createLocal(localDeclaration.name, localType,
-          b.isFinal(), enclosingMethod);
+      JSourceInfo info = makeInfo(localDeclaration);
+      JLocal newLocal = program.createLocal(info, localDeclaration.name,
+          localType, b.isFinal(), enclosingMethod);
       typeMap.put(b, newLocal);
       return true;
     }
 
     public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
       MethodBinding b = methodDeclaration.binding;
+      JSourceInfo info = makeSourceInfo(methodDeclaration);
       JType returnType = (JType) typeMap.get(methodDeclaration.returnType.resolvedType);
       JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
-      JMethod newMethod = program.createMethod(methodDeclaration.selector,
-          enclosingType, returnType, b.isAbstract(), b.isStatic(), b.isFinal(),
-          b.isPrivate(), b.isNative());
+      JMethod newMethod = program.createMethod(info,
+          methodDeclaration.selector, enclosingType, returnType,
+          b.isAbstract(), b.isStatic(), b.isFinal(), b.isPrivate(),
+          b.isNative());
 
       mapThrownExceptions(newMethod, methodDeclaration);
       mapParameters(newMethod, methodDeclaration);
@@ -218,23 +243,21 @@
         int endPos = jsniCode.lastIndexOf("}-*/");
         if (startPos < 0 && endPos < 0) {
           GenerateJavaAST.reportJsniError(
+              info,
               methodDeclaration,
-              "Native methods require a JavaScript implementation enclosed with /*-{ and }-*/",
-              0);
+              "Native methods require a JavaScript implementation enclosed with /*-{ and }-*/");
           return true;
         }
         if (startPos < 0) {
-          GenerateJavaAST.reportJsniError(
-              methodDeclaration,
-              "Unable to find start of native block; begin your JavaScript block with: /*-{",
-              0);
+          GenerateJavaAST.reportJsniError(info, methodDeclaration,
+              "Unable to find start of native block; begin your JavaScript block with: /*-{");
           return true;
         }
         if (endPos < 0) {
           GenerateJavaAST.reportJsniError(
+              info,
               methodDeclaration,
-              "Unable to find end of native block; terminate your JavaScript block with: }-*/",
-              0);
+              "Unable to find end of native block; terminate your JavaScript block with: }-*/");
           return true;
         }
 
@@ -261,6 +284,7 @@
         StringReader sr = new StringReader(syntheticFnHeader + '\n' + jsniCode);
         try {
           // start at -1 to avoid counting our synthetic header
+          // TODO: get the character position start correct
           JsStatements result = jsParser.parse(jsProgram.getScope(), sr, -1);
           JsExprStmt jsExprStmt = (JsExprStmt) result.get(0);
           JsFunction jsFunction = (JsFunction) jsExprStmt.getExpression();
@@ -270,7 +294,6 @@
               "Internal error parsing JSNI in method '" + newMethod
                   + "' in type '" + enclosingType.getName() + "'", e);
         } catch (JsParserException e) {
-
           /*
            * count the number of characters to the problem (from the start of
            * the JSNI code)
@@ -297,10 +320,13 @@
             // CHECKSTYLE_ON
           }
 
+          // TODO: check this
           // Map into the original source stream;
           i += startPos + detail.getLineOffset();
-          String message = e.getMessage();
-          GenerateJavaAST.reportJsniError(methodDeclaration, message, i);
+          info = new JSourceInfo(i, i, info.getStartLine() + detail.getLine(),
+              info.getFileName());
+          GenerateJavaAST.reportJsniError(info, methodDeclaration,
+              e.getMessage());
         }
       }
 
@@ -320,11 +346,11 @@
       return process(typeDeclaration);
     }
 
-    private JField createField(FieldBinding binding,
+    private JField createField(JSourceInfo info, FieldBinding binding,
         JReferenceType enclosingType, boolean hasInitializer) {
       JType type = (JType) typeMap.get(binding.type);
-      JField field = program.createField(binding.name, enclosingType, type,
-          binding.isStatic(), binding.isFinal(), hasInitializer);
+      JField field = program.createField(info, binding.name, enclosingType,
+          type, binding.isStatic(), binding.isFinal(), hasInitializer);
       typeMap.put(binding, field);
       return field;
     }
@@ -332,8 +358,8 @@
     private JField createField(SyntheticArgumentBinding binding,
         JReferenceType enclosingType) {
       JType type = (JType) typeMap.get(binding.type);
-      JField field = program.createField(binding.name, enclosingType, type,
-          false, true, true);
+      JField field = program.createField(null, binding.name, enclosingType,
+          type, false, true, true);
       if (binding.matchingField != null) {
         typeMap.put(binding.matchingField, field);
       }
@@ -344,7 +370,8 @@
     private JParameter createParameter(LocalVariableBinding binding,
         JMethod enclosingMethod) {
       JType type = (JType) typeMap.get(binding.type);
-      JParameter param = program.createParameter(binding.name, type,
+      JSourceInfo info = makeInfo(binding.declaration);
+      JParameter param = program.createParameter(info, binding.name, type,
           binding.isFinal(), enclosingMethod);
       typeMap.put(binding, param);
       return param;
@@ -353,8 +380,8 @@
     private JParameter createParameter(SyntheticArgumentBinding arg,
         String argName, JMethod enclosingMethod) {
       JType type = (JType) typeMap.get(arg.type);
-      JParameter param = program.createParameter(argName.toCharArray(), type,
-          true, enclosingMethod);
+      JParameter param = program.createParameter(null, argName.toCharArray(),
+          type, true, enclosingMethod);
       return param;
     }
 
@@ -376,6 +403,13 @@
       return (JMethod) typeMap.get(referenceMethod.binding);
     }
 
+    private JSourceInfo makeInfo(Statement stmt) {
+      int startLine = ProblemHandler.searchLineNumber(
+          currentSeparatorPositions, stmt.sourceStart);
+      return new JSourceInfo(stmt.sourceStart, stmt.sourceEnd, startLine,
+          currentFileName);
+    }
+
     private void mapParameters(JMethod method, AbstractMethodDeclaration x) {
       MethodBinding b = x.binding;
       int paramCount = (b.parameters != null ? b.parameters.length : 0);
@@ -409,6 +443,9 @@
      * implements the stuff under the hood anyway.
      */
     private boolean process(TypeDeclaration typeDeclaration) {
+      CompilationResult compResult = typeDeclaration.compilationResult;
+      currentSeparatorPositions = compResult.lineSeparatorPositions;
+      currentFileName = String.valueOf(compResult.fileName);
       SourceTypeBinding binding = typeDeclaration.binding;
       if (binding.constantPoolName() == null) {
         /*
@@ -467,8 +504,18 @@
    */
   private static class BuildTypeMapVisitor extends ASTVisitor {
 
-    private final TypeMap typeMap;
+    private static JSourceInfo makeSourceInfo(TypeDeclaration typeDecl) {
+      CompilationResult compResult = typeDecl.compilationResult;
+      int[] indexes = compResult.lineSeparatorPositions;
+      String fileName = String.valueOf(compResult.fileName);
+      int startLine = ProblemHandler.searchLineNumber(indexes,
+          typeDecl.sourceStart);
+      return new JSourceInfo(typeDecl.sourceStart, typeDecl.bodyEnd, startLine,
+          fileName);
+    }
+
     private final JProgram program;
+    private final TypeMap typeMap;
 
     public BuildTypeMapVisitor(TypeMap typeMap) {
       this.typeMap = typeMap;
@@ -511,12 +558,13 @@
         name[0] = localName;
       }
 
+      JSourceInfo info = makeSourceInfo(typeDeclaration);
       JReferenceType newType;
       if (binding.isClass()) {
-        newType = program.createClass(name, binding.isAbstract(),
+        newType = program.createClass(info, name, binding.isAbstract(),
             binding.isFinal());
       } else if (binding.isInterface()) {
-        newType = program.createInterface(name);
+        newType = program.createInterface(info, name);
       } else {
         assert (false);
         return false;
@@ -528,13 +576,13 @@
        * like output JavaScript. Clinit is always in slot 0, init (if it exists)
        * is always in slot 1.
        */
-      JMethod clinit = program.createMethod("$clinit".toCharArray(), newType,
-          program.getTypeVoid(), false, true, true, true, false);
+      JMethod clinit = program.createMethod(null, "$clinit".toCharArray(),
+          newType, program.getTypeVoid(), false, true, true, true, false);
       clinit.freezeParamTypes();
 
       if (newType instanceof JClassType) {
-        JMethod init = program.createMethod("$init".toCharArray(), newType,
-            program.getTypeVoid(), false, false, true, true, false);
+        JMethod init = program.createMethod(null, "$init".toCharArray(),
+            newType, program.getTypeVoid(), false, false, true, true, false);
         init.freezeParamTypes();
       }
 
@@ -569,4 +617,5 @@
       unitDecls[i].traverse(v1, unitDecls[i].scope);
     }
   }
+
 }
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 2102401..70d4589 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.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
@@ -26,6 +27,7 @@
 import com.google.gwt.dev.jjs.ast.JIntLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
@@ -34,8 +36,6 @@
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JTypeOracle;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 import com.google.gwt.dev.jjs.ast.js.JClassSeed;
 import com.google.gwt.dev.jjs.ast.js.JsonObject;
 import com.google.gwt.dev.jjs.ast.js.JsonObject.JsonPropInit;
@@ -62,11 +62,8 @@
     Set/* <JClassType> */alreadyRan = new HashSet/* <JClassType> */();
     private Map/* <JReferenceType, Set<JReferenceType>> */queriedTypes = new IdentityHashMap();
     private int nextQueryId = 1; // 0 is reserved
-
     private final List/* <JArrayType> */instantiatedArrayTypes = new ArrayList/* <JArrayType> */();
-
     private List/* <JClassType> */classes = new ArrayList/* <JClassType> */();
-
     private List/* <JsonObject> */jsonObjects = new ArrayList/* <JsonObject> */();
 
     {
@@ -112,8 +109,8 @@
      * must record a query on the element type being assigned to.
      */
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
-      if (x.op == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
+      if (x.getOp() == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
 
         // first, calculate the transitive closure of all possible runtime types
         // the lhs could be
@@ -157,13 +154,13 @@
     }
 
     // @Override
-    public void endVisit(JCastOperation x, Mutator m) {
-      recordCast(x.castType, x.getExpression());
+    public void endVisit(JCastOperation x, Context ctx) {
+      recordCast(x.getCastType(), x.getExpr());
     }
 
     // @Override
-    public void endVisit(JInstanceOf x, Mutator m) {
-      recordCast(x.testType, x.getExpression());
+    public void endVisit(JInstanceOf x, Context ctx) {
+      recordCast(x.getTestType(), x.getExpr());
     }
 
     /**
@@ -179,9 +176,9 @@
 
       /*
        * IMPORTANT: Visit my supertype first. The implementation of
-       * {@link com.google.gwt.lang.Cast#wrapJSO()} depends on all superclasses
-       * having typeIds that are less than all their subclasses. This allows the
-       * same JSO to be wrapped stronger but not weaker.
+       * com.google.gwt.lang.Cast.wrapJSO() depends on all superclasses having
+       * typeIds that are less than all their subclasses. This allows the same
+       * JSO to be wrapped stronger but not weaker.
        */
       computeSourceClass(type.extnds);
 
@@ -275,46 +272,50 @@
    * Explicitly convert any char-typed expressions within a concat operation
    * into strings.
    */
-  private class ConcatVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Convert chars to Strings inside of concat operations.");
+  private class ConcatVisitor extends JModVisitor {
 
     private JMethod stringValueOfChar = null;
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       if (x.getType() != program.getTypeJavaLangString()) {
         return;
       }
 
-      if (x.op == JBinaryOperator.ADD) {
-        convertCharString(x.lhs);
-        convertCharString(x.rhs);
-      } else if (x.op == JBinaryOperator.ASG_ADD) {
-        convertCharString(x.rhs);
+      if (x.getOp() == JBinaryOperator.ADD) {
+        JExpression newLhs = convertCharString(x.getLhs());
+        JExpression newRhs = convertCharString(x.getRhs());
+        if (newLhs != x.getLhs() || newRhs != x.getRhs()) {
+          JBinaryOperation newExpr = new JBinaryOperation(program,
+              x.getSourceInfo(), program.getTypeJavaLangString(),
+              JBinaryOperator.ADD, newLhs, newRhs);
+          ctx.replaceMe(newExpr);
+        }
+      } else if (x.getOp() == JBinaryOperator.ASG_ADD) {
+        JExpression newRhs = convertCharString(x.getRhs());
+        if (newRhs != x.getRhs()) {
+          JBinaryOperation newExpr = new JBinaryOperation(program,
+              x.getSourceInfo(), program.getTypeJavaLangString(),
+              JBinaryOperator.ASG_ADD, x.getLhs(), newRhs);
+          ctx.replaceMe(newExpr);
+        }
       }
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    private void convertCharString(Mutator m) {
+    private JExpression convertCharString(JExpression expr) {
       JPrimitiveType charType = program.getTypePrimitiveChar();
-      JExpression expr = m.get();
       if (expr.getType() == charType) {
+        // Replace the character with a call to Cast.charToString()
         if (stringValueOfChar == null) {
           stringValueOfChar = program.getSpecialMethod("Cast.charToString");
           assert (stringValueOfChar != null);
         }
-        JMethodCall call = new JMethodCall(program, null, stringValueOfChar);
-        ChangeList myChangeList = new ChangeList("Replace '" + expr
-            + "' with a call to Cast.charToString()");
-        myChangeList.addExpression(m, call.args);
-        myChangeList.replaceExpression(m, call);
-        changeList.add(myChangeList);
+        JMethodCall call = new JMethodCall(program, expr.getSourceInfo(), null,
+            stringValueOfChar);
+        call.getArgs().add(expr);
+        return call;
       }
+      return expr;
     }
   }
 
@@ -322,72 +323,69 @@
    * Explicitly cast all integral divide operations to trigger replacements with
    * narrowing calls in the next pass.
    */
-  private class DivVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Explicitly cast all integral division operations.");
+  private class DivVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       JType type = x.getType();
-      if (x.op == JBinaryOperator.DIV
+      if (x.getOp() == JBinaryOperator.DIV
           && type != program.getTypePrimitiveFloat()
           && type != program.getTypePrimitiveDouble()) {
-        JCastOperation cast = new JCastOperation(program, type,
-            program.getLiteralNull());
-        ChangeList myChangeList = new ChangeList("Cast '" + x + "' to type '"
-            + type + "'");
-        myChangeList.changeType(x, program.getTypePrimitiveDouble());
-        myChangeList.replaceExpression(cast.expr, m);
-        myChangeList.replaceExpression(m, cast);
-        changeList.add(myChangeList);
+        x.setType(program.getTypePrimitiveDouble());
+        JCastOperation cast = new JCastOperation(program, x.getSourceInfo(),
+            type, x);
+        ctx.replaceMe(cast);
       }
     }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
   }
 
-  private class ReplaceTypeChecksVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Replace all casts and instanceof operations.");
+  /**
+   * Replaces all casts and instanceof operations with calls to implementation
+   * methods.
+   */
+  private class ReplaceTypeChecksVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JCastOperation x, Mutator m) {
-      JType toType = x.castType;
+    public void endVisit(JCastOperation x, Context ctx) {
+      JExpression replaceExpr;
+      JType toType = x.getCastType();
       if (toType instanceof JReferenceType) {
+        JExpression curExpr = x.getExpr();
         JReferenceType refType = (JReferenceType) toType;
-        JType argType = x.getExpression().getType();
+        JType argType = x.getExpr().getType();
         if (program.isJavaScriptObject(argType)) {
           /*
            * A JSO-derived class that is about to be cast must be "wrapped"
            * first. Since a JSO was never constructed, it may not have an
            * accessible prototype. Instead we copy fields from the seed
            * function's prototype directly onto the target object as expandos.
-           * See {@link com.google.gwt.lang.Cast#wrapJSO()}.
+           * See com.google.gwt.lang.Cast.wrapJSO().
            */
-          ChangeList myChangeList = new ChangeList("Wrap a JavaScript Object");
           JMethod wrap = program.getSpecialMethod("Cast.wrapJSO");
           // override the type of the called method with the JSO's type
-          JMethodCall call = new JMethodCall(program, null, wrap, argType);
-          myChangeList.addExpression(x.expr, call.args);
+          JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+              wrap, argType);
           JClassSeed seed = program.getLiteralClassSeed((JClassType) argType);
-          myChangeList.addExpression(seed, call.args);
-          myChangeList.replaceExpression(x.expr, call);
-          changeList.add(myChangeList);
+          call.getArgs().add(curExpr);
+          call.getArgs().add(seed);
+          curExpr = call;
         }
         if (argType instanceof JClassType
             && program.typeOracle.canTriviallyCast((JClassType) argType,
                 refType)) {
+          // TODO(???): why is this only for JClassType?
           // just remove the cast
-          changeList.replaceExpression(m, x.expr);
+          replaceExpr = curExpr;
         } else {
           JMethod method = program.getSpecialMethod("Cast.dynamicCast");
           // override the type of the called method with the target cast type
-          JMethodCall call = new JMethodCall(program, null, method, toType);
-          replaceCast(call, refType, m, x.expr);
+          JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+              method, toType);
+          Integer boxedInt = (Integer) queryIds.get(refType);
+          JIntLiteral qId = program.getLiteralInt(boxedInt.intValue());
+          call.getArgs().add(curExpr);
+          call.getArgs().add(qId);
+          replaceExpr = call;
         }
       } else {
         /*
@@ -403,7 +401,7 @@
         JPrimitiveType tLong = program.getTypePrimitiveLong();
         JPrimitiveType tFloat = program.getTypePrimitiveFloat();
         JPrimitiveType tDouble = program.getTypePrimitiveDouble();
-        JType fromType = x.getExpression().getType();
+        JType fromType = x.getExpr().getType();
         if (tByte == fromType) {
           if (tChar == toType) {
             narrow = true;
@@ -432,62 +430,46 @@
           }
         }
 
-        ChangeList myChangeList;
         if (narrow || round) {
+          // Replace the expression with a call to the narrow or round method
           String methodName = "Cast." + (narrow ? "narrow_" : "round_")
               + toType.getName();
-          myChangeList = new ChangeList("Replace '" + x + "' with a call to "
-              + methodName);
           JMethod castMethod = program.getSpecialMethod(methodName);
-          JMethodCall call = new JMethodCall(program, null, castMethod);
-          myChangeList.addExpression(x.expr, call.args);
-          myChangeList.replaceExpression(m, call);
+          JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+              castMethod);
+          call.getArgs().add(x.getExpr());
+          replaceExpr = call;
         } else {
-          myChangeList = new ChangeList("Remove the cast from '" + x + "'");
-          myChangeList.replaceExpression(m, x.expr);
+          // Just remove the cast
+          replaceExpr = x.getExpr();
         }
-        changeList.add(myChangeList);
       }
+      ctx.replaceMe(replaceExpr);
     }
 
     // @Override
-    public void endVisit(JInstanceOf x, Mutator m) {
-      JType argType = x.getExpression().getType();
+    public void endVisit(JInstanceOf x, Context ctx) {
+      JType argType = x.getExpr().getType();
       if (argType instanceof JClassType
           && program.typeOracle.canTriviallyCast((JClassType) argType,
-              x.testType)) {
-        // trivially true if non-null
+              x.getTestType())) {
+        // trivially true if non-null; replace with a null test
         JNullLiteral nullLit = program.getLiteralNull();
-        JBinaryOperation eq = new JBinaryOperation(program,
-            program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ, nullLit,
-            nullLit);
-        ChangeList myChangeList = new ChangeList("Replace '" + x
-            + "' with a simple null test.");
-        myChangeList.replaceExpression(eq.lhs, x.expr);
-        myChangeList.replaceExpression(m, eq);
-        changeList.add(myChangeList);
+        JBinaryOperation eq = new JBinaryOperation(program, x.getSourceInfo(),
+            program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ,
+            x.getExpr(), nullLit);
+        ctx.replaceMe(eq);
       } else {
         JMethod method = program.getSpecialMethod("Cast.instanceOf");
-        JMethodCall call = new JMethodCall(program, null, method);
-        replaceCast(call, x.testType, m, x.expr);
+        JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+            method);
+        Integer boxedInt = (Integer) queryIds.get(x.getTestType());
+        JIntLiteral qId = program.getLiteralInt(boxedInt.intValue());
+        call.getArgs().add(x.getExpr());
+        call.getArgs().add(qId);
+        ctx.replaceMe(call);
       }
     }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    private void replaceCast(JMethodCall call, JReferenceType type,
-        Mutator dest, Mutator arg) {
-      Integer boxedInt = (Integer) queryIds.get(type);
-      JIntLiteral qId = program.getLiteralInt(boxedInt.intValue());
-      ChangeList myChangeList = new ChangeList("Replace '" + dest.get()
-          + " with a call to " + call.getTarget().getName());
-      myChangeList.addExpression(arg, call.args);
-      myChangeList.addExpression(qId, call.args);
-      myChangeList.replaceExpression(dest, call);
-      changeList.add(myChangeList);
-    }
   }
 
   public static void exec(JProgram program) {
@@ -505,32 +487,20 @@
   private void execImpl() {
     {
       ConcatVisitor visitor = new ConcatVisitor();
-      program.traverse(visitor);
-      ChangeList changes = visitor.getChangeList();
-      if (!changes.empty()) {
-        changes.apply();
-      }
+      visitor.accept(program);
     }
     {
       DivVisitor visitor = new DivVisitor();
-      program.traverse(visitor);
-      ChangeList changes = visitor.getChangeList();
-      if (!changes.empty()) {
-        changes.apply();
-      }
+      visitor.accept(program);
     }
     {
       AssignTypeIdsVisitor assigner = new AssignTypeIdsVisitor();
-      program.traverse(assigner);
+      assigner.accept(program);
       assigner.computeTypeIds();
     }
     {
       ReplaceTypeChecksVisitor replacer = new ReplaceTypeChecksVisitor();
-      program.traverse(replacer);
-      ChangeList changes = replacer.getChangeList();
-      if (!changes.empty()) {
-        changes.apply();
-      }
+      replacer.accept(program);
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastOptimizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastOptimizer.java
index 36ae6e1..517e037 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastOptimizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastOptimizer.java
@@ -15,20 +15,19 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JTypeOracle;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 
 /**
  * Optimizer that will remove all trivially computable casts and instanceof
@@ -36,20 +35,20 @@
  */
 public class CastOptimizer {
 
-  private class ReplaceTrivialCastsVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Replace all trivially computable casts and instanceof operations.");
+  /**
+   * Replaces all trivially computable casts and instanceof operations.
+   */
+  private class ReplaceTrivialCastsVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JCastOperation x, Mutator m) {
-      JType argType = x.getExpression().getType();
-      if (!(x.castType instanceof JReferenceType)
+    public void endVisit(JCastOperation x, Context ctx) {
+      JType argType = x.getExpr().getType();
+      if (!(x.getCastType() instanceof JReferenceType)
           || !(argType instanceof JReferenceType)) {
         return;
       }
 
-      JReferenceType toType = (JReferenceType) x.castType;
+      JReferenceType toType = (JReferenceType) x.getCastType();
       JReferenceType fromType = (JReferenceType) argType;
 
       boolean triviallyTrue = false;
@@ -66,7 +65,7 @@
 
       if (triviallyTrue) {
         // remove the cast operation
-        changeList.replaceExpression(m, x.expr);
+        ctx.replaceMe(x.getExpr());
       } else if (triviallyFalse) {
         // throw a ClassCastException unless the argument is null
         JMethod method = program.getSpecialMethod("Cast.throwClassCastExceptionUnlessNull");
@@ -75,31 +74,31 @@
          * will proceedeth forth from this cast operation. Assuredly, if the
          * call completes normally it will return null.
          */
-        JMethodCall call = new JMethodCall(program, null, method,
-            program.getTypeNull());
-        ChangeList myChangeList = new ChangeList("Replace '" + x
-            + "' with a call to throwClassCastExceptionUnlessNull().");
-        myChangeList.addExpression(x.expr, call.args);
-        myChangeList.replaceExpression(m, call);
-        changeList.add(myChangeList);
+        JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
+            method, program.getTypeNull());
+        call.getArgs().add(x.getExpr());
+        ctx.replaceMe(call);
       }
     }
 
     // @Override
-    public void endVisit(JInstanceOf x, Mutator m) {
-      JType argType = x.getExpression().getType();
+    public void endVisit(JInstanceOf x, Context ctx) {
+      JType argType = x.getExpr().getType();
       if (!(argType instanceof JReferenceType)) {
         return;
       }
 
-      JReferenceType toType = x.testType;
+      JReferenceType toType = x.getTestType();
       JReferenceType fromType = (JReferenceType) argType;
 
       boolean triviallyTrue = false;
       boolean triviallyFalse = false;
 
       JTypeOracle typeOracle = program.typeOracle;
-      if (typeOracle.canTriviallyCast(fromType, toType)) {
+      if (fromType == program.getTypeNull()) {
+        // null is never instanceOf anything
+        triviallyFalse = true;
+      } else if (typeOracle.canTriviallyCast(fromType, toType)) {
         triviallyTrue = true;
       } else if (!typeOracle.isInstantiatedType(toType)) {
         triviallyFalse = true;
@@ -108,30 +107,17 @@
       }
 
       if (triviallyTrue) {
-        if (fromType == program.getTypeNull()) {
-          // replace with a true literal
-          changeList.replaceExpression(m, program.getLiteralBoolean(true));
-        } else {
-          // replace with a simple null test
-          JNullLiteral nullLit = program.getLiteralNull();
-          JBinaryOperation eq = new JBinaryOperation(program,
-              program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ, nullLit,
-              nullLit);
-          ChangeList myChangeList = new ChangeList("Replace '" + x
-              + "' with a simple null test.");
-          myChangeList.replaceExpression(eq.lhs, x.expr);
-          myChangeList.replaceExpression(m, eq);
-          changeList.add(myChangeList);
-        }
+        // replace with a simple null test
+        JNullLiteral nullLit = program.getLiteralNull();
+        JBinaryOperation neq = new JBinaryOperation(program, x.getSourceInfo(),
+            program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ,
+            x.getExpr(), nullLit);
+        ctx.replaceMe(neq);
       } else if (triviallyFalse) {
         // replace with a false literal
-        changeList.replaceExpression(m, program.getLiteralBoolean(false));
+        ctx.replaceMe(program.getLiteralBoolean(false));
       }
     }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
   }
 
   public static boolean exec(JProgram program) {
@@ -146,12 +132,7 @@
 
   private boolean execImpl() {
     ReplaceTrivialCastsVisitor replacer = new ReplaceTrivialCastsVisitor();
-    program.traverse(replacer);
-    ChangeList changes = replacer.getChangeList();
-    if (changes.empty()) {
-      return false;
-    }
-    changes.apply();
-    return true;
+    replacer.accept(program);
+    return replacer.didChange();
   }
 }
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 aadfdbd..064fa81 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBlock;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
@@ -24,13 +25,13 @@
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JThrowStatement;
 import com.google.gwt.dev.jjs.ast.JTryStatement;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -41,33 +42,34 @@
  */
 public class CatchBlockNormalizer {
 
-  private class CollapseCatchBlocks extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Collapse all multi-catch blocks into a single catch block.");
+  /**
+   * Collapses all multi-catch blocks into a single catch block.
+   */
+  private class CollapseCatchBlocks extends JModVisitor {
 
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
       clearLocals();
       currentMethod = null;
     }
 
     // @Override
-    public void endVisit(JTryStatement x) {
-      if (x.catchBlocks.isEmpty()) {
+    public void endVisit(JTryStatement x, Context ctx) {
+      if (x.getCatchBlocks().isEmpty()) {
         return;
       }
 
-      ChangeList myChangeList = new ChangeList("Merge " + x.catchBlocks.size()
-          + " catch blocks.");
+      JSourceInfo catchInfo = ((JBlock) x.getCatchBlocks().get(0)).getSourceInfo();
+
       JLocal exObj = popTempLocal();
-      JLocalRef exRef = new JLocalRef(program, exObj);
-      JBlock newCatchBlock = new JBlock(program);
+      JLocalRef exRef = new JLocalRef(program, catchInfo, exObj);
+      JBlock newCatchBlock = new JBlock(program, catchInfo);
       // $e = Exceptions.caught($e)
       JMethod caughtMethod = program.getSpecialMethod("Exceptions.caught");
-      JMethodCall call = new JMethodCall(program, null, caughtMethod);
-      call.args.add(exRef);
-      JExpressionStatement asg = program.createAssignmentStmt(exRef, call);
+      JMethodCall call = new JMethodCall(program, catchInfo, null, caughtMethod);
+      call.getArgs().add(exRef);
+      JExpressionStatement asg = program.createAssignmentStmt(catchInfo, exRef,
+          call);
       newCatchBlock.statements.add(asg);
 
       /*
@@ -77,41 +79,40 @@
        * 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, exRef);
-      for (int i = x.catchBlocks.size() - 1; i >= 0; --i) {
-        JBlock block = (JBlock) x.catchBlocks.get(i);
-        JLocalRef arg = (JLocalRef) x.catchArgs.get(i);
+      JStatement cur = new JThrowStatement(program, null, exRef);
+      for (int i = x.getCatchBlocks().size() - 1; i >= 0; --i) {
+        JBlock block = (JBlock) x.getCatchBlocks().get(i);
+        JLocalRef arg = (JLocalRef) x.getCatchArgs().get(i);
+        catchInfo = block.getSourceInfo();
         JReferenceType argType = (JReferenceType) arg.getType();
         // if ($e instanceof Argtype) { userVar = $e; <user code> }
-        JExpression ifTest = new JInstanceOf(program, argType, exRef);
-        asg = program.createAssignmentStmt(arg, exRef);
-        myChangeList.addStatement(asg, 0, block);
+        JExpression ifTest = new JInstanceOf(program, catchInfo, argType, exRef);
+        asg = program.createAssignmentStmt(catchInfo, arg, exRef);
+        if (!block.statements.isEmpty()) {
+          // Only bother adding the assingment if the block is non-empty
+          block.statements.add(0, asg);
+        }
         // nest the previous as an else for me
-        cur = new JIfStatement(program, ifTest, block, cur);
+        cur = new JIfStatement(program, catchInfo, ifTest, block, cur);
       }
 
       newCatchBlock.statements.add(cur);
-      myChangeList.clear(x.catchArgs);
-      myChangeList.clear(x.catchBlocks);
-      myChangeList.addNode(exRef, 0, x.catchArgs);
-      myChangeList.addNode(newCatchBlock, 0, x.catchBlocks);
-      changeList.add(myChangeList);
-    }
-
-    public ChangeList getChangeList() {
-      return changeList;
+      x.getCatchArgs().clear();
+      x.getCatchArgs().add(exRef);
+      x.getCatchBlocks().clear();
+      x.getCatchBlocks().add(newCatchBlock);
     }
 
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
       clearLocals();
       return true;
     }
 
     // @Override
-    public boolean visit(JTryStatement x) {
-      if (!x.catchBlocks.isEmpty()) {
+    public boolean visit(JTryStatement x, Context ctx) {
+      if (!x.getCatchBlocks().isEmpty()) {
         pushTempLocal();
       }
       return true;
@@ -123,12 +124,9 @@
   }
 
   private JMethod currentMethod;
-
-  private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
-
   private int localIndex;
-
   private final JProgram program;
+  private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
 
   private CatchBlockNormalizer(JProgram program) {
     this.program = program;
@@ -140,14 +138,8 @@
   }
 
   private void execImpl() {
-    {
-      CollapseCatchBlocks collapser = new CollapseCatchBlocks();
-      program.traverse(collapser);
-      ChangeList changes = collapser.getChangeList();
-      if (!changes.empty()) {
-        changes.apply();
-      }
-    }
+    CollapseCatchBlocks collapser = new CollapseCatchBlocks();
+    collapser.accept(program);
   }
 
   private JLocal popTempLocal() {
@@ -156,8 +148,9 @@
 
   private void pushTempLocal() {
     if (localIndex == tempLocals.size()) {
-      JLocal newTemp = program.createLocal(("$e" + localIndex).toCharArray(),
-          program.getTypeJavaLangObject(), false, currentMethod);
+      JLocal newTemp = program.createLocal(null,
+          ("$e" + localIndex).toCharArray(), program.getTypeJavaLangObject(),
+          false, currentMethod);
       tempLocals.add(newTemp);
     }
     ++localIndex;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
index c2fcc16..a4abe8a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
@@ -15,21 +15,20 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
-import com.google.gwt.dev.jjs.ast.Holder;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
 import com.google.gwt.dev.jjs.ast.JLocal;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JThisRef;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 
 import java.util.ArrayList;
@@ -42,19 +41,20 @@
  */
 public class CompoundAssignmentNormalizer {
 
-  private class BreakupAssignOpsVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Break apart certain complex assignments.");
+  /**
+   * Breaks apart certain complex assignments.
+   */
+  private class BreakupAssignOpsVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       /*
        * Convert to a normal divide operation so we can cast the result. Since
        * the left hand size must be computed twice, we have to replace any
        * left-hand side expressions that could have side effects with
        * temporaries, so that they are only run once.
        */
-      if (x.op == JBinaryOperator.ASG_DIV
+      if (x.getOp() == JBinaryOperator.ASG_DIV
           && x.getType() != program.getTypePrimitiveFloat()
           && x.getType() != program.getTypePrimitiveDouble()) {
 
@@ -65,57 +65,49 @@
          * temporaries, so that they are only run once.
          */
         final int pushUsedLocals = localIndex;
-        JMultiExpression multi = new JMultiExpression(program);
+        JMultiExpression multi = new JMultiExpression(program,
+            x.getSourceInfo());
         ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
             multi);
-        x.lhs.traverse(replacer);
+        JExpression newLhs = replacer.accept(x.getLhs());
         localIndex = pushUsedLocals;
 
         JNullLiteral litNull = program.getLiteralNull();
         JBinaryOperation operation = new JBinaryOperation(program,
-            x.getLhs().getType(), JBinaryOperator.DIV, litNull, litNull);
-        JBinaryOperation asg = new JBinaryOperation(program,
-            x.getLhs().getType(), JBinaryOperator.ASG, litNull, operation);
+            x.getSourceInfo(), newLhs.getType(), JBinaryOperator.DIV, newLhs,
+            x.getRhs());
+        JBinaryOperation asg = new JBinaryOperation(program, x.getSourceInfo(),
+            newLhs.getType(), JBinaryOperator.ASG, newLhs, operation);
 
-        ChangeList myChangeList = new ChangeList("Break '" + x
-            + "' into two operations.");
-
-        myChangeList.replaceExpression(operation.lhs, x.lhs);
-        myChangeList.replaceExpression(operation.rhs, x.rhs);
-        myChangeList.replaceExpression(asg.lhs, x.lhs);
-
-        if (replacer.getChangeList().empty()) {
-          myChangeList.replaceExpression(m, asg);
+        JMultiExpression multiExpr = replacer.getMultiExpr();
+        if (multiExpr.exprs.isEmpty()) {
+          // just use the split assignment expression
+          ctx.replaceMe(asg);
         } else {
-          myChangeList.add(replacer.getChangeList());
-          myChangeList.addExpression(asg, multi.exprs);
-          myChangeList.replaceExpression(m, multi);
+          // add the assignment as the last item in the multi
+          multi.exprs.add(asg);
+          ctx.replaceMe(multi);
         }
-
-        changeList.add(myChangeList);
       }
     }
 
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
       clearLocals();
       currentMethod = null;
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
       clearLocals();
       return true;
     }
   }
-  private class ReplaceSideEffectsInLvalue extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Replace side effects in lvalue.");
+  /**
+   * Replaces side effects in lvalue.
+   */
+  private class ReplaceSideEffectsInLvalue extends JModVisitor {
 
     private final JMultiExpression multi;
 
@@ -123,56 +115,65 @@
       this.multi = multi;
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
+    public JMultiExpression getMultiExpr() {
+      return multi;
     }
 
     // @Override
-    public boolean visit(JArrayRef x, Mutator m) {
-      possiblyReplace(x.instance);
-      possiblyReplace(x.indexExpr);
-      return false;
-    }
-
-    // @Override
-    public boolean visit(JFieldRef x, Mutator m) {
-      if (x.getInstance() != null) {
-        possiblyReplace(x.instance);
+    public boolean visit(JArrayRef x, Context ctx) {
+      JExpression newInstance = possiblyReplace(x.getInstance());
+      JExpression newIndexExpr = possiblyReplace(x.getIndexExpr());
+      if (newInstance != x.getInstance() || newIndexExpr != x.getIndexExpr()) {
+        JArrayRef newExpr = new JArrayRef(program, x.getSourceInfo(),
+            newInstance, newIndexExpr);
+        ctx.replaceMe(newExpr);
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JLocalRef x, Mutator m) {
+    public boolean visit(JFieldRef x, Context ctx) {
+      if (x.getInstance() != null) {
+        JExpression newInstance = possiblyReplace(x.getInstance());
+        if (newInstance != x.getInstance()) {
+          JFieldRef newExpr = new JFieldRef(program, x.getSourceInfo(),
+              newInstance, x.getField(), x.getEnclosingType());
+          ctx.replaceMe(newExpr);
+        }
+      }
       return false;
     }
 
     // @Override
-    public boolean visit(JParameterRef x, Mutator m) {
+    public boolean visit(JLocalRef x, Context ctx) {
       return false;
     }
 
     // @Override
-    public boolean visit(JThisRef x, Mutator m) {
+    public boolean visit(JParameterRef x, Context ctx) {
       return false;
     }
 
-    private void possiblyReplace(Holder x) {
-      if (!x.get().hasSideEffects()) {
-        return;
+    // @Override
+    public boolean visit(JThisRef x, Context ctx) {
+      return false;
+    }
+
+    private JExpression possiblyReplace(JExpression x) {
+      if (!x.hasSideEffects()) {
+        return x;
       }
 
       // Create a temp local
       JLocal tempLocal = getTempLocal();
 
       // Create an assignment for this temp and add it to multi.
-      JLocalRef tempRef = new JLocalRef(program, tempLocal);
-      JBinaryOperation asg = new JBinaryOperation(program, x.get().getType(),
-          JBinaryOperator.ASG, tempRef, program.getLiteralNull());
-      changeList.replaceExpression(asg.rhs, x);
-      changeList.addExpression(asg, multi.exprs);
+      JLocalRef tempRef = new JLocalRef(program, x.getSourceInfo(), tempLocal);
+      JBinaryOperation asg = new JBinaryOperation(program, x.getSourceInfo(),
+          x.getType(), JBinaryOperator.ASG, tempRef, x);
+      multi.exprs.add(asg);
       // Update me with the temp
-      changeList.replaceExpression(x, tempRef);
+      return tempRef;
     }
   }
 
@@ -181,12 +182,9 @@
   }
 
   private JMethod currentMethod;
-
-  private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
-
   private int localIndex;
-
   private final JProgram program;
+  private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
 
   private CompoundAssignmentNormalizer(JProgram program) {
     this.program = program;
@@ -199,19 +197,16 @@
 
   private void execImpl() {
     BreakupAssignOpsVisitor breaker = new BreakupAssignOpsVisitor();
-    program.traverse(breaker);
-    ChangeList changes = breaker.getChangeList();
-    if (!changes.empty()) {
-      changes.apply();
-    }
+    breaker.accept(program);
   }
 
   private JLocal getTempLocal() {
     if (localIndex < tempLocals.size()) {
       return (JLocal) tempLocals.get(localIndex++);
     }
-    JLocal newTemp = program.createLocal(("$t" + localIndex++).toCharArray(),
-        program.getTypeVoid(), false, currentMethod);
+    JLocal newTemp = program.createLocal(null,
+        ("$t" + localIndex++).toCharArray(), program.getTypeVoid(), false,
+        currentMethod);
     tempLocals.add(newTemp);
     return newTemp;
   }
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
new file mode 100644
index 0000000..7b5311a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JBlock;
+import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
+import com.google.gwt.dev.jjs.ast.JBreakStatement;
+import com.google.gwt.dev.jjs.ast.JContinueStatement;
+import com.google.gwt.dev.jjs.ast.JDoStatement;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JForStatement;
+import com.google.gwt.dev.jjs.ast.JIfStatement;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JPrefixOperation;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JStatement;
+import com.google.gwt.dev.jjs.ast.JTryStatement;
+import com.google.gwt.dev.jjs.ast.JUnaryOperator;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.JWhileStatement;
+
+/**
+ * Attempts to remove dead code.
+ */
+public class DeadCodeElimination {
+
+  /**
+   * Eliminates dead or unreachable code when possible.
+   */
+  public class DeadCodeVisitor extends JModVisitor {
+
+    /**
+     * Short circuit boolean AND or OR expressions when possible.
+     */
+    public void endVisit(JBinaryOperation x, Context ctx) {
+      if (x.getOp() == JBinaryOperator.AND) {
+        // simplify short circuit AND expressions
+        if (x.getLhs() instanceof JBooleanLiteral) {
+          // eg: if (false && isWhatever()) -> if (false)
+          // eg: if (true && isWhatever()) -> if (isWhatever())
+          JBooleanLiteral booleanLiteral = (JBooleanLiteral) x.getLhs();
+          if (booleanLiteral.getValue()) {
+            ctx.replaceMe(x.getRhs());
+          } else {
+            ctx.replaceMe(x.getLhs());
+          }
+
+        } else if (x.getRhs() instanceof JBooleanLiteral) {
+          // eg: if (isWhatever() && true) -> if (isWhatever())
+          JBooleanLiteral booleanLiteral = (JBooleanLiteral) x.getRhs();
+          if (booleanLiteral.getValue()) {
+            ctx.replaceMe(x.getLhs());
+          }
+        }
+
+      } else if (x.getOp() == JBinaryOperator.OR) {
+        // simplify short circuit OR expressions
+        if (x.getLhs() instanceof JBooleanLiteral) {
+          // eg: if (true || isWhatever()) -> if (true)
+          // eg: if (false || isWhatever()) -> if (isWhatever())
+          JBooleanLiteral booleanLiteral = (JBooleanLiteral) x.getLhs();
+          if (booleanLiteral.getValue()) {
+            ctx.replaceMe(x.getLhs());
+          } else {
+            ctx.replaceMe(x.getRhs());
+          }
+
+        } else if (x.getRhs() instanceof JBooleanLiteral) {
+          // eg: if (isWhatever() || false) -> if (isWhatever())
+          JBooleanLiteral booleanLiteral = (JBooleanLiteral) x.getRhs();
+          if (!booleanLiteral.getValue()) {
+            ctx.replaceMe(x.getLhs());
+          }
+        }
+      }
+    }
+
+    /**
+     * Prune empty blocks.
+     */
+    public void endVisit(JBlock x, Context ctx) {
+      if (x.statements.size() == 0) {
+        if (ctx.canRemove()) {
+          ctx.removeMe();
+        }
+      }
+    }
+
+    /**
+     * Convert do { } while (false); into a block.
+     */
+    public void endVisit(JDoStatement x, Context ctx) {
+      final JExpression expression = x.getTestExpr();
+      if (expression instanceof JBooleanLiteral) {
+        final JBooleanLiteral booleanLiteral = (JBooleanLiteral) expression;
+
+        // If false, replace do with do's body
+        if (!booleanLiteral.getValue()) {
+          // Unless it contains break/continue statements
+          FindBreakContinueStatementsVisitor visitor = new FindBreakContinueStatementsVisitor();
+          visitor.accept(x.getBody());
+          if (!visitor.hasBreakContinueStatements()) {
+            ctx.replaceMe(x.getBody());
+          }
+        }
+      }
+    }
+
+    /**
+     * Prune for (X; false; Y) statements, but make sure X is run.
+     */
+    public void endVisit(JForStatement x, Context ctx) {
+      final JExpression expression = x.getTestExpr();
+      if (expression instanceof JBooleanLiteral) {
+        final JBooleanLiteral booleanLiteral = (JBooleanLiteral) expression;
+
+        // If false, replace the for statement with its initializers
+        if (!booleanLiteral.getValue()) {
+          JBlock block = new JBlock(program, x.getSourceInfo());
+          block.statements.addAll(x.getInitializers());
+          ctx.replaceMe(block);
+        }
+      }
+    }
+
+    /**
+     * Prune "if (false)" statements.
+     */
+    public void endVisit(JIfStatement x, Context ctx) {
+      final JExpression expression = x.getIfExpr();
+      if (expression instanceof JBooleanLiteral) {
+        final JBooleanLiteral booleanLiteral = (JBooleanLiteral) expression;
+
+        if (booleanLiteral.getValue()) {
+          // If true, replace myself with then statement
+          ctx.replaceMe(x.getThenStmt());
+        } else if (x.getElseStmt() != null) {
+          // If false, replace myself with else statement
+          ctx.replaceMe(x.getElseStmt());
+        } else {
+          // just prune me
+          removeMe(x, ctx);
+        }
+      }
+    }
+
+    /**
+     * Resolve "!true" into "false" and vice versa.
+     */
+    public void endVisit(JPrefixOperation x, Context ctx) {
+      if (x.getOp() == JUnaryOperator.NOT) {
+        if (x.getArg() instanceof JBooleanLiteral) {
+          JBooleanLiteral booleanLiteral = (JBooleanLiteral) x.getArg();
+          ctx.replaceMe(program.getLiteralBoolean(!booleanLiteral.getValue()));
+        }
+      }
+    }
+
+    /**
+     * Prune try statements with no body. Hoist up try statements with no
+     * catches and an empty finally.
+     */
+    public void endVisit(JTryStatement x, Context ctx) {
+      boolean noTry = x.getTryBlock().statements.isEmpty();
+      // TODO: normalize finally block handling
+      boolean noFinally = (x.getFinallyBlock() == null)
+          || x.getFinallyBlock().statements.isEmpty();
+      boolean noCatch = x.getCatchArgs().size() == 0;
+
+      if (noTry) {
+        // If the try block is empty, just remove it
+        removeMe(x, ctx);
+      } else if (noCatch && noFinally) {
+        // If there's no catch or finally, there's no point in this even being
+        // a try statement, replace myself with the try block
+        ctx.replaceMe(x.getTryBlock());
+      }
+    }
+
+    /**
+     * Prune while (false) statements.
+     */
+    public void endVisit(JWhileStatement x, Context ctx) {
+      final JExpression expression = x.getTestExpr();
+      if (expression instanceof JBooleanLiteral) {
+        final JBooleanLiteral booleanLiteral = (JBooleanLiteral) expression;
+
+        // If false, prune the while statement
+        if (!booleanLiteral.getValue()) {
+          removeMe(x, ctx);
+        }
+      }
+    }
+
+    private void removeMe(JStatement stmt, Context ctx) {
+      if (ctx.canRemove()) {
+        ctx.removeMe();
+      } else {
+        // empty block statement
+        ctx.replaceMe(new JBlock(program, stmt.getSourceInfo()));
+      }
+    }
+  }
+
+  /**
+   * Examines code to find out whether it contains any break or continue
+   * statements.
+   */
+  public static class FindBreakContinueStatementsVisitor extends JVisitor {
+    private boolean hasBreakContinueStatements = false;
+
+    public void endVisit(JBreakStatement x, Context ctx) {
+      hasBreakContinueStatements = true;
+    }
+
+    public void endVisit(JContinueStatement x, Context ctx) {
+      hasBreakContinueStatements = true;
+    }
+
+    protected boolean hasBreakContinueStatements() {
+      return hasBreakContinueStatements;
+    }
+  }
+
+  public static boolean exec(JProgram program) {
+    return new DeadCodeElimination(program).execImpl();
+  }
+
+  private final JProgram program;
+
+  public DeadCodeElimination(JProgram program) {
+    this.program = program;
+  }
+
+  private boolean execImpl() {
+    boolean madeChanges = false;
+    while (true) {
+      DeadCodeVisitor deadCodeVisitor = new DeadCodeVisitor();
+      deadCodeVisitor.accept(program);
+      if (!deadCodeVisitor.didChange()) {
+        break;
+      }
+      madeChanges = true;
+    }
+    return madeChanges;
+  }
+}
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 d733368..c903926 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
@@ -18,7 +18,6 @@
 import com.google.gwt.dev.jjs.ast.CanBeStatic;
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
 import com.google.gwt.dev.jjs.ast.HasName;
-import com.google.gwt.dev.jjs.ast.HolderList;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -63,6 +62,7 @@
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JSwitchStatement;
@@ -79,10 +79,12 @@
 import com.google.gwt.dev.js.JsAbstractVisitorWithAllVisits;
 import com.google.gwt.dev.js.ast.JsFunction;
 import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsSourceInfo;
 
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.internal.compiler.CompilationResult;
 import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
 import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
@@ -134,7 +136,6 @@
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
 import org.eclipse.jdt.internal.compiler.ast.WhileStatement;
-import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
 import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
 import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
 import org.eclipse.jdt.internal.compiler.impl.CharConstant;
@@ -183,7 +184,7 @@
   /**
    * Comparator for <code>HasName</code> instances.
    */
-  public static class HasNameSort implements Comparator/* <HasName> */ {
+  public static class HasNameSort implements Comparator {
     public int compare(Object o1, Object o2) {
       HasName h1 = (HasName) o1;
       HasName h2 = (HasName) o2;
@@ -203,6 +204,12 @@
    * {@link org.eclipse.jdt.internal.compiler.ast.ASTNode} we'd have to override
    * every single method and explicitly throw an exception to get the same
    * behavior.
+   * 
+   * NOTE ON JDT FORCED OPTIMIZATIONS - If JDT statically determines that a
+   * section of code in unreachable, it won't fully resolve that section of
+   * code. This invalid-state code causes us major problems. As a result, we
+   * have to optimize out those dead blocks early and never try to translate
+   * them to our AST.
    */
   private static class JavaASTGenerationVisitor {
 
@@ -224,18 +231,22 @@
 
     private ClassScope currentClassScope;
 
+    private String currentFileName;
+
     private JMethod currentMethod;
 
     private MethodScope currentMethodScope;
 
+    private int[] currentSeparatorPositions;
+
     private final Map/* <JMethod, Map<String, JLabel>> */labelMap = new IdentityHashMap();
 
     private Class[] params = new Class[1];
 
-    private final TypeMap typeMap;
-
     private final JProgram program;
 
+    private final TypeMap typeMap;
+
     public JavaASTGenerationVisitor(TypeMap typeMap) {
       this.typeMap = typeMap;
       program = this.typeMap.getProgram();
@@ -249,6 +260,8 @@
     public void processType(TypeDeclaration x) {
       currentClass = (JReferenceType) typeMap.get(x.binding);
       currentClassScope = x.scope;
+      currentSeparatorPositions = x.compilationResult.lineSeparatorPositions;
+      currentFileName = String.valueOf(x.compilationResult.fileName);
 
       if (x.fields != null) {
         // Process fields
@@ -292,6 +305,8 @@
 
       currentClassScope = null;
       currentClass = null;
+      currentSeparatorPositions = null;
+      currentFileName = null;
     }
 
     /**
@@ -304,23 +319,35 @@
       }
 
       try {
-        params[0] = child.getClass();
-        Method method = getClass().getDeclaredMethod(name, params);
-        args[0] = child;
-        return (JNode) method.invoke(this, args);
-      } catch (SecurityException e) {
-        throw new InternalCompilerException("Error during dispatch", e);
-      } catch (NoSuchMethodException e) {
-        String s = params[0].getName();
-        throw new InternalCompilerException("Expecting: private void " + name
-            + "(" + s.substring(s.lastIndexOf('.') + 1) + " x) { }", e);
-      } catch (IllegalArgumentException e) {
-        throw new InternalCompilerException("Error during dispatch", e);
-      } catch (IllegalAccessException e) {
-        throw new InternalCompilerException("Error during dispatch", e);
-      } catch (InvocationTargetException e) {
-        throw new InternalCompilerException("Error during dispatch",
-            e.getTargetException());
+        try {
+          params[0] = child.getClass();
+          Method method = getClass().getDeclaredMethod(name, params);
+          args[0] = child;
+          return (JNode) method.invoke(this, args);
+        } catch (SecurityException e) {
+          throw new InternalCompilerException("Error during dispatch", e);
+        } catch (NoSuchMethodException e) {
+          String s = params[0].getName();
+          throw new InternalCompilerException("Expecting: private void " + name
+              + "(" + s.substring(s.lastIndexOf('.') + 1) + " x) { }", e);
+        } catch (IllegalArgumentException e) {
+          throw new InternalCompilerException("Error during dispatch", e);
+        } catch (IllegalAccessException e) {
+          throw new InternalCompilerException("Error during dispatch", e);
+        } catch (InvocationTargetException e) {
+          Throwable target = e.getTargetException();
+          if (target instanceof InternalCompilerException) {
+            throw (InternalCompilerException) target;
+          } else {
+            throw new InternalCompilerException(
+                "Error during AST construction", target);
+          }
+        }
+      } catch (InternalCompilerException ice) {
+        if (child instanceof ASTNode) {
+          ice.addNode((ASTNode) child);
+        }
+        throw ice;
       }
     }
 
@@ -351,7 +378,7 @@
         if (expr == null) {
           return null;
         }
-        stmt = new JExpressionStatement(program, expr);
+        stmt = new JExpressionStatement(program, makeSourceInfo(x), expr);
       } else {
         stmt = (JStatement) dispatch("processStatement", x);
       }
@@ -422,6 +449,7 @@
      */
     void processConstructor(ConstructorDeclaration x) {
       JMethod ctor = (JMethod) typeMap.get(x.binding);
+      JSourceInfo info = ctor.body.getSourceInfo();
 
       currentMethod = ctor;
       currentMethodScope = x.scope;
@@ -444,8 +472,10 @@
 
       // Call clinit; $clinit is always in position 0.
       JMethod clinitMethod = (JMethod) enclosingType.methods.get(0);
-      JMethodCall clinitCall = new JMethodCall(program, null, clinitMethod);
-      ctor.body.statements.add(new JExpressionStatement(program, clinitCall));
+      JMethodCall clinitCall = new JMethodCall(program, info, null,
+          clinitMethod);
+      ctor.body.statements.add(new JExpressionStatement(program, info,
+          clinitCall));
 
       /*
        * All synthetic fields must be assigned, unless we have an explicit this
@@ -462,8 +492,9 @@
               JParameter param = (JParameter) paramIt.next();
               if (arg.matchingField != null) {
                 JField field = (JField) typeMap.get(arg);
-                ctor.body.statements.add(program.createAssignmentStmt(
-                    createVariableRef(field), createVariableRef(param)));
+                ctor.body.statements.add(program.createAssignmentStmt(info,
+                    createVariableRef(info, field), createVariableRef(info,
+                        param)));
               }
             }
           }
@@ -473,8 +504,9 @@
               SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
               JParameter param = (JParameter) paramIt.next();
               JField field = (JField) typeMap.get(arg);
-              ctor.body.statements.add(program.createAssignmentStmt(
-                  createVariableRef(field), createVariableRef(param)));
+              ctor.body.statements.add(program.createAssignmentStmt(info,
+                  createVariableRef(info, field),
+                  createVariableRef(info, param)));
             }
           }
         }
@@ -482,10 +514,11 @@
 
       // optional this or super constructor call
       if (call != null) {
-        ctor.body.statements.add(new JExpressionStatement(program, call));
+        ctor.body.statements.add(new JExpressionStatement(program,
+            makeSourceInfo(ctorCall), call));
       }
 
-      JExpression thisRef = createThisRef(enclosingType);
+      JExpression thisRef = createThisRef(info, enclosingType);
 
       /*
        * Call the synthetic instance initializer method, unless we have an
@@ -494,8 +527,10 @@
       if (!hasExplicitThis) {
         // $init is always in position 1 (clinit is in 0)
         JMethod initMethod = (JMethod) enclosingType.methods.get(1);
-        JMethodCall initCall = new JMethodCall(program, thisRef, initMethod);
-        ctor.body.statements.add(new JExpressionStatement(program, initCall));
+        JMethodCall initCall = new JMethodCall(program, info, thisRef,
+            initMethod);
+        ctor.body.statements.add(new JExpressionStatement(program, info,
+            initCall));
       }
 
       // user code (finally!)
@@ -513,10 +548,11 @@
       currentMethod = null;
 
       // synthesize a return statement to emulate returning the new object
-      ctor.body.statements.add(new JReturnStatement(program, thisRef));
+      ctor.body.statements.add(new JReturnStatement(program, null, thisRef));
     }
 
     JExpression processExpression(AllocationExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       SourceTypeBinding typeBinding = (SourceTypeBinding) x.resolvedType;
       if (typeBinding.constantPoolName() == null) {
         /*
@@ -558,10 +594,10 @@
           throw new InternalCompilerException(
               "String constructor error; no matching implementation.");
         }
-        call = new JMethodCall(program, null, targetMethod);
+        call = new JMethodCall(program, makeSourceInfo(x), null, targetMethod);
       } else {
-        JNewInstance newInstance = new JNewInstance(program, newType);
-        call = new JMethodCall(program, newInstance, ctor);
+        JNewInstance newInstance = new JNewInstance(program, info, newType);
+        call = new JMethodCall(program, info, newInstance, ctor);
       }
 
       // Synthetic args for inner classes
@@ -573,7 +609,7 @@
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
             JClassType syntheticThisType = (JClassType) typeMap.get(arg.type);
-            call.args.add(createThisRef(syntheticThisType));
+            call.getArgs().add(createThisRef(info, syntheticThisType));
           }
         }
         // Synthetic locals for local classes
@@ -581,8 +617,8 @@
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
             JVariable variable = (JVariable) typeMap.get(arg.actualOuterLocalVariable);
-            call.args.add(createVariableRef(variable,
-                arg.actualOuterLocalVariable));
+            call.getArgs().add(
+                createVariableRef(info, variable, arg.actualOuterLocalVariable));
           }
         }
       }
@@ -590,7 +626,7 @@
       // Plain old regular user arguments
       if (x.arguments != null) {
         for (int i = 0, n = x.arguments.length; i < n; ++i) {
-          call.args.add(dispProcessExpression(x.arguments[i]));
+          call.getArgs().add(dispProcessExpression(x.arguments[i]));
         }
       }
 
@@ -599,15 +635,18 @@
 
     JExpression processExpression(AND_AND_Expression x) {
       JType type = (JType) typeMap.get(x.resolvedType);
-      return processBinaryOperation(JBinaryOperator.AND, type, x.left, x.right);
+      JSourceInfo info = makeSourceInfo(x);
+      return processBinaryOperation(info, JBinaryOperator.AND, type, x.left,
+          x.right);
     }
 
     JExpression processExpression(ArrayAllocationExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
-      JNewArray newArray = new JNewArray(program, type);
+      JNewArray newArray = new JNewArray(program, info, type);
 
       if (x.initializer != null) {
-        newArray.initializers = new HolderList();
+        newArray.initializers = new ArrayList();
         if (x.initializer.expressions != null) {
           for (int i = 0; i < x.initializer.expressions.length; i++) {
             Expression expression = x.initializer.expressions[i];
@@ -615,7 +654,7 @@
           }
         }
       } else {
-        newArray.dims = new HolderList();
+        newArray.dims = new ArrayList();
         for (int i = 0; i < x.dimensions.length; i++) {
           Expression dimension = x.dimensions[i];
           // can be null if index expression was empty
@@ -631,10 +670,11 @@
     }
 
     JExpression processExpression(ArrayInitializer x) {
+      JSourceInfo info = makeSourceInfo(x);
       JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
-      JNewArray newArray = new JNewArray(program, type);
+      JNewArray newArray = new JNewArray(program, info, type);
 
-      newArray.initializers = new HolderList();
+      newArray.initializers = new ArrayList();
       if (x.expressions != null) {
         for (int i = 0; i < x.expressions.length; i++) {
           Expression expression = x.expressions[i];
@@ -646,14 +686,16 @@
     }
 
     JExpression processExpression(ArrayReference x) {
-      JArrayRef arrayRef = new JArrayRef(program,
+      JSourceInfo info = makeSourceInfo(x);
+      JArrayRef arrayRef = new JArrayRef(program, info,
           dispProcessExpression(x.receiver), dispProcessExpression(x.position));
       return arrayRef;
     }
 
     JExpression processExpression(Assignment x) {
       JType type = (JType) typeMap.get(x.resolvedType);
-      return processBinaryOperation(JBinaryOperator.ASG, type, x.lhs,
+      JSourceInfo info = makeSourceInfo(x);
+      return processBinaryOperation(info, JBinaryOperator.ASG, type, x.lhs,
           x.expression);
     }
 
@@ -713,12 +755,14 @@
       }
 
       JType type = (JType) typeMap.get(x.resolvedType);
-      return processBinaryOperation(op, type, x.left, x.right);
+      JSourceInfo info = makeSourceInfo(x);
+      return processBinaryOperation(info, op, type, x.left, x.right);
     }
 
     JExpression processExpression(CastExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JType type = (JType) typeMap.get(x.resolvedType);
-      JCastOperation cast = new JCastOperation(program, type,
+      JCastOperation cast = new JCastOperation(program, info, type,
           dispProcessExpression(x.expression));
       return cast;
     }
@@ -771,15 +815,17 @@
       }
 
       JType type = (JType) typeMap.get(x.resolvedType);
-      return processBinaryOperation(op, type, x.lhs, x.expression);
+      JSourceInfo info = makeSourceInfo(x);
+      return processBinaryOperation(info, op, type, x.lhs, x.expression);
     }
 
     JExpression processExpression(ConditionalExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JType type = (JType) typeMap.get(x.resolvedType);
       JExpression ifTest = dispProcessExpression(x.condition);
       JExpression thenExpr = dispProcessExpression(x.valueIfTrue);
       JExpression elseExpr = dispProcessExpression(x.valueIfFalse);
-      JConditional conditional = new JConditional(program, type, ifTest,
+      JConditional conditional = new JConditional(program, info, type, ifTest,
           thenExpr, elseExpr);
       return conditional;
     }
@@ -799,7 +845,8 @@
       }
 
       JType type = (JType) typeMap.get(x.resolvedType);
-      return processBinaryOperation(op, type, x.left, x.right);
+      JSourceInfo info = makeSourceInfo(x);
+      return processBinaryOperation(info, op, type, x.left, x.right);
     }
 
     /**
@@ -815,6 +862,7 @@
     }
 
     JExpression processExpression(FieldReference x) {
+      JSourceInfo info = makeSourceInfo(x);
       FieldBinding fieldBinding = x.binding;
       JField field;
       if (fieldBinding.declaringClass == null) {
@@ -827,18 +875,20 @@
         field = (JField) typeMap.get(fieldBinding);
       }
       JExpression instance = dispProcessExpression(x.receiver);
-      JExpression fieldRef = new JFieldRef(program, instance, field,
+      JExpression fieldRef = new JFieldRef(program, info, instance, field,
           currentClass);
       return fieldRef;
     }
 
     JExpression processExpression(InstanceOfExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JExpression expr = dispProcessExpression(x.expression);
       JReferenceType testType = (JReferenceType) typeMap.get(x.type.resolvedType);
-      return new JInstanceOf(program, testType, expr);
+      return new JInstanceOf(program, info, testType, expr);
     }
 
     JExpression processExpression(MessageSend x) {
+      JSourceInfo info = makeSourceInfo(x);
       JType type = (JType) typeMap.get(x.resolvedType);
       JMethod method = (JMethod) typeMap.get(x.binding);
       assert (type == method.getType());
@@ -847,7 +897,7 @@
       if (x.receiver instanceof ThisReference) {
         if (method.isStatic()) {
           // don't bother qualifying it, it's a no-op
-          // TODO: this may be handled by later optimizations now
+          // TODO(???): this may be handled by later optimizations now
           qualifier = null;
         } else if (x.receiver instanceof QualifiedThisReference) {
           // do nothing, the qualifier is correct
@@ -857,11 +907,11 @@
            * actually be the wrong type, if the target method is in an enclosing
            * class. We have to sythensize our own ref of the correct type.
            */
-          qualifier = createThisRef(method.getEnclosingType());
+          qualifier = createThisRef(info, method.getEnclosingType());
         }
       }
 
-      JMethodCall call = new JMethodCall(program, qualifier, method);
+      JMethodCall call = new JMethodCall(program, info, qualifier, method);
       boolean isSuperRef = x.receiver instanceof SuperReference;
       if (isSuperRef) {
         call.setStaticDispatchOnly();
@@ -870,7 +920,7 @@
       // The arguments come first...
       if (x.arguments != null) {
         for (int i = 0, n = x.arguments.length; i < n; ++i) {
-          call.args.add(dispProcessExpression(x.arguments[i]));
+          call.getArgs().add(dispProcessExpression(x.arguments[i]));
         }
       }
 
@@ -883,10 +933,13 @@
 
     JExpression processExpression(OR_OR_Expression x) {
       JType type = (JType) typeMap.get(x.resolvedType);
-      return processBinaryOperation(JBinaryOperator.OR, type, x.left, x.right);
+      JSourceInfo info = makeSourceInfo(x);
+      return processBinaryOperation(info, JBinaryOperator.OR, type, x.left,
+          x.right);
     }
 
     JExpression processExpression(PostfixExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JUnaryOperator op;
 
       switch (x.operator) {
@@ -902,12 +955,13 @@
           throw new InternalCompilerException("Unexpected postfix operator");
       }
 
-      JPostfixOperation postOp = new JPostfixOperation(program, op,
+      JPostfixOperation postOp = new JPostfixOperation(program, info, op,
           dispProcessExpression(x.lhs));
       return postOp;
     }
 
     JExpression processExpression(PrefixExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JUnaryOperator op;
 
       switch (x.operator) {
@@ -923,7 +977,7 @@
           throw new InternalCompilerException("Unexpected prefix operator");
       }
 
-      JPrefixOperation preOp = new JPrefixOperation(program, op,
+      JPrefixOperation preOp = new JPrefixOperation(program, info, op,
           dispProcessExpression(x.lhs));
       return preOp;
     }
@@ -942,11 +996,12 @@
         return processExpression((AllocationExpression) x);
       }
 
+      JSourceInfo info = makeSourceInfo(x);
       MethodBinding b = x.binding;
       JMethod ctor = (JMethod) typeMap.get(b);
       JClassType enclosingType = (JClassType) ctor.getEnclosingType();
-      JNewInstance newInstance = new JNewInstance(program, enclosingType);
-      JMethodCall call = new JMethodCall(program, newInstance, ctor);
+      JNewInstance newInstance = new JNewInstance(program, info, enclosingType);
+      JMethodCall call = new JMethodCall(program, info, newInstance, ctor);
       JExpression qualifier = dispProcessExpression(x.enclosingInstance);
 
       // Synthetic args for inner classes
@@ -958,7 +1013,7 @@
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
             JClassType syntheticThisType = (JClassType) typeMap.get(arg.type);
-            call.args.add(createThisRef(syntheticThisType, qualifier));
+            call.getArgs().add(createThisRef(syntheticThisType, qualifier));
           }
         }
         // Synthetic locals for local classes
@@ -966,8 +1021,8 @@
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
             JVariable variable = (JVariable) typeMap.get(arg.actualOuterLocalVariable);
-            call.args.add(createVariableRef(variable,
-                arg.actualOuterLocalVariable));
+            call.getArgs().add(
+                createVariableRef(info, variable, arg.actualOuterLocalVariable));
           }
         }
       }
@@ -975,7 +1030,7 @@
       // Plain old regular arguments
       if (x.arguments != null) {
         for (int i = 0, n = x.arguments.length; i < n; ++i) {
-          call.args.add(dispProcessExpression(x.arguments[i]));
+          call.getArgs().add(dispProcessExpression(x.arguments[i]));
         }
       }
 
@@ -983,6 +1038,7 @@
     }
 
     JExpression processExpression(QualifiedNameReference x) {
+      JSourceInfo info = makeSourceInfo(x);
       Binding binding = x.binding;
       JNode node = typeMap.get(binding);
       if (!(node instanceof JVariable)) {
@@ -990,7 +1046,7 @@
       }
       JVariable variable = (JVariable) node;
 
-      JExpression curRef = createVariableRef(variable, binding);
+      JExpression curRef = createVariableRef(info, variable, binding);
 
       /*
        * Wackiness: JDT represents multiple field access as an array of fields,
@@ -1011,7 +1067,7 @@
           } else {
             field = (JField) typeMap.get(fieldBinding);
           }
-          curRef = new JFieldRef(program, curRef, field, currentClass);
+          curRef = new JFieldRef(program, info, curRef, field, currentClass);
         }
       }
 
@@ -1020,6 +1076,7 @@
 
     JExpression processExpression(QualifiedSuperReference x) {
       JClassType type = (JClassType) typeMap.get(x.resolvedType);
+      JSourceInfo info = makeSourceInfo(x);
       /*
        * WEIRD: If a superref is qualified with the EXACT type of the innermost
        * type (in other words, a needless qualifier), it must refer to that
@@ -1031,14 +1088,15 @@
        * compatible type), so we must create a reference to some outer type.
        */
       if (type == currentClass) {
-        return createSuperRef(type);
+        return createSuperRef(info, type);
       } else {
-        return createQualifiedSuperRef(type);
+        return createQualifiedSuperRef(info, type);
       }
     }
 
     JExpression processExpression(QualifiedThisReference x) {
       JClassType type = (JClassType) typeMap.get(x.resolvedType);
+      JSourceInfo info = makeSourceInfo(x);
       /*
        * WEIRD: If a thisref is qualified with the EXACT type of the innermost
        * type (in other words, a needless qualifier), it must refer to that
@@ -1050,13 +1108,14 @@
        * compatible type), so we must create a reference to some outer type.
        */
       if (type == currentClass) {
-        return createThisRef(type);
+        return createThisRef(info, type);
       } else {
-        return createQualifiedThisRef(type);
+        return createQualifiedThisRef(info, type);
       }
     }
 
     JExpression processExpression(SingleNameReference x) {
+      JSourceInfo info = makeSourceInfo(x);
       Binding binding = x.binding;
       Object target = typeMap.get(binding);
       if (!(target instanceof JVariable)) {
@@ -1073,29 +1132,32 @@
       if (x.syntheticAccessors != null) {
         JField field = (JField) variable;
         if (!field.isStatic()) {
-          JExpression instance = createThisRef(field.getEnclosingType());
-          return new JFieldRef(program, instance, field, currentClass);
+          JExpression instance = createThisRef(info, field.getEnclosingType());
+          return new JFieldRef(program, info, instance, field, currentClass);
         }
       }
 
-      return createVariableRef(variable, binding);
+      return createVariableRef(info, variable, binding);
     }
 
     JExpression processExpression(SuperReference x) {
       JClassType type = (JClassType) typeMap.get(x.resolvedType);
       assert (type == currentClass.extnds);
-      JExpression superRef = createSuperRef(type);
+      JSourceInfo info = makeSourceInfo(x);
+      JExpression superRef = createSuperRef(info, type);
       return superRef;
     }
 
     JExpression processExpression(ThisReference x) {
       JClassType type = (JClassType) typeMap.get(x.resolvedType);
       assert (type == currentClass);
-      JExpression thisRef = createThisRef(type);
+      JSourceInfo info = makeSourceInfo(x);
+      JExpression thisRef = createThisRef(info, type);
       return thisRef;
     }
 
     JExpression processExpression(UnaryExpression x) {
+      JSourceInfo info = makeSourceInfo(x);
       JUnaryOperator op;
       int operator = ((x.bits & UnaryExpression.OperatorMASK) >> UnaryExpression.OperatorSHIFT);
 
@@ -1121,7 +1183,7 @@
               "Unexpected operator for unary expression");
       }
 
-      JPrefixOperation preOp = new JPrefixOperation(program, op,
+      JPrefixOperation preOp = new JPrefixOperation(program, info, op,
           dispProcessExpression(x.expression));
       return preOp;
     }
@@ -1144,8 +1206,9 @@
       if (initializer instanceof JLiteral) {
         field.constInitializer = (JLiteral) initializer;
       } else if (initializer != null) {
-        JStatement assignStmt = program.createAssignmentStmt(
-            createVariableRef(field), initializer);
+        JSourceInfo info = makeSourceInfo(declaration);
+        JStatement assignStmt = program.createAssignmentStmt(info,
+            createVariableRef(info, field), initializer);
 
         // will either be init or clinit
         currentMethod.body.statements.add(assignStmt);
@@ -1158,11 +1221,6 @@
       currentMethod.body.statements.add(block);
     }
 
-    // // 5.0
-    // JStatement processStatement(ForeachStatement x) {
-    // return null;
-    // }
-
     void processMethod(AbstractMethodDeclaration x) {
       MethodBinding b = x.binding;
       JMethod method = (JMethod) typeMap.get(b);
@@ -1192,6 +1250,11 @@
       currentMethod = null;
     }
 
+    // // 5.0
+    // JStatement processStatement(ForeachStatement x) {
+    // return null;
+    // }
+
     void processNativeMethod(AbstractMethodDeclaration x,
         JsniMethod nativeMethod) {
 
@@ -1214,10 +1277,11 @@
 
       for (int i = 0; i < nameRefs.size(); ++i) {
         JsNameRef nameRef = (JsNameRef) nameRefs.get(i);
+        JSourceInfo info = translateInfo(nameRef.getInfo());
         String ident = nameRef.getName().getIdent();
         HasEnclosingType node = (HasEnclosingType) program.jsniMap.get(ident);
         if (node == null) {
-          node = parseJsniRef(x, ident);
+          node = parseJsniRef(info, x, ident);
           if (node == null) {
             continue; // already reported error
           }
@@ -1229,12 +1293,13 @@
         boolean isField = node instanceof JField;
         assert (isField || node instanceof JMethod);
         if (canBeStatic.isStatic() && nameRef.getQualifier() != null) {
-          reportJsniError(x, "Cannot make a qualified reference to the static "
-              + (isField ? "field " : "method ") + hasName.getName(), 0);
+          reportJsniError(info, x,
+              "Cannot make a qualified reference to the static "
+                  + (isField ? "field " : "method ") + hasName.getName());
         } else if (!canBeStatic.isStatic() && nameRef.getQualifier() == null) {
-          reportJsniError(x,
+          reportJsniError(info, x,
               "Cannot make an unqualified reference to the instance "
-                  + (isField ? "field " : "method ") + hasName.getName(), 0);
+                  + (isField ? "field " : "method ") + hasName.getName());
         }
 
         if (isField) {
@@ -1243,20 +1308,22 @@
            * from JSNI with the literal value of the field.
            */
           JField field = (JField) node;
-          JsniFieldRef fieldRef = new JsniFieldRef(program, field, currentClass);
+          JsniFieldRef fieldRef = new JsniFieldRef(program, info, field,
+              currentClass);
           nativeMethod.jsniFieldRefs.add(fieldRef);
         } else {
           JMethod method = (JMethod) node;
-          JsniMethodRef methodRef = new JsniMethodRef(program, method);
+          JsniMethodRef methodRef = new JsniMethodRef(program, info, method);
           nativeMethod.jsniMethodRefs.add(methodRef);
         }
       }
     }
 
     JStatement processStatement(AssertStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       JExpression expr = dispProcessExpression(x.assertExpression);
       JExpression arg = dispProcessExpression(x.exceptionArgument);
-      return new JAssertStatement(program, expr, arg);
+      return new JAssertStatement(program, info, expr, arg);
     }
 
     JBlock processStatement(Block x) {
@@ -1264,7 +1331,8 @@
         return null;
       }
 
-      JBlock block = new JBlock(program);
+      JSourceInfo info = makeSourceInfo(x);
+      JBlock block = new JBlock(program, info);
       if (x.statements != null) {
         for (int i = 0, n = x.statements.length; i < n; ++i) {
           JStatement jstmt = dispProcessStatement(x.statements[i]);
@@ -1277,32 +1345,28 @@
     }
 
     JStatement processStatement(BreakStatement x) {
-      return new JBreakStatement(program, getOrCreateLabel(currentMethod,
-          x.label));
+      JSourceInfo info = makeSourceInfo(x);
+      return new JBreakStatement(program, info, getOrCreateLabel(info,
+          currentMethod, x.label));
     }
 
     JStatement processStatement(CaseStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       JExpression expression = dispProcessExpression(x.constantExpression);
-      return new JCaseStatement(program, (JLiteral) expression);
+      return new JCaseStatement(program, info, (JLiteral) expression);
     }
 
     JStatement processStatement(ContinueStatement x) {
-      return new JContinueStatement(program, getOrCreateLabel(currentMethod,
-          x.label));
+      JSourceInfo info = makeSourceInfo(x);
+      return new JContinueStatement(program, info, getOrCreateLabel(info,
+          currentMethod, x.label));
     }
 
     JStatement processStatement(DoStatement x) {
-      // If false, just return the body
-      // do { x } while (false) => x
-      Constant cst = x.condition.optimizedBooleanConstant();
-      if (cst != Constant.NotAConstant) {
-        if (!cst.booleanValue()) {
-          return dispProcessStatement(x.action);
-        }
-      }
+      JSourceInfo info = makeSourceInfo(x);
       JExpression loopTest = dispProcessExpression(x.condition);
       JStatement loopBody = dispProcessStatement(x.action);
-      JDoStatement stmt = new JDoStatement(program, loopTest, loopBody);
+      JDoStatement stmt = new JDoStatement(program, info, loopTest, loopBody);
       return stmt;
     }
 
@@ -1311,13 +1375,15 @@
     }
 
     JStatement processStatement(ForStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
+      // SEE NOTE ON JDT FORCED OPTIMIZATIONS
       // If false, just return the initializers
       // for (init; false; inc) { x } => { init }
       if (x.condition != null) {
         Constant cst = x.condition.optimizedBooleanConstant();
         if (cst != Constant.NotAConstant) {
           if (!cst.booleanValue()) {
-            JBlock block = new JBlock(program);
+            JBlock block = new JBlock(program, info);
             block.statements = processStatements(x.initializations);
             return block;
           }
@@ -1328,10 +1394,11 @@
       JExpression expr = dispProcessExpression(x.condition);
       List/* <JExpressionStatement> */incr = processStatements(x.increments);
       JStatement body = dispProcessStatement(x.action);
-      return new JForStatement(program, init, expr, incr, body);
+      return new JForStatement(program, info, init, expr, incr, body);
     }
 
     JStatement processStatement(IfStatement x) {
+      // SEE NOTE ON JDT FORCED OPTIMIZATIONS
       // If constant, just return the appropriate case
       // if (true) x; else y; => x
       // if (false) x; else y; => y
@@ -1346,10 +1413,12 @@
         }
       }
 
+      JSourceInfo info = makeSourceInfo(x);
       JExpression expr = dispProcessExpression(x.condition);
       JStatement thenStmt = dispProcessStatement(x.thenStatement);
       JStatement elseStmt = dispProcessStatement(x.elseStatement);
-      JIfStatement ifStmt = new JIfStatement(program, expr, thenStmt, elseStmt);
+      JIfStatement ifStmt = new JIfStatement(program, info, expr, thenStmt,
+          elseStmt);
       return ifStmt;
     }
 
@@ -1358,18 +1427,22 @@
       if (body == null) {
         return null;
       }
-      return new JLabeledStatement(program, getOrCreateLabel(currentMethod,
-          x.label), body);
+      JSourceInfo info = makeSourceInfo(x);
+      return new JLabeledStatement(program, info, getOrCreateLabel(info,
+          currentMethod, x.label), body);
     }
 
     JStatement processStatement(LocalDeclaration x) {
+      JSourceInfo info = makeSourceInfo(x);
       JLocal local = (JLocal) typeMap.get(x.binding);
-      JLocalRef localRef = new JLocalRef(program, local);
+      JLocalRef localRef = new JLocalRef(program, info, local);
       JExpression initializer = dispProcessExpression(x.initialization);
-      return new JLocalDeclarationStatement(program, localRef, initializer);
+      return new JLocalDeclarationStatement(program, info, localRef,
+          initializer);
     }
 
     JStatement processStatement(ReturnStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       if (currentMethodScope.referenceContext instanceof ConstructorDeclaration) {
         /*
          * Special: constructors are implemented as instance methods that return
@@ -1378,47 +1451,52 @@
          */
         JClassType enclosingType = (JClassType) currentMethod.getEnclosingType();
         assert (x.expression == null);
-        return new JReturnStatement(program, createThisRef(enclosingType));
+        return new JReturnStatement(program, info, createThisRef(info,
+            enclosingType));
       } else {
-        return new JReturnStatement(program,
+        return new JReturnStatement(program, info,
             dispProcessExpression(x.expression));
       }
     }
 
     JStatement processStatement(SwitchStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       JExpression expression = dispProcessExpression(x.expression);
-      JBlock block = new JBlock(program);
+      JBlock block = new JBlock(program, info);
       block.statements = processStatements(x.statements);
-      return new JSwitchStatement(program, expression, block);
+      return new JSwitchStatement(program, info, expression, block);
     }
 
     JStatement processStatement(SynchronizedStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       JBlock block = processStatement(x.block);
       JExpression expr = dispProcessExpression(x.expression);
-      block.statements.add(0, new JExpressionStatement(program, expr));
+      block.statements.add(0, new JExpressionStatement(program, info, expr));
       return block;
     }
 
     JStatement processStatement(ThrowStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       JExpression toThrow = dispProcessExpression(x.exception);
-      return new JThrowStatement(program, toThrow);
+      return new JThrowStatement(program, info, toThrow);
     }
 
     JStatement processStatement(TryStatement x) {
+      JSourceInfo info = makeSourceInfo(x);
       JBlock tryBlock = processStatement(x.tryBlock);
       List/* <JLocalRef> */catchArgs = new ArrayList/* <JLocalRef> */();
       List/* <JBlock> */catchBlocks = new ArrayList/* <JBlock> */();
       if (x.catchBlocks != null) {
         for (int i = 0, c = x.catchArguments.length; i < c; ++i) {
           JLocal local = (JLocal) typeMap.get(x.catchArguments[i].binding);
-          catchArgs.add(createVariableRef(local));
+          catchArgs.add(createVariableRef(info, local));
         }
         for (int i = 0, c = x.catchBlocks.length; i < c; ++i) {
           catchBlocks.add(processStatement(x.catchBlocks[i]));
         }
       }
       JBlock finallyBlock = processStatement(x.finallyBlock);
-      return new JTryStatement(program, tryBlock, catchArgs, catchBlocks,
+      return new JTryStatement(program, info, tryBlock, catchArgs, catchBlocks,
           finallyBlock);
     }
 
@@ -1428,6 +1506,7 @@
     }
 
     JStatement processStatement(WhileStatement x) {
+      // SEE NOTE ON JDT FORCED OPTIMIZATIONS
       // If false, just return nothing
       // while (false) { x } => ;
       Constant cst = x.condition.optimizedBooleanConstant();
@@ -1436,9 +1515,11 @@
           return null;
         }
       }
+      JSourceInfo info = makeSourceInfo(x);
       JExpression loopTest = dispProcessExpression(x.condition);
       JStatement loopBody = dispProcessStatement(x.action);
-      JWhileStatement stmt = new JWhileStatement(program, loopTest, loopBody);
+      JWhileStatement stmt = new JWhileStatement(program, info, loopTest,
+          loopBody);
       return stmt;
     }
 
@@ -1456,9 +1537,10 @@
     }
 
     JMethodCall processSuperConstructorCall(ExplicitConstructorCall x) {
+      JSourceInfo info = makeSourceInfo(x);
       JMethod ctor = (JMethod) typeMap.get(x.binding);
-      JExpression trueQualifier = createThisRef(currentClass);
-      JMethodCall call = new JMethodCall(program, trueQualifier, ctor);
+      JExpression trueQualifier = createThisRef(info, currentClass);
+      JMethodCall call = new JMethodCall(program, info, trueQualifier, ctor);
 
       // We have to find and pass through any synthetics our supertype needs
       ReferenceBinding superClass = x.binding.declaringClass;
@@ -1484,11 +1566,12 @@
               List/* <JExpression> */workList = new ArrayList/* <JExpression> */();
               Iterator/* <JParameter> */paramIt = currentMethod.params.iterator();
               for (int i = 0; i < myBinding.enclosingInstances.length; ++i) {
-                workList.add(createVariableRef((JParameter) paramIt.next()));
+                workList.add(createVariableRef(info,
+                    (JParameter) paramIt.next()));
               }
-              call.args.add(createThisRef(classType, workList));
+              call.getArgs().add(createThisRef(classType, workList));
             } else {
-              call.args.add(createThisRef(classType, qualifier));
+              call.getArgs().add(createThisRef(classType, qualifier));
             }
           }
         }
@@ -1512,14 +1595,14 @@
               throw new InternalCompilerException(
                   "Could not find matching local arg for explicit super ctor call.");
             }
-            call.args.add(createVariableRef(param));
+            call.getArgs().add(createVariableRef(info, param));
           }
         }
       }
 
       if (x.arguments != null) {
         for (int i = 0, n = x.arguments.length; i < n; ++i) {
-          call.args.add(dispProcessExpression(x.arguments[i]));
+          call.getArgs().add(dispProcessExpression(x.arguments[i]));
         }
       }
 
@@ -1527,9 +1610,10 @@
     }
 
     JMethodCall processThisConstructorCall(ExplicitConstructorCall x) {
+      JSourceInfo info = makeSourceInfo(x);
       JMethod ctor = (JMethod) typeMap.get(x.binding);
-      JExpression trueQualifier = createThisRef(currentClass);
-      JMethodCall call = new JMethodCall(program, trueQualifier, ctor);
+      JExpression trueQualifier = createThisRef(info, currentClass);
+      JMethodCall call = new JMethodCall(program, info, trueQualifier, ctor);
 
       // All synthetics must be passed through to the target ctor
       ReferenceBinding declaringClass = x.binding.declaringClass;
@@ -1538,12 +1622,14 @@
         NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
         if (nestedBinding.enclosingInstances != null) {
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
-            call.args.add(createVariableRef((JParameter) paramIt.next()));
+            call.getArgs().add(
+                createVariableRef(info, (JParameter) paramIt.next()));
           }
         }
         if (nestedBinding.outerLocalVariables != null) {
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
-            call.args.add(createVariableRef((JParameter) paramIt.next()));
+            call.getArgs().add(
+                createVariableRef(info, (JParameter) paramIt.next()));
           }
         }
       }
@@ -1551,7 +1637,7 @@
       assert (x.qualification == null);
       if (x.arguments != null) {
         for (int i = 0, n = x.arguments.length; i < n; ++i) {
-          call.args.add(dispProcessExpression(x.arguments[i]));
+          call.getArgs().add(dispProcessExpression(x.arguments[i]));
         }
       }
 
@@ -1563,7 +1649,8 @@
       if (classType.fields.size() > 0) {
         JField field = (JField) classType.fields.get(0);
         if (field.getName().startsWith("this$")) {
-          list.add(new JFieldRef(program, expr, field, currentClass));
+          list.add(new JFieldRef(program, expr.getSourceInfo(), expr, field,
+              currentClass));
         }
       }
     }
@@ -1596,9 +1683,10 @@
      * access) of the appropriate type. Always use this method instead of
      * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
-    private JExpression createQualifiedSuperRef(JClassType targetType) {
+    private JExpression createQualifiedSuperRef(JSourceInfo info,
+        JClassType targetType) {
       assert (currentClass instanceof JClassType);
-      JExpression expr = program.getExpressionThisRef((JClassType) currentClass);
+      JExpression expr = program.getExprThisRef(info, (JClassType) currentClass);
       List/* <JExpression> */list = new ArrayList();
       addAllOuterThisRefsPlusSuperChain(list, expr, (JClassType) currentClass);
       return createSuperRef(targetType, list);
@@ -1609,9 +1697,10 @@
      * access) of the appropriate type. Always use this method instead of
      * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
-    private JExpression createQualifiedThisRef(JClassType targetType) {
+    private JExpression createQualifiedThisRef(JSourceInfo info,
+        JClassType targetType) {
       assert (currentClass instanceof JClassType);
-      JExpression expr = program.getExpressionThisRef((JClassType) currentClass);
+      JExpression expr = program.getExprThisRef(info, (JClassType) currentClass);
       List/* <JExpression> */list = new ArrayList();
       addAllOuterThisRefsPlusSuperChain(list, expr, (JClassType) currentClass);
       return createThisRef(targetType, list);
@@ -1622,19 +1711,6 @@
      * access) of the appropriate type. Always use this method instead of
      * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
-    private JExpression createSuperRef(JClassType targetType) {
-      assert (currentClass instanceof JClassType);
-      JExpression expr = program.getExpressionThisRef((JClassType) currentClass);
-      List/* <JExpression> */list = new ArrayList();
-      list.add(expr);
-      return createSuperRef(targetType, list);
-    }
-
-    /**
-     * Helper to create a "super" ref (really a this ref or synthetic this field
-     * access) of the appropriate type. Always use this method instead of
-     * creating a naked JThisRef or you won't get the synthetic accesses right.
-     */
     private JExpression createSuperRef(JClassType targetType,
         List/* <JExpression> */list) {
       assert (currentClass instanceof JClassType);
@@ -1655,14 +1731,16 @@
     }
 
     /**
-     * Helper to creates this ref (or maybe a synthetic this field access) of
-     * the appropriate type. Always use this method instead of creating a naked
-     * JThisRef or you won't get the synthetic accesses right.
+     * Helper to create a "super" ref (really a this ref or synthetic this field
+     * access) of the appropriate type. Always use this method instead of
+     * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
-    private JExpression createThisRef(JReferenceType targetType) {
+    private JExpression createSuperRef(JSourceInfo info, JClassType targetType) {
       assert (currentClass instanceof JClassType);
-      return createThisRef(targetType,
-          program.getExpressionThisRef((JClassType) currentClass));
+      JExpression expr = program.getExprThisRef(info, (JClassType) currentClass);
+      List/* <JExpression> */list = new ArrayList();
+      list.add(expr);
+      return createSuperRef(targetType, list);
     }
 
     /**
@@ -1719,37 +1797,49 @@
     }
 
     /**
+     * Helper to creates this ref (or maybe a synthetic this field access) of
+     * the appropriate type. Always use this method instead of creating a naked
+     * JThisRef or you won't get the synthetic accesses right.
+     */
+    private JExpression createThisRef(JSourceInfo info,
+        JReferenceType targetType) {
+      assert (currentClass instanceof JClassType);
+      return createThisRef(targetType, program.getExprThisRef(info,
+          (JClassType) currentClass));
+    }
+
+    /**
      * Creates an appropriate JVariableRef for the polymorphic type of the
      * requested JVariable.
      */
-    private JVariableRef createVariableRef(JVariable variable) {
+    private JVariableRef createVariableRef(JSourceInfo info, JVariable variable) {
       if (variable instanceof JLocal) {
         JLocal local = (JLocal) variable;
         if (local.getEnclosingMethod() != currentMethod) {
           throw new InternalCompilerException(
               "LocalRef referencing local in a different method.");
         }
-        return new JLocalRef(program, local);
+        return new JLocalRef(program, info, local);
       } else if (variable instanceof JParameter) {
         JParameter parameter = (JParameter) variable;
         if (parameter.getEnclosingMethod() != currentMethod) {
           throw new InternalCompilerException(
               "ParameterRef referencing param in a different method.");
         }
-        return new JParameterRef(program, parameter);
+        return new JParameterRef(program, info, parameter);
       } else if (variable instanceof JField) {
         JField field = (JField) variable;
         JExpression instance = null;
         if (!field.isStatic()) {
           JClassType fieldEnclosingType = (JClassType) field.getEnclosingType();
-          instance = createThisRef(fieldEnclosingType);
+          instance = createThisRef(info, fieldEnclosingType);
           if (!program.typeOracle.canTriviallyCast(
               (JClassType) instance.getType(), fieldEnclosingType)) {
             throw new InternalCompilerException(
                 "FieldRef referencing field in a different type.");
           }
         }
-        return new JFieldRef(program, instance, field, currentClass);
+        return new JFieldRef(program, info, instance, field, currentClass);
       }
       throw new InternalCompilerException("Unknown JVariable subclass.");
     }
@@ -1758,7 +1848,8 @@
      * Creates an appropriate JVariableRef for the polymorphic type of the
      * requested JVariable.
      */
-    private JVariableRef createVariableRef(JVariable variable, Binding binding) {
+    private JVariableRef createVariableRef(JSourceInfo info,
+        JVariable variable, Binding binding) {
       // Fix up the reference if it's to an outer local/param
       variable = possiblyReferenceOuterLocal(variable, binding);
       if (variable == null) {
@@ -1770,14 +1861,15 @@
          */
         return null;
       }
-      return createVariableRef(variable);
+      return createVariableRef(info, variable);
     }
 
     /**
      * Get a new label of a particular name, or create a new one if it doesn't
      * exist already.
      */
-    private JLabel getOrCreateLabel(JMethod enclosingMethod, char[] name) {
+    private JLabel getOrCreateLabel(JSourceInfo info, JMethod enclosingMethod,
+        char[] name) {
       if (name == null) {
         return null;
       }
@@ -1789,21 +1881,28 @@
       }
       JLabel jlabel = (JLabel) lblMap.get(sname);
       if (jlabel == null) {
-        jlabel = new JLabel(program, sname);
+        jlabel = new JLabel(program, info, sname);
         lblMap.put(sname, jlabel);
       }
       return jlabel;
     }
 
-    private HasEnclosingType parseJsniRef(AbstractMethodDeclaration x,
-        String ident) {
+    private JSourceInfo makeSourceInfo(Statement x) {
+      int startLine = ProblemHandler.searchLineNumber(
+          currentSeparatorPositions, x.sourceStart);
+      return new JSourceInfo(x.sourceStart, x.sourceEnd, startLine,
+          currentFileName);
+    }
+
+    private HasEnclosingType parseJsniRef(JSourceInfo info,
+        AbstractMethodDeclaration x, String ident) {
       String[] parts = ident.substring(1).split("::");
       assert (parts.length == 2);
       String className = parts[0];
       JReferenceType type = program.getFromTypeMap(className);
       if (type == null) {
-        reportJsniError(x, "Unresolvable native reference to type '"
-            + className + "'", 0);
+        reportJsniError(info, x, "Unresolvable native reference to type '"
+            + className + "'");
         return null;
       }
       String rhs = parts[1];
@@ -1817,8 +1916,8 @@
           }
         }
 
-        reportJsniError(x, "Unresolvable native reference to field '" + rhs
-            + "' in type '" + className + "'", 0);
+        reportJsniError(info, x, "Unresolvable native reference to field '"
+            + rhs + "' in type '" + className + "'");
       } else {
         // look for a method
         String methodName = rhs.substring(0, parenPos);
@@ -1838,12 +1937,12 @@
         }
 
         if (almostMatches == null) {
-          reportJsniError(x, "Unresolvable native reference to method '"
-              + methodName + "' in type '" + className + "'", 0);
+          reportJsniError(info, x, "Unresolvable native reference to method '"
+              + methodName + "' in type '" + className + "'");
         } else {
-          reportJsniError(x, "Unresolvable native reference to method '"
+          reportJsniError(info, x, "Unresolvable native reference to method '"
               + methodName + "' in type '" + className + "' (did you mean "
-              + almostMatches + "?)", 0);
+              + almostMatches + "?)");
         }
       }
       return null;
@@ -1906,12 +2005,12 @@
      * look for the specific operators that we think should match each JDT node,
      * and throw an error if there's a mismatch.
      */
-    private JExpression processBinaryOperation(JBinaryOperator op, JType type,
-        Expression arg1, Expression arg2) {
+    private JExpression processBinaryOperation(JSourceInfo info,
+        JBinaryOperator op, JType type, Expression arg1, Expression arg2) {
       JExpression exprArg1 = dispProcessExpression(arg1);
       JExpression exprArg2 = dispProcessExpression(arg2);
-      JBinaryOperation binaryOperation = new JBinaryOperation(program, type,
-          op, exprArg1, exprArg2);
+      JBinaryOperation binaryOperation = new JBinaryOperation(program, info,
+          type, op, exprArg1, exprArg2);
       return binaryOperation;
     }
 
@@ -1920,14 +2019,14 @@
      * overrides/implements.
      */
     private void tryFindUpRefs(JMethod method, MethodBinding binding) {
-      tryFindUpRefsRecurive(method, binding, binding.declaringClass);
+      tryFindUpRefsRecursive(method, binding, binding.declaringClass);
     }
 
     /**
      * For a given method(and method binding), recursively try to find all
      * methods that it overrides/implements.
      */
-    private void tryFindUpRefsRecurive(JMethod method, MethodBinding binding,
+    private void tryFindUpRefsRecursive(JMethod method, MethodBinding binding,
         ReferenceBinding searchThisType) {
 
       // See if this class has any uprefs, unless this class is myself
@@ -1947,14 +2046,14 @@
 
       // recurse super class
       if (searchThisType.superclass() != null) {
-        tryFindUpRefsRecurive(method, binding, searchThisType.superclass());
+        tryFindUpRefsRecursive(method, binding, searchThisType.superclass());
       }
 
       // recurse super interfaces
       if (searchThisType.superInterfaces() != null) {
         for (int i = 0; i < searchThisType.superInterfaces().length; i++) {
           ReferenceBinding intf = searchThisType.superInterfaces()[i];
-          tryFindUpRefsRecurive(method, binding, intf);
+          tryFindUpRefsRecursive(method, binding, intf);
         }
       }
     }
@@ -1973,16 +2072,19 @@
     Collections.sort(jprogram.getDeclaredTypes(), new HasNameSort());
   }
 
-  public static void reportJsniError(
-      AbstractMethodDeclaration methodDeclaration, String message,
-      int offsetFromStart) {
+  public static void reportJsniError(JSourceInfo info,
+      AbstractMethodDeclaration methodDeclaration, String message) {
     CompilationResult compResult = methodDeclaration.compilationResult();
-    ICompilationUnit compUnit = compResult.getCompilationUnit();
-    offsetFromStart += methodDeclaration.bodyStart;
-    DefaultProblem problem = new DefaultProblem(compUnit.getFileName(),
-        message, IProblem.Unclassified, null, ProblemSeverities.Error,
-        offsetFromStart, offsetFromStart, ProblemHandler.searchLineNumber(
-            compResult.lineSeparatorPositions, offsetFromStart));
+    DefaultProblem problem = new DefaultProblem(
+        info.getFileName().toCharArray(), message, IProblem.Unclassified, null,
+        ProblemSeverities.Error, info.getStartPos(), info.getEndPos(),
+        info.getStartLine());
     compResult.record(problem, methodDeclaration);
   }
+
+  public static JSourceInfo translateInfo(JsSourceInfo info) {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index ce0738e..804a097 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
 import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
@@ -73,7 +74,6 @@
 import com.google.gwt.dev.jjs.ast.JUnaryOperator;
 import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.jjs.ast.JWhileStatement;
-import com.google.gwt.dev.jjs.ast.Mutator;
 import com.google.gwt.dev.jjs.ast.js.JClassSeed;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
@@ -149,12 +149,12 @@
     private final Stack/* <JsScope> */scopeStack = new Stack();
 
     // @Override
-    public void endVisit(JClassType x) {
+    public void endVisit(JClassType x, Context ctx) {
       pop();
     }
 
     // @Override
-    public void endVisit(JField x) {
+    public void endVisit(JField x, Context ctx) {
       String name = x.getName();
       String mangleName = mangleName(x);
       if (x.isStatic()) {
@@ -165,12 +165,12 @@
     }
 
     // @Override
-    public void endVisit(JInterfaceType x) {
+    public void endVisit(JInterfaceType x, Context ctx) {
       pop();
     }
 
     // @Override
-    public void endVisit(JLabel x) {
+    public void endVisit(JLabel x, Context ctx) {
       if (getName(x) != null) {
         return;
       }
@@ -178,7 +178,7 @@
     }
 
     // @Override
-    public void endVisit(JLocal x) {
+    public void endVisit(JLocal x, Context ctx) {
       // locals can conflict, that's okay just reuse the same variable
       JsScope scope = peek();
       JsName jsName = scope.getOrCreateObfuscatableName(x.getName());
@@ -186,17 +186,17 @@
     }
 
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
       pop();
     }
 
     // @Override
-    public void endVisit(JParameter x) {
+    public void endVisit(JParameter x, Context ctx) {
       names.put(x, peek().createUniqueObfuscatableName(x.getName()));
     }
 
     // @Override
-    public void endVisit(JProgram x) {
+    public void endVisit(JProgram x, Context ctx) {
       // visit special things that may have been culled
       JField field = x.getSpecialField("Object.typeId");
       names.put(field, objectScope.getOrCreateObfuscatableName(
@@ -230,12 +230,12 @@
     }
 
     // @Override
-    public void endVisit(JsniMethod x) {
+    public void endVisit(JsniMethod x, Context ctx) {
       // didn't push anything
     }
 
     // @Override
-    public boolean visit(JClassType x) {
+    public boolean visit(JClassType x, Context ctx) {
       // have I already been visited as a supertype?
       JsScope myScope = (JsScope) classScopes.get(x);
       if (myScope != null) {
@@ -254,7 +254,7 @@
         JsScope parentScope = (JsScope) classScopes.get(x.extnds);
         // Run my superclass first!
         if (parentScope == null) {
-          x.extnds.traverse(this);
+          accept(x.extnds);
         }
         parentScope = (JsScope) classScopes.get(x.extnds);
         assert (parentScope != null);
@@ -276,14 +276,14 @@
     }
 
     // @Override
-    public boolean visit(JInterfaceType x) {
+    public boolean visit(JInterfaceType x, Context ctx) {
       // interfaces have no name at run time
       push(interfaceScope);
       return true;
     }
 
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
 
       // my polymorphic name
       String name = x.getName();
@@ -321,7 +321,7 @@
     }
 
     // @Override
-    public boolean visit(JsniMethod x) {
+    public boolean visit(JsniMethod x, Context ctx) {
       // my polymorphic name
       String name = x.getName();
       if (!x.isStatic()) {
@@ -363,19 +363,19 @@
 
     private final JsName globalTemp = rootScope.getOrCreateUnobfuscatableName("_");
 
+    private final Stack/* <JsNode> */nodeStack = new Stack/* <JsNode> */();
+
     private final JsName prototype = objectScope.getOrCreateUnobfuscatableName("prototype");
 
     private final JsName window = rootScope.getOrCreateUnobfuscatableName("window");
 
-    private final Stack/* <JsNode> */nodeStack = new Stack/* <JsNode> */();
-
     // @Override
-    public void endVisit(JAbsentArrayDimension x, Mutator m) {
+    public void endVisit(JAbsentArrayDimension x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
     }
 
     // @Override
-    public void endVisit(JArrayRef x, Mutator m) {
+    public void endVisit(JArrayRef x, Context ctx) {
       JsArrayAccess jsArrayAccess = new JsArrayAccess();
       jsArrayAccess.setIndexExpr((JsExpression) pop());
       jsArrayAccess.setArrayExpr((JsExpression) pop());
@@ -383,8 +383,8 @@
     }
 
     // @Override
-    public void endVisit(JAssertStatement x) {
-      // TODO: implement assert
+    public void endVisit(JAssertStatement x, Context ctx) {
+      // TODO(later): implement assert
       if (x.getArg() != null) {
         pop(); // arg
       }
@@ -393,10 +393,10 @@
     }
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       JsExpression rhs = (JsExpression) pop(); // rhs
       JsExpression lhs = (JsExpression) pop(); // lhs
-      JsBinaryOperator myOp = JavaToJsOperatorMap.get(x.op);
+      JsBinaryOperator myOp = JavaToJsOperatorMap.get(x.getOp());
 
       /*
        * Use === and !== on reference types, or else you can get wrong answers
@@ -416,7 +416,7 @@
     }
 
     // @Override
-    public void endVisit(JBlock x) {
+    public void endVisit(JBlock x, Context ctx) {
       JsBlock jsBlock = new JsBlock();
       JsStatements stmts = jsBlock.getStatements();
       popList(stmts, x.statements.size()); // stmts
@@ -431,14 +431,15 @@
     }
 
     // @Override
-    public void endVisit(JBooleanLiteral x, Mutator m) {
-      push(x.value ? jsProgram.getTrueLiteral() : jsProgram.getFalseLiteral());
+    public void endVisit(JBooleanLiteral x, Context ctx) {
+      push(x.getValue() ? jsProgram.getTrueLiteral()
+          : jsProgram.getFalseLiteral());
     }
 
     // @Override
-    public void endVisit(JBreakStatement x) {
+    public void endVisit(JBreakStatement x, Context ctx) {
       JsNameRef labelRef = null;
-      if (x.label != null) {
+      if (x.getLabel() != null) {
         JsLabel label = (JsLabel) pop(); // label
         labelRef = label.getName().makeRef();
       }
@@ -446,8 +447,8 @@
     }
 
     // @Override
-    public void endVisit(JCaseStatement x) {
-      if (x.getExpression() == null) {
+    public void endVisit(JCaseStatement x, Context ctx) {
+      if (x.getExpr() == null) {
         push(new JsDefault());
       } else {
         JsCase jsCase = new JsCase();
@@ -457,31 +458,31 @@
     }
 
     // @Override
-    public void endVisit(JCastOperation x, Mutator m) {
+    public void endVisit(JCastOperation x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
     }
 
     // @Override
-    public void endVisit(JCharLiteral x, Mutator m) {
-      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.value)));
+    public void endVisit(JCharLiteral x, Context ctx) {
+      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
     }
 
     // @Override
-    public void endVisit(JClassLiteral x, Mutator m) {
+    public void endVisit(JClassLiteral x, Context ctx) {
       // My seed function name
-      String nameString = x.refType.getJavahSignatureName() + "_classlit";
+      String nameString = x.getRefType().getJavahSignatureName() + "_classlit";
       JsName classLit = rootScope.getOrCreateObfuscatableName(nameString);
-      classLits.put(x.refType, classLit);
+      classLits.put(x.getRefType(), classLit);
       push(classLit.makeRef());
     }
 
     // @Override
-    public void endVisit(JClassSeed x, Mutator m) {
-      push(getName(x.refType).makeRef());
+    public void endVisit(JClassSeed x, Context ctx) {
+      push(getName(x.getRefType()).makeRef());
     }
 
     // @Override
-    public void endVisit(JClassType x) {
+    public void endVisit(JClassType x, Context ctx) {
       if (alreadyRan.contains(x)) {
         return;
       }
@@ -612,7 +613,7 @@
     }
 
     // @Override
-    public void endVisit(JConditional x, Mutator m) {
+    public void endVisit(JConditional x, Context ctx) {
       JsExpression elseExpr = (JsExpression) pop(); // elseExpr
       JsExpression thenExpr = (JsExpression) pop(); // thenExpr
       JsExpression ifTest = (JsExpression) pop(); // ifTest
@@ -620,9 +621,9 @@
     }
 
     // @Override
-    public void endVisit(JContinueStatement x) {
+    public void endVisit(JContinueStatement x, Context ctx) {
       JsNameRef labelRef = null;
-      if (x.label != null) {
+      if (x.getLabel() != null) {
         JsLabel label = (JsLabel) pop(); // label
         labelRef = label.getName().makeRef();
       }
@@ -630,9 +631,9 @@
     }
 
     // @Override
-    public void endVisit(JDoStatement x) {
+    public void endVisit(JDoStatement x, Context ctx) {
       JsDoWhile stmt = new JsDoWhile();
-      if (x.body != null) {
+      if (x.getBody() != null) {
         stmt.setBody((JsStatement) pop()); // body
       } else {
         stmt.setBody(jsProgram.getEmptyStmt());
@@ -642,14 +643,14 @@
     }
 
     // @Override
-    public void endVisit(JDoubleLiteral x, Mutator m) {
-      push(jsProgram.getDecimalLiteral(String.valueOf(x.value)));
+    public void endVisit(JDoubleLiteral x, Context ctx) {
+      push(jsProgram.getDecimalLiteral(String.valueOf(x.getValue())));
     }
 
     // @Override
-    public void endVisit(JExpressionStatement x) {
+    public void endVisit(JExpressionStatement x, Context ctx) {
       JsExpression expr = (JsExpression) pop(); // expr
-      if (x.getExpression().hasSideEffects()) {
+      if (x.getExpr().hasSideEffects()) {
         push(expr.makeStmt());
       } else {
         push(jsProgram.getEmptyStmt());
@@ -657,7 +658,7 @@
     }
 
     // @Override
-    public void endVisit(JField x) {
+    public void endVisit(JField x, Context ctx) {
       if (x.hasInitializer() && x.constInitializer == null) {
         // do nothing
         push(null);
@@ -666,9 +667,9 @@
 
       // if we need an initial value, create an assignment
       if (x.constInitializer != null) {
-        x.constInitializer.traverse(this);
+        accept(x.constInitializer);
       } else {
-        x.getType().getDefaultValue().traverse(this);
+        accept(x.getType().getDefaultValue());
       }
 
       JsNameRef fieldRef = getName(x).makeRef();
@@ -680,8 +681,8 @@
     }
 
     // @Override
-    public void endVisit(JFieldRef x, Mutator m) {
-      JsName jsFieldName = getName(x.field);
+    public void endVisit(JFieldRef x, Context ctx) {
+      JsName jsFieldName = getName(x.getField());
       JsNameRef nameRef = jsFieldName.makeRef();
       JsExpression qualifier = null;
 
@@ -700,16 +701,16 @@
     }
 
     // @Override
-    public void endVisit(JFloatLiteral x, Mutator m) {
-      push(jsProgram.getDecimalLiteral(String.valueOf(x.value)));
+    public void endVisit(JFloatLiteral x, Context ctx) {
+      push(jsProgram.getDecimalLiteral(String.valueOf(x.getValue())));
     }
 
     // @Override
-    public void endVisit(JForStatement x) {
+    public void endVisit(JForStatement x, Context ctx) {
       JsFor jsFor = new JsFor();
 
       // body
-      if (x.body != null) {
+      if (x.getBody() != null) {
         jsFor.setBody((JsStatement) pop());
       } else {
         jsFor.setBody(jsProgram.getEmptyStmt());
@@ -746,14 +747,14 @@
     }
 
     // @Override
-    public void endVisit(JIfStatement x) {
+    public void endVisit(JIfStatement x, Context ctx) {
       JsIf stmt = new JsIf();
 
-      if (x.elseStmt != null) {
+      if (x.getElseStmt() != null) {
         stmt.setElseStmt((JsStatement) pop()); // elseStmt
       }
 
-      if (x.thenStmt != null) {
+      if (x.getThenStmt() != null) {
         stmt.setThenStmt((JsStatement) pop()); // thenStmt
       } else {
         stmt.setThenStmt(jsProgram.getEmptyStmt());
@@ -764,12 +765,12 @@
     }
 
     // @Override
-    public void endVisit(JInstanceOf x, Mutator m) {
+    public void endVisit(JInstanceOf x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
     }
 
     // @Override
-    public void endVisit(JInterfaceType x) {
+    public void endVisit(JInterfaceType x, Context ctx) {
       List/* <JsFunction> */jsFuncs = popList(x.methods.size()); // methods
       List/* <JsStatement> */jsFields = popList(x.fields.size()); // fields
 
@@ -789,17 +790,17 @@
     }
 
     // @Override
-    public void endVisit(JIntLiteral x, Mutator m) {
-      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.value)));
+    public void endVisit(JIntLiteral x, Context ctx) {
+      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
     }
 
     // @Override
-    public void endVisit(JLabel x) {
+    public void endVisit(JLabel x, Context ctx) {
       push(new JsLabel(getName(x)));
     }
 
     // @Override
-    public void endVisit(JLabeledStatement x) {
+    public void endVisit(JLabeledStatement x, Context ctx) {
       JsStatement body = (JsStatement) pop(); // body
       JsLabel label = (JsLabel) pop(); // label
       label.setStmt(body);
@@ -807,12 +808,12 @@
     }
 
     // @Override
-    public void endVisit(JLocal x) {
+    public void endVisit(JLocal x, Context ctx) {
       push(getName(x).makeRef());
     }
 
     // @Override
-    public void endVisit(JLocalDeclarationStatement x) {
+    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
 
       if (x.getInitializer() == null) {
         pop(); // localRef
@@ -834,17 +835,17 @@
     }
 
     // @Override
-    public void endVisit(JLocalRef x, Mutator m) {
+    public void endVisit(JLocalRef x, Context ctx) {
       push(getName(x.getTarget()).makeRef());
     }
 
     // @Override
-    public void endVisit(JLongLiteral x, Mutator m) {
-      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.value)));
+    public void endVisit(JLongLiteral x, Context ctx) {
+      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
     }
 
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
 
       JsBlock body = (JsBlock) pop();
       List/* <JsNameRef> */locals = popList(x.locals.size()); // locals
@@ -864,10 +865,28 @@
         jsParams.add(param);
       }
 
+      /*
+       * Emit a statement to declare the method's complete set of local
+       * variables. JavaScript doesn't have the same concept of lexical scoping
+       * as Java, so it's okay to just predeclare all local vars at the top of
+       * the function, which saves us having to use the "var" keyword over and
+       * over.
+       * 
+       * Note: it's fine to use the same JS ident to represent two different
+       * Java locals of the same name since they could never conflict with each
+       * 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();
+      Set alreadySeen = new HashSet();
       for (int i = 0; i < locals.size(); ++i) {
         JsNameRef localRef = (JsNameRef) locals.get(i);
-        vars.add(new JsVar(localRef.getName()));
+        JsName name = localRef.getName();
+        String ident = name.getIdent();
+        if (!alreadySeen.contains(ident)) {
+          alreadySeen.add(ident);
+          vars.add(new JsVar(name));
+        }
       }
 
       if (vars.iterator().hasNext()) {
@@ -884,11 +903,11 @@
     }
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
+    public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
       JsInvocation jsInvocation = new JsInvocation();
 
-      popList(jsInvocation.getArguments(), x.args.size()); // args
+      popList(jsInvocation.getArguments(), x.getArgs().size()); // args
 
       JsNameRef qualifier;
       JsExpression unnecessaryQualifier = null;
@@ -922,7 +941,7 @@
     }
 
     // @Override
-    public void endVisit(JMultiExpression x, Mutator m) {
+    public void endVisit(JMultiExpression x, Context ctx) {
       List/* <JsExpression> */exprs = popList(x.exprs.size());
       JsExpression cur = null;
       for (int i = 0; i < exprs.size(); ++i) {
@@ -933,12 +952,12 @@
     }
 
     // @Override
-    public void endVisit(JNewArray x, Mutator m) {
+    public void endVisit(JNewArray x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
     }
 
     // @Override
-    public void endVisit(JNewInstance x, Mutator m) {
+    public void endVisit(JNewInstance x, Context ctx) {
       JsNew newOp = new JsNew();
       JsNameRef nameRef = getName(x.getType()).makeRef();
       newOp.setConstructorExpression(nameRef);
@@ -946,34 +965,34 @@
     }
 
     // @Override
-    public void endVisit(JNullLiteral x, Mutator m) {
+    public void endVisit(JNullLiteral x, Context ctx) {
       push(jsProgram.getNullLiteral());
     }
 
     // @Override
-    public void endVisit(JParameter x) {
+    public void endVisit(JParameter x, Context ctx) {
       push(new JsParameter(getName(x)));
     }
 
     // @Override
-    public void endVisit(JParameterRef x, Mutator m) {
+    public void endVisit(JParameterRef x, Context ctx) {
       push(getName(x.getTarget()).makeRef());
     }
 
     // @Override
-    public void endVisit(JPostfixOperation x, Mutator m) {
-      push(new JsPostfixOperation(JavaToJsOperatorMap.get(x.op),
+    public void endVisit(JPostfixOperation x, Context ctx) {
+      push(new JsPostfixOperation(JavaToJsOperatorMap.get(x.getOp()),
           (JsExpression) pop())); // arg
     }
 
     // @Override
-    public void endVisit(JPrefixOperation x, Mutator m) {
-      push(new JsPrefixOperation(JavaToJsOperatorMap.get(x.op),
+    public void endVisit(JPrefixOperation x, Context ctx) {
+      push(new JsPrefixOperation(JavaToJsOperatorMap.get(x.getOp()),
           (JsExpression) pop())); // arg
     }
 
     // @Override
-    public void endVisit(JProgram x) {
+    public void endVisit(JProgram x, Context ctx) {
       JsStatements globalStmts = jsProgram.getGlobalBlock().getStatements();
 
       // types don't push
@@ -1043,7 +1062,7 @@
       JsArrayLiteral arrayLit = new JsArrayLiteral();
       for (int i = 0; i < program.getJsonTypeTable().size(); ++i) {
         JsonObject jsonObject = (JsonObject) program.getJsonTypeTable().get(i);
-        jsonObject.traverse(this);
+        accept(jsonObject);
         arrayLit.getExpressions().add((JsExpression) pop());
       }
       JsExpression asg = createAssignment(fieldRef, arrayLit);
@@ -1071,8 +1090,8 @@
     }
 
     // @Override
-    public void endVisit(JReturnStatement x) {
-      if (x.getExpression() != null) {
+    public void endVisit(JReturnStatement x, Context ctx) {
+      if (x.getExpr() != null) {
         push(new JsReturn((JsExpression) pop())); // expr
       } else {
         push(new JsReturn());
@@ -1080,7 +1099,7 @@
     }
 
     // @Override
-    public void endVisit(JsniMethod x) {
+    public void endVisit(JsniMethod x, Context ctx) {
       JsFunction jsFunc = x.getFunc();
 
       // replace all jsni idents with a real JsName now that we know it
@@ -1131,51 +1150,54 @@
     }
 
     // @Override
-    public void endVisit(JsonArray x, Mutator m) {
+    public void endVisit(JsonArray x, Context ctx) {
       JsArrayLiteral jsArrayLiteral = new JsArrayLiteral();
       popList(jsArrayLiteral.getExpressions(), x.exprs.size());
       push(jsArrayLiteral);
     }
 
     // @Override
-    public void endVisit(JsonObject x, Mutator mutator) {
+    public void endVisit(JsonObject x, Context ctx) {
       JsObjectLiteral jsObjectLiteral = new JsObjectLiteral();
       popList(jsObjectLiteral.getPropertyInitializers(), x.propInits.size());
       push(jsObjectLiteral);
     }
 
     // @Override
-    public void endVisit(JsonPropInit init) {
+    public void endVisit(JsonPropInit init, Context ctx) {
       JsExpression valueExpr = (JsExpression) pop();
       JsExpression labelExpr = (JsExpression) pop();
       push(new JsPropertyInitializer(labelExpr, valueExpr));
     }
 
     // @Override
-    public void endVisit(JStringLiteral x, Mutator m) {
-      push(jsProgram.getStringLiteral(x.value));
+    public void endVisit(JStringLiteral x, Context ctx) {
+      push(jsProgram.getStringLiteral(x.getValue()));
     }
 
     // @Override
-    public void endVisit(JThisRef x, Mutator m) {
+    public void endVisit(JThisRef x, Context ctx) {
       push(new JsThisRef());
     }
 
     // @Override
-    public void endVisit(JThrowStatement x) {
+    public void endVisit(JThrowStatement x, Context ctx) {
       push(new JsThrow((JsExpression) pop())); // expr
     }
 
     // @Override
-    public void endVisit(JTryStatement x) {
+    public void endVisit(JTryStatement x, Context ctx) {
       JsTry jsTry = new JsTry();
 
-      if (x.finallyBlock != null) {
-        jsTry.setFinallyBlock((JsBlock) pop()); // finallyBlock
+      if (x.getFinallyBlock() != null) {
+        JsBlock finallyBlock = (JsBlock) pop(); // finallyBlock
+        if (finallyBlock.getStatements().size() > 0) {
+          jsTry.setFinallyBlock(finallyBlock);
+        }
       }
 
-      int size = x.catchArgs.size();
-      assert (size < 2 && size == x.catchBlocks.size());
+      int size = x.getCatchArgs().size();
+      assert (size < 2 && size == x.getCatchBlocks().size());
       if (size == 1) {
         JsBlock catchBlock = (JsBlock) pop(); // catchBlocks
         JsNameRef arg = (JsNameRef) pop(); // catchArgs
@@ -1190,9 +1212,9 @@
     }
 
     // @Override
-    public void endVisit(JWhileStatement x) {
+    public void endVisit(JWhileStatement x, Context ctx) {
       JsWhile stmt = new JsWhile();
-      if (x.body != null) {
+      if (x.getBody() != null) {
         stmt.setBody((JsStatement) pop()); // body
       } else {
         stmt.setBody(jsProgram.getEmptyStmt());
@@ -1202,7 +1224,7 @@
     }
 
     // @Override
-    public boolean visit(JClassType x) {
+    public boolean visit(JClassType x, Context ctx) {
       if (alreadyRan.contains(x)) {
         return false;
       }
@@ -1210,20 +1232,20 @@
       // force supertype to generate code first, this is required for prototype
       // chaining to work properly
       if (x.extnds != null && !alreadyRan.contains(x)) {
-        x.extnds.traverse(this);
+        accept(x.extnds);
       }
 
       return true;
     }
 
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
       return true;
     }
 
     // @Override
-    public boolean visit(JProgram x) {
+    public boolean visit(JProgram x, Context ctx) {
       // handle null method
       // return 'window' so that fields can be referenced
       JsReturn jsReturn = new JsReturn(window.makeRef());
@@ -1236,26 +1258,26 @@
     }
 
     // @Override
-    public boolean visit(JsniMethod x) {
+    public boolean visit(JsniMethod x, Context ctx) {
       currentMethod = x;
       return false;
     }
 
-    public boolean visit(JSwitchStatement x) {
+    public boolean visit(JSwitchStatement x, Context ctx) {
       /*
        * What a pain.. JSwitchStatement and JsSwitch are modelled completely
        * differently. Here we try to resolve those differences.
        */
       JsSwitch jsSwitch = new JsSwitch();
-      x.getExpression().traverse(this);
+      accept(x.getExpr());
       jsSwitch.setExpr((JsExpression) pop()); // expr
 
-      List/* <JStatement> */bodyStmts = x.body.statements;
+      List/* <JStatement> */bodyStmts = x.getBody().statements;
       if (bodyStmts.size() > 0) {
         JsStatements curStatements = null;
         for (int i = 0; i < bodyStmts.size(); ++i) {
           JStatement stmt = (JStatement) bodyStmts.get(i);
-          stmt.traverse(this);
+          accept(stmt);
           if (stmt instanceof JCaseStatement) {
             // create a new switch member
             JsSwitchMember switchMember = (JsSwitchMember) pop(); // stmt
@@ -1450,19 +1472,19 @@
 
   private final JsScope interfaceScope;
 
-  private JsName nullMethodName;
-
-  private final JsScope objectScope;
-
-  private final JsScope rootScope;
-
   private final JsProgram jsProgram;
 
   private final Map/* <JMethod, JsFunction> */methodMap = new IdentityHashMap();
 
   private final Map/* <HasName, JsName> */names = new IdentityHashMap();
+
+  private JsName nullMethodName;
+
+  private final JsScope objectScope;
+
   private final Map/* <JMethod, JsName> */polymorphicNames = new IdentityHashMap();
   private final JProgram program;
+  private final JsScope rootScope;
   private final JTypeOracle typeOracle;
 
   private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram) {
@@ -1507,9 +1529,9 @@
 
   private void execImpl() {
     CreateNamesAndScopesVisitor creator = new CreateNamesAndScopesVisitor();
-    program.traverse(creator);
+    creator.accept(program);
     GenerateJavaScriptVisitor generator = new GenerateJavaScriptVisitor();
-    program.traverse(generator);
+    generator.accept(program);
   }
 
   private JsName getName(HasName x) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/InternalCompilerException.java b/dev/core/src/com/google/gwt/dev/jjs/impl/InternalCompilerException.java
index c93e1f5..29d3f35 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/InternalCompilerException.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/InternalCompilerException.java
@@ -15,6 +15,10 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.JNode;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+
 /**
  * Indicates the compiler encountered an unexpected and unsupported state of
  * operation.
@@ -28,4 +32,13 @@
   public InternalCompilerException(String message, Throwable cause) {
     super(message, cause);
   }
+
+  public void addNode(ASTNode node) {
+    // TODO Auto-generated method stub
+  }
+
+  public void addNode(JNode node) {
+    // TODO Auto-generated method stub
+  }
+
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
index ad86e58..927e3b1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
@@ -41,7 +42,6 @@
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JThisRef;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
 
 /**
  * See the Java Programming Language, 4th Edition, p. 750, Table 2. I just
@@ -54,7 +54,7 @@
 
   public static int exec(JExpression expression) {
     JavaPrecedenceVisitor visitor = new JavaPrecedenceVisitor();
-    expression.traverse(visitor);
+    visitor.accept(expression);
     if (visitor.answer < 0) {
       throw new InternalCompilerException("Precedence must be >= 0!");
     }
@@ -67,145 +67,145 @@
   }
 
   // @Override
-  public boolean visit(JAbsentArrayDimension x, Mutator m) {
+  public boolean visit(JAbsentArrayDimension x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JArrayRef x, Mutator m) {
+  public boolean visit(JArrayRef x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JBinaryOperation operation, Mutator mutator) {
-    answer = operation.op.getPrecedence();
+  public boolean visit(JBinaryOperation operation, Context ctx) {
+    answer = operation.getOp().getPrecedence();
     return false;
   }
 
   // @Override
-  public boolean visit(JBooleanLiteral x, Mutator m) {
+  public boolean visit(JBooleanLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JCastOperation operation, Mutator mutator) {
+  public boolean visit(JCastOperation operation, Context ctx) {
     answer = 2;
     return false;
   }
 
   // @Override
-  public boolean visit(JCharLiteral x, Mutator m) {
+  public boolean visit(JCharLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JClassLiteral x, Mutator m) {
+  public boolean visit(JClassLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JConditional conditional, Mutator mutator) {
+  public boolean visit(JConditional conditional, Context ctx) {
     answer = 13;
     return false;
   }
 
   // @Override
-  public boolean visit(JDoubleLiteral x, Mutator m) {
+  public boolean visit(JDoubleLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JFieldRef x, Mutator m) {
+  public boolean visit(JFieldRef x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JFloatLiteral x, Mutator m) {
+  public boolean visit(JFloatLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JInstanceOf of, Mutator mutator) {
+  public boolean visit(JInstanceOf of, Context ctx) {
     answer = 6;
     return false;
   }
 
   // @Override
-  public boolean visit(JIntLiteral x, Mutator m) {
+  public boolean visit(JIntLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JLocalRef x, Mutator m) {
+  public boolean visit(JLocalRef x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JLongLiteral x, Mutator m) {
+  public boolean visit(JLongLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JMethodCall x, Mutator m) {
+  public boolean visit(JMethodCall x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JNewArray array, Mutator mutator) {
+  public boolean visit(JNewArray array, Context ctx) {
     answer = 2;
     return false;
   }
 
   // @Override
-  public boolean visit(JNewInstance instance, Mutator mutator) {
+  public boolean visit(JNewInstance instance, Context ctx) {
     answer = 2;
     return false;
   }
 
   // @Override
-  public boolean visit(JNullLiteral x, Mutator m) {
+  public boolean visit(JNullLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JParameterRef x, Mutator m) {
+  public boolean visit(JParameterRef x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JPostfixOperation operation, Mutator mutator) {
+  public boolean visit(JPostfixOperation operation, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JPrefixOperation operation, Mutator mutator) {
+  public boolean visit(JPrefixOperation operation, Context ctx) {
     answer = 1;
     return false;
   }
 
   // @Override
-  public boolean visit(JStringLiteral x, Mutator m) {
+  public boolean visit(JStringLiteral x, Context ctx) {
     answer = 0;
     return false;
   }
 
   // @Override
-  public boolean visit(JThisRef x, Mutator m) {
+  public boolean visit(JThisRef x, Context ctx) {
     answer = 0;
     return false;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
index f34e52c..dd43d91 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JConditional;
@@ -22,102 +23,124 @@
 import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 
 /**
  * Replace cast and instanceof operations with calls to the Cast class.
  */
 public class JavaScriptObjectCaster {
 
-  private class AssignmentVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Synthesize casts from JavaScriptObjects to trigger wrapping.");
+  /**
+   * Synthesize casts from JavaScriptObjects to trigger wrapping.
+   */
+  private class AssignmentVisitor extends JModVisitor {
 
     private JMethod currentMethod;
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       if (x.isAssignment()) {
-        checkAndReplaceJso(x.rhs, x.getLhs().getType());
+        JType lhsType = x.getLhs().getType();
+        JExpression newRhs = checkAndReplaceJso(x.getRhs(), lhsType);
+        if (newRhs != x.getRhs()) {
+          JBinaryOperation asg = new JBinaryOperation(program,
+              x.getSourceInfo(), lhsType, x.getOp(), x.getLhs(), newRhs);
+          ctx.replaceMe(asg);
+        }
       }
     }
 
     // @Override
-    public void endVisit(JConditional x, Mutator m) {
-      checkAndReplaceJso(x.thenExpr, x.getType());
-      checkAndReplaceJso(x.elseExpr, x.getType());
-    }
-
-    // @Override
-    public void endVisit(JLocalDeclarationStatement x) {
-      JExpression initializer = x.getInitializer();
-      if (initializer != null) {
-        checkAndReplaceJso(x.initializer, x.getLocalRef().getType());
+    public void endVisit(JConditional x, Context ctx) {
+      JExpression newThen = checkAndReplaceJso(x.getThenExpr(), x.getType());
+      JExpression newElse = checkAndReplaceJso(x.getElseExpr(), x.getType());
+      if (newThen != x.getThenExpr() || newElse != x.getElseExpr()) {
+        JConditional newCond = new JConditional(program, x.getSourceInfo(),
+            x.getType(), x.getIfTest(), newThen, newElse);
+        ctx.replaceMe(newCond);
       }
     }
 
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
+      JExpression newInst = x.getInitializer();
+      if (newInst != null) {
+        newInst = checkAndReplaceJso(newInst, x.getLocalRef().getType());
+        if (newInst != x.getInitializer()) {
+          JLocalDeclarationStatement newStmt = new JLocalDeclarationStatement(
+              program, x.getSourceInfo(), x.getLocalRef(), newInst);
+          ctx.replaceMe(newStmt);
+        }
+      }
+    }
+
+    // @Override
+    public void endVisit(JMethod x, Context ctx) {
       currentMethod = null;
     }
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
-      if (!x.getTarget().isStatic()) {
-        // for polymorphic calls, force wrapping
-        checkAndReplaceJso(x.instance, program.getTypeJavaLangObject());
-      }
+    public void endVisit(JMethodCall x, Context ctx) {
       for (int i = 0; i < x.getTarget().params.size(); ++i) {
         JParameter param = (JParameter) x.getTarget().params.get(i);
-        checkAndReplaceJso(x.args.getMutator(i), param.getType());
+        JExpression newArg = checkAndReplaceJso(
+            (JExpression) x.getArgs().get(i), param.getType());
+        x.getArgs().set(i, newArg);
+      }
+      if (!x.getTarget().isStatic()) {
+        // for polymorphic calls, force wrapping
+        JExpression newInst = checkAndReplaceJso(x.getInstance(),
+            program.getTypeJavaLangObject());
+        if (newInst != x.getInstance()) {
+          JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
+              newInst, x.getTarget(), x.isStaticDispatchOnly());
+          newCall.getArgs().addAll(x.getArgs());
+          ctx.replaceMe(newCall);
+        }
       }
     }
 
     // @Override
-    public void endVisit(JReturnStatement x) {
-      if (x.getExpression() != null) {
-        checkAndReplaceJso(x.expr, currentMethod.getType());
+    public void endVisit(JReturnStatement x, Context ctx) {
+      if (x.getExpr() != null) {
+        JExpression newExpr = checkAndReplaceJso(x.getExpr(),
+            currentMethod.getType());
+        if (newExpr != x.getExpr()) {
+          JReturnStatement newStmt = new JReturnStatement(program,
+              x.getSourceInfo(), newExpr);
+          ctx.replaceMe(newStmt);
+        }
       }
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
       return true;
     }
 
-    private void checkAndReplaceJso(Mutator arg, JType targetType) {
-      JType argType = arg.get().getType();
+    private JExpression checkAndReplaceJso(JExpression arg, JType targetType) {
+      JType argType = arg.getType();
       if (argType == targetType) {
-        return;
+        return arg;
       }
 
       if (!(targetType instanceof JReferenceType)) {
-        return;
+        return arg;
       }
 
       if (!program.isJavaScriptObject(argType)) {
-        return;
+        return arg;
       }
-      JCastOperation cast = new JCastOperation(program, targetType,
-          program.getLiteralNull());
-      ChangeList myChangeList = new ChangeList("Synthesize a cast from '"
-          + argType + "' to '" + targetType + "'.");
-      myChangeList.replaceExpression(cast.expr, arg);
-      myChangeList.replaceExpression(arg, cast);
-      changeList.add(myChangeList);
+      // Synthesize a cast to the target type
+      JCastOperation cast = new JCastOperation(program, arg.getSourceInfo(),
+          targetType, arg);
+      return cast;
     }
   }
 
@@ -132,14 +155,8 @@
   }
 
   private void execImpl() {
-    {
-      AssignmentVisitor visitor = new AssignmentVisitor();
-      program.traverse(visitor);
-      ChangeList changes = visitor.getChangeList();
-      if (!changes.empty()) {
-        changes.apply();
-      }
-    }
+    AssignmentVisitor visitor = new AssignmentVisitor();
+    visitor.accept(program);
   }
 
 }
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 40c8071..477387a 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
@@ -15,12 +15,14 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
 import com.google.gwt.dev.jjs.ast.JLocal;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JProgram;
@@ -29,11 +31,12 @@
 import com.google.gwt.dev.jjs.ast.JThisRef;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 
+import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * This is an interesting "optimization". It's not really an optimization in and
@@ -47,116 +50,132 @@
  * reduces use of the long "this" keyword in the resulting JavaScript, and in
  * most cases the polymorphic version can be pruned later.
  * 
- * TODO: make this work on JSNI methods!
+ * TODO(later): make this work on JSNI methods!
  */
 public class MakeCallsStatic {
 
   /**
-   * For any instance methods that are called in a non-polymorphic manner, move
-   * the contents of the method to a static method, and have the instance method
-   * delegate to it. Sometimes the instance method can be pruned later since we
-   * update all non-polymorphic call sites.
+   * For all methods that should be made static, move the contents of the method
+   * to a new static method, and have the original (instance) method delegate to
+   * it. Sometimes the instance method can be pruned later since we update all
+   * non-polymorphic call sites.
    */
-  private class CreateStaticMethodVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Create static impls for instance methods");
+  private class CreateStaticImplsVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
-      JMethod oldMethod = x.getTarget();
+    public boolean visit(JMethod x, Context ctx) {
+      if (!toBeMadeStatic.contains(x)) {
+        return false;
+      }
+
+      // Let's do it!
+      JClassType enclosingType = (JClassType) x.getEnclosingType();
+      JType oldReturnType = x.getType();
+
+      // Create the new static method
+      String newName = "$" + x.getName();
+
+      /*
+       * Don't use the JProgram helper because it auto-adds the new method to
+       * its enclosing class, which will break iteration.
+       */
+      JMethod newMethod = new JMethod(program, x.getSourceInfo(), newName,
+          enclosingType, oldReturnType, false, true, true, x.isPrivate());
+
+      // Setup all params and locals; map from the old method to the new method
+      JParameter thisParam = program.createParameter(null,
+          "this$static".toCharArray(), enclosingType, true, newMethod);
+      Map/* <JVariable, JVariable> */varMap = new IdentityHashMap();
+      for (int i = 0; i < x.params.size(); ++i) {
+        JParameter oldVar = (JParameter) x.params.get(i);
+        JParameter newVar = program.createParameter(oldVar.getSourceInfo(),
+            oldVar.getName().toCharArray(), oldVar.getType(), oldVar.isFinal(),
+            newMethod);
+        varMap.put(oldVar, newVar);
+      }
+      newMethod.freezeParamTypes();
+
+      // Copy all locals over to the new method
+      for (int i = 0; i < x.locals.size(); ++i) {
+        JLocal oldVar = (JLocal) x.locals.get(i);
+        JLocal newVar = program.createLocal(oldVar.getSourceInfo(),
+            oldVar.getName().toCharArray(), oldVar.getType(), oldVar.isFinal(),
+            newMethod);
+        varMap.put(oldVar, newVar);
+      }
+      x.locals.clear();
+
+      // Move the body of the instance method to the static method
+      newMethod.body.statements.addAll(x.body.statements);
+      x.body.statements.clear();
+
+      /*
+       * Rewrite the method body. Update all thisRefs to paramrefs. Update
+       * paramRefs and localRefs to target the params/locals in the new method.
+       */
+      RewriteMethodBody rewriter = new RewriteMethodBody(thisParam, varMap);
+      rewriter.accept(newMethod);
+
+      JSourceInfo bodyInfo = x.body.getSourceInfo();
+      // delegate from the instance method to the static method
+      JMethodCall newCall = new JMethodCall(program, bodyInfo, null, newMethod);
+      newCall.getArgs().add(program.getExprThisRef(bodyInfo, enclosingType));
+      for (int i = 0; i < x.params.size(); ++i) {
+        JParameter param = (JParameter) x.params.get(i);
+        newCall.getArgs().add(new JParameterRef(program, bodyInfo, param));
+      }
+      JStatement statement;
+      if (oldReturnType == program.getTypeVoid()) {
+        statement = new JExpressionStatement(program, bodyInfo, newCall);
+      } else {
+        statement = new JReturnStatement(program, bodyInfo, newCall);
+      }
+      x.body.statements.add(statement);
+
+      // Add the new method as a static impl of the old method
+      program.putStaticImpl(x, newMethod);
+      assert (ctx.canInsert());
+      ctx.insertAfter(newMethod);
+      return false;
+    }
+  }
+
+  /**
+   * Look for any places where instance methods are called in a static manner.
+   * Record this fact so we can create static dispatch implementations.
+   */
+  private class FindStaticDispatchSitesVisitor extends JVisitor {
+
+    // @Override
+    public void endVisit(JMethodCall x, Context ctx) {
+      JMethod method = x.getTarget();
+
+      // Did we already do this one?
+      if (program.getStaticImpl(method) != null
+          || toBeMadeStatic.contains(method)) {
+        return;
+      }
 
       // Must be instance and final
       if (x.canBePolymorphic()) {
         return;
       }
-      if (oldMethod.isStatic()) {
+      if (method.isStatic()) {
         return;
       }
-      if (oldMethod.isAbstract()) {
+      if (method.isAbstract()) {
         return;
       }
-      if (oldMethod.isNative()) {
+      if (method.isNative()) {
         return;
       }
-      if (oldMethod == program.getNullMethod()) {
+      if (method == program.getNullMethod()) {
         // Special case: we don't make calls to this method static.
         return;
       }
 
-      // Did we already do this one?
-      if (program.getStaticImpl(oldMethod) != null) {
-        return;
-      }
-
-      JClassType enclosingType = (JClassType) oldMethod.getEnclosingType();
-      JType oldReturnType = oldMethod.getType();
-
-      // Create the new static method
-      String newName = "$" + oldMethod.getName();
-
-      /*
-       * Don't use thie JProgram helper because it auto-adds the new method to
-       * its enclosing class, which will break iteration.
-       */
-      JMethod newMethod = new JMethod(program, newName, enclosingType,
-          oldReturnType, false, true, true, oldMethod.isPrivate());
-
-      // Setup all params and locals; map from the old method to the new method
-      JParameter thisParam = program.createParameter(
-          "this$static".toCharArray(), enclosingType, true, newMethod);
-      Map/* <JVariable, JVariable> */varMap = new IdentityHashMap();
-      for (int i = 0; i < oldMethod.params.size(); ++i) {
-        JParameter oldVar = (JParameter) oldMethod.params.get(i);
-        JParameter newVar = program.createParameter(
-            oldVar.getName().toCharArray(), oldVar.getType(), oldVar.isFinal(),
-            newMethod);
-        varMap.put(oldVar, newVar);
-      }
-
-      newMethod.freezeParamTypes();
-      for (int i = 0; i < oldMethod.locals.size(); ++i) {
-        JLocal oldVar = (JLocal) oldMethod.locals.get(i);
-        JLocal newVar = program.createLocal(oldVar.getName().toCharArray(),
-            oldVar.getType(), oldVar.isFinal(), newMethod);
-        varMap.put(oldVar, newVar);
-      }
-      ChangeList myChangeList = new ChangeList("Create a new static method '"
-          + newMethod + "' for instance method '" + oldMethod + "'");
-      myChangeList.addMethod(newMethod);
-      program.putStaticImpl(oldMethod, newMethod);
-
-      // rewrite the method body to update all thisRefs to instance refs
-      ChangeList subChangeList = new ChangeList(
-          "Update thisrefs as paramrefs; update paramrefs and localrefs to target this method.");
-      RewriteMethodBody rewriter = new RewriteMethodBody(thisParam, varMap,
-          subChangeList);
-      oldMethod.traverse(rewriter);
-      myChangeList.add(subChangeList);
-
-      // Move the body of the instance method to the static method
-      myChangeList.clear(oldMethod.locals);
-      myChangeList.moveBody(oldMethod, newMethod);
-
-      // delegate from the instance method to the static method
-      JMethodCall newCall = new JMethodCall(program, null, newMethod);
-      newCall.args.add(program.getExpressionThisRef(enclosingType));
-      for (int i = 0; i < oldMethod.params.size(); ++i) {
-        JParameter param = (JParameter) oldMethod.params.get(i);
-        newCall.args.add(new JParameterRef(program, param));
-      }
-      JStatement statement;
-      if (oldReturnType == program.getTypeVoid()) {
-        statement = new JExpressionStatement(program, newCall);
-      } else {
-        statement = new JReturnStatement(program, newCall);
-      }
-      myChangeList.addStatement(statement, oldMethod.body);
-      changeList.add(myChangeList);
-    }
-
-    public ChangeList getChangeList() {
-      return changeList;
+      // Let's do it!
+      toBeMadeStatic.add(method);
     }
   }
 
@@ -165,10 +184,7 @@
    * CreateStaticMethodVisitor, go and rewrite the call sites to call the static
    * method instead.
    */
-  private class RewriteCallSites extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Rewrite calls to final instance methods as calls to static impl methods.");
+  private class RewriteCallSites extends JModVisitor {
 
     /*
      * In cases where callers are directly referencing (effectively) final
@@ -176,33 +192,24 @@
      * static method instead.
      */
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
+    public void endVisit(JMethodCall x, Context ctx) {
       JMethod oldMethod = x.getTarget();
       JMethod newMethod = program.getStaticImpl(oldMethod);
       if (newMethod == null || x.canBePolymorphic()) {
         return;
       }
 
-      ChangeList changes = new ChangeList("Replace '" + x
-          + "' with a static call");
-
       // Update the call site
-      JMethodCall newCall = new JMethodCall(program, null, newMethod);
-      changes.replaceExpression(m, newCall);
+      JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(), null,
+          newMethod);
 
       // The qualifier becomes the first arg
-      changes.addExpression(x.instance, newCall.args);
+      newCall.getArgs().add(x.getInstance());
       // Copy the rest of the args
-      for (int i = 0; i < x.args.size(); ++i) {
-        Mutator arg = x.args.getMutator(i);
-        changes.addExpression(arg, newCall.args);
+      for (int i = 0; i < x.getArgs().size(); ++i) {
+        newCall.getArgs().add(x.getArgs().get(i));
       }
-
-      changeList.add(changes);
-    }
-
-    public ChangeList getChangeList() {
-      return changeList;
+      ctx.replaceMe(newCall);
     }
   }
 
@@ -210,37 +217,37 @@
    * When code is moved from an instance method to a static method, all this
    * refs must be replaced with param refs to the synthetic this param.
    */
-  private class RewriteMethodBody extends JVisitor {
+  private class RewriteMethodBody extends JModVisitor {
 
-    private final ChangeList changeList;
     private final JParameter thisParam;
     private final Map/* <JVariable, JVariable> */varMap;
 
     public RewriteMethodBody(JParameter thisParam,
-        Map/* <JVariable, JVariable> */varMap, ChangeList changeList) {
-      this.changeList = changeList;
+        Map/* <JVariable, JVariable> */varMap) {
       this.thisParam = thisParam;
       this.varMap = varMap;
     }
 
     // @Override
-    public void endVisit(JLocalRef x, Mutator m) {
+    public void endVisit(JLocalRef x, Context ctx) {
       JLocal local = (JLocal) varMap.get(x.getTarget());
-      JLocalRef localRef = new JLocalRef(program, local);
-      changeList.replaceExpression(m, localRef);
+      JLocalRef localRef = new JLocalRef(program, x.getSourceInfo(), local);
+      ctx.replaceMe(localRef);
     }
 
     // @Override
-    public void endVisit(JParameterRef x, Mutator m) {
+    public void endVisit(JParameterRef x, Context ctx) {
       JParameter param = (JParameter) varMap.get(x.getTarget());
-      JParameterRef paramRef = new JParameterRef(program, param);
-      changeList.replaceExpression(m, paramRef);
+      JParameterRef paramRef = new JParameterRef(program, x.getSourceInfo(),
+          param);
+      ctx.replaceMe(paramRef);
     }
 
     // @Override
-    public void endVisit(JThisRef x, Mutator m) {
-      JParameterRef paramRef = new JParameterRef(program, thisParam);
-      changeList.replaceExpression(m, paramRef);
+    public void endVisit(JThisRef x, Context ctx) {
+      JParameterRef paramRef = new JParameterRef(program, x.getSourceInfo(),
+          thisParam);
+      ctx.replaceMe(paramRef);
     }
   }
 
@@ -248,6 +255,8 @@
     return new MakeCallsStatic(program).execImpl();
   }
 
+  public Set toBeMadeStatic = new HashSet();
+
   private final JProgram program;
 
   private MakeCallsStatic(JProgram program) {
@@ -255,24 +264,21 @@
   }
 
   private boolean execImpl() {
-    {
-      CreateStaticMethodVisitor creator = new CreateStaticMethodVisitor();
-      program.traverse(creator);
-      ChangeList changes = creator.getChangeList();
-      if (changes.empty()) {
-        return false;
-      }
-      changes.apply();
+    FindStaticDispatchSitesVisitor finder = new FindStaticDispatchSitesVisitor();
+    finder.accept(program);
+    if (toBeMadeStatic.isEmpty()) {
+      return false;
     }
 
-    {
-      RewriteCallSites rewriter = new RewriteCallSites();
-      program.traverse(rewriter);
-      ChangeList changes = rewriter.getChangeList();
-      assert (!changes.empty());
-      changes.apply();
+    CreateStaticImplsVisitor creator = new CreateStaticImplsVisitor();
+    creator.accept(program);
+    if (!creator.didChange()) {
+      return false;
     }
 
+    RewriteCallSites rewriter = new RewriteCallSites();
+    rewriter.accept(program);
+    assert (rewriter.didChange());
     return true;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java
index a2ded14..950b5c1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java
@@ -15,13 +15,13 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
 
 import java.util.HashSet;
@@ -46,45 +46,46 @@
    */
   private class FinalizeVisitor extends JVisitor {
 
-    private final ChangeList changeList = new ChangeList(
-        "Finalize effectively final methods and types.");
+    private boolean didChange = false;
 
-    public ChangeList getChangeList() {
-      return changeList;
+    public boolean didChange() {
+      return didChange;
     }
 
     // @Override
-    public boolean visit(JClassType x) {
+    public boolean visit(JClassType x, Context ctx) {
       if (!x.isFinal() && !isSubclassed.contains(x)) {
-        changeList.makeFinal(x);
+        x.setFinal(true);
+        didChange = true;
       }
       for (int i = 0; i < x.methods.size(); ++i) {
         JMethod method = (JMethod) x.methods.get(i);
-        method.traverse(this);
+        accept(method);
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JInterfaceType x) {
+    public boolean visit(JInterfaceType x, Context ctx) {
       for (int i = 0; i < x.methods.size(); ++i) {
         JMethod method = (JMethod) x.methods.get(i);
-        method.traverse(this);
+        accept(method);
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       if (!x.isFinal() && !isOverriden.contains(x)) {
-        changeList.makeFinal(x);
+        x.setFinal(true);
+        didChange = true;
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JsniMethod x) {
-      return visit((JMethod) x);
+    public boolean visit(JsniMethod x, Context ctx) {
+      return visit((JMethod) x, ctx);
     }
   }
   /**
@@ -93,29 +94,29 @@
   private class MarkVisitor extends JVisitor {
 
     // @Override
-    public boolean visit(JClassType x) {
+    public boolean visit(JClassType x, Context ctx) {
       if (x.extnds != null) {
         isSubclassed.add(x.extnds);
       }
 
       for (int i = 0; i < x.methods.size(); ++i) {
         JMethod method = (JMethod) x.methods.get(i);
-        method.traverse(this);
+        accept(method);
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JInterfaceType x) {
+    public boolean visit(JInterfaceType x, Context ctx) {
       for (int i = 0; i < x.methods.size(); ++i) {
         JMethod method = (JMethod) x.methods.get(i);
-        method.traverse(this);
+        accept(method);
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       for (int i = 0; i < x.overrides.size(); ++i) {
         JMethod it = (JMethod) x.overrides.get(i);
         isOverriden.add(it);
@@ -124,17 +125,17 @@
     }
 
     // @Override
-    public boolean visit(JProgram x) {
+    public boolean visit(JProgram x, Context ctx) {
       for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
         JReferenceType type = (JReferenceType) x.getDeclaredTypes().get(i);
-        type.traverse(this);
+        accept(type);
       }
       return false;
     }
 
     // @Override
-    public boolean visit(JsniMethod x) {
-      return visit((JMethod) x);
+    public boolean visit(JsniMethod x, Context ctx) {
+      return visit((JMethod) x, ctx);
     }
   }
 
@@ -142,24 +143,20 @@
     return new MethodAndClassFinalizer().execImpl(program);
   }
 
-  private final Set/* <JClassType> */isSubclassed = new HashSet/* <JClassType> */();
-
   private final Set/* <JMethod> */isOverriden = new HashSet/* <JMethod> */();
 
+  private final Set/* <JClassType> */isSubclassed = new HashSet/* <JClassType> */();
+
   private MethodAndClassFinalizer() {
   }
 
   private boolean execImpl(JProgram program) {
     MarkVisitor marker = new MarkVisitor();
-    program.traverse(marker);
+    marker.accept(program);
+
     FinalizeVisitor finalizer = new FinalizeVisitor();
-    program.traverse(finalizer);
-    ChangeList changes = finalizer.getChangeList();
-    if (changes.empty()) {
-      return false;
-    }
-    changes.apply();
-    return true;
+    finalizer.accept(program);
+    return finalizer.didChange();
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
index 141460d..1b002ec 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
@@ -15,19 +15,18 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 
 /**
  * Update polymorphic method calls to tighter bindings based on the type of the
@@ -43,12 +42,10 @@
    * Updates polymorphic method calls to tighter bindings based on the type of
    * the qualifier.
    */
-  public class MethodCallTighteningVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Update polymorphic method calls to tighter bindings based on the type of the qualifier.");
+  public class MethodCallTighteningVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
+    public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
       JExpression instance = x.getInstance();
 
@@ -100,30 +97,14 @@
         return;
       }
 
-      ChangeList changes = new ChangeList("Replace call '" + x + "' to type '"
-          + enclosingType + "' with a call to type '"
-          + foundMethod.getEnclosingType() + "'");
-
-      // Update the call site
-      JMethodCall call = new JMethodCall(program, null, foundMethod);
-      changes.replaceExpression(m, call);
-
-      // Copy the qualifier
-      changes.replaceExpression(call.instance, x.instance);
-
-      // Copy the args
-      for (int i = 0; i < x.args.size(); ++i) {
-        Mutator arg = x.args.getMutator(i);
-        changes.addExpression(arg, call.args);
-      }
-
-      changeList.add(changes);
-
-      return;
-    }
-
-    public ChangeList getChangeList() {
-      return changeList;
+      /*
+       * Replace the call to the original method with a call to the same method
+       * on the tighter type.
+       */
+      JMethodCall call = new JMethodCall(program, x.getSourceInfo(),
+          x.getInstance(), foundMethod);
+      call.getArgs().addAll(x.getArgs());
+      ctx.replaceMe(call);
     }
   }
 
@@ -139,14 +120,8 @@
 
   private boolean execImpl() {
     MethodCallTighteningVisitor tightener = new MethodCallTighteningVisitor();
-    program.traverse(tightener);
-    ChangeList changes = tightener.getChangeList();
-    if (changes.empty()) {
-      return false;
-    }
-
-    changes.apply();
-    return true;
+    tightener.accept(program);
+    return tightener.didChange();
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
index 3cce0f6..26f9e97 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -15,8 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
-import com.google.gwt.dev.jjs.ast.Holder;
-import com.google.gwt.dev.jjs.ast.HolderList;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBlock;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
@@ -25,16 +24,18 @@
 import com.google.gwt.dev.jjs.ast.JLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -47,55 +48,61 @@
 
   /**
    * Flattens <code>JMultiExpressions</code> where possible.
+   * 
+   * TODO: make this a JModVisitor
    */
   public class FlattenMultiVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Flatten multis where possible.");
 
-    // @Override
-    public void endVisit(JMultiExpression x, Mutator m) {
-      HolderList exprs = x.exprs;
+    private boolean didChange = false;
 
-      // do all adds FIRST or the indices will be wrong
-      for (int i = 0; i < exprs.size(); ++i) {
-        JExpression expr = exprs.getExpr(i);
-        if (expr instanceof JMultiExpression) {
-          JMultiExpression sub = (JMultiExpression) expr;
-          changeList.addAll(sub.exprs, i, exprs);
-        }
-      }
-
-      // now remove the old multi
-      for (int i = 0; i < exprs.size(); ++i) {
-        JExpression expr = exprs.getExpr(i);
-        if (expr instanceof JMultiExpression) {
-          changeList.removeNode(exprs.getMutator(i), exprs);
-        }
-      }
+    public boolean didChange() {
+      return didChange;
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
+    // @Override
+    public void endVisit(JMultiExpression x, Context ctx) {
+      ArrayList exprs = x.exprs;
+
+      /*
+       * Add the contents of all nested multis into the top multi, in place. We
+       * are in fact iterating over nodes we've just added, but that should be
+       * okay as the children will already be flattened.
+       */
+      for (int i = 0; i < exprs.size(); ++i) {
+        JExpression expr = (JExpression) exprs.get(i);
+        if (expr instanceof JMultiExpression) {
+          JMultiExpression sub = (JMultiExpression) expr;
+          exprs.addAll(i + 1, sub.exprs);
+          didChange = true;
+        }
+      }
+
+      // now remove the old multis
+      for (Iterator it = exprs.iterator(); it.hasNext();) {
+        JExpression expr = (JExpression) it.next();
+        if (expr instanceof JMultiExpression) {
+          it.remove();
+          didChange = true;
+        }
+      }
     }
   }
   /**
    * Method inlining visitor.
    */
-  public class InliningVisitor extends JVisitor {
+  public class InliningVisitor extends JModVisitor {
     /**
      * Resets with each new visitor, which is good since things that couldn't be
      * inlined before might become inlineable.
      */
     Set/* <JMethod> */cannotInline = new HashSet/* <JMethod> */();
 
-    private final ChangeList changeList = new ChangeList("Inline methods.");
-
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
       currentMethod = null;
     }
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
+    public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
 
       // The method call must be known statically
@@ -110,12 +117,16 @@
       List/* <JStatement> */stmts = method.body.statements;
       boolean possibleToInline = false;
       if (stmts.isEmpty()) {
-        possibleToInline = inlineEmptyMethodCall(x, m, method);
+        inlineEmptyMethodCall(x, ctx);
+        possibleToInline = true;
       } else if (stmts.size() == 1) {
         JStatement stmt = (JStatement) stmts.get(0);
         if (stmt instanceof JReturnStatement) {
-          possibleToInline = tryInlineSimpleMethodCall(x, m, method,
-              (JReturnStatement) stmt);
+          possibleToInline = tryInlineExpression(x, ctx,
+              ((JReturnStatement) stmt).getExpr());
+        } else if (stmt instanceof JExpressionStatement) {
+          possibleToInline = tryInlineExpression(x, ctx,
+              ((JExpressionStatement) stmt).getExpr());
         }
       }
 
@@ -124,11 +135,7 @@
       }
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
       return true;
     }
@@ -148,10 +155,10 @@
      * should be inlinable, but we have to first examine the field reference and
      * then recursively determine that the qualifier is inlinable.
      */
-    private Mutator canInlineResultExpression(JExpression targetReturnExpr,
-        List/* <JParameter> */params, HolderList args, int[] magicArg,
-        ChangeList changes) {
-      if (targetReturnExpr instanceof JLiteral) {
+    private JExpression canInlineExpression(JSourceInfo info,
+        JExpression targetExpr, List/* <JParameter> */params, ArrayList args,
+        int[] magicArg) {
+      if (targetExpr instanceof JLiteral) {
         // just reference the same JLiteral
         /*
          * hackish: pretend there is an arg that is returned which comes after
@@ -159,29 +166,27 @@
          * tryInlineSimpleMethodCall to succeed
          */
         magicArg[0] = args.size();
-        return new Holder(targetReturnExpr);
-      } else if (targetReturnExpr instanceof JParameterRef) {
+        return targetExpr;
+      } else if (targetExpr instanceof JParameterRef) {
         // translate the param ref into the appropriate arg
-        int i = params.indexOf(((JParameterRef) targetReturnExpr).getTarget());
+        int i = params.indexOf(((JParameterRef) targetExpr).getTarget());
         assert (i >= 0);
         magicArg[0] = i;
-        return args.getMutator(i);
-      } else if (targetReturnExpr instanceof JFieldRef) {
-        JFieldRef oldFieldRef = (JFieldRef) targetReturnExpr;
+        return (JExpression) args.get(i);
+      } else if (targetExpr instanceof JFieldRef) {
+        JFieldRef oldFieldRef = (JFieldRef) targetExpr;
         JField field = oldFieldRef.getField();
         JExpression instance = oldFieldRef.getInstance();
-        JFieldRef newFieldRef = new JFieldRef(program, null, field,
-            currentMethod.getEnclosingType());
         if (instance != null) {
           // If an instance field, we have to be able to inline the qualifier
-          Mutator instanceMutator = canInlineResultExpression(instance, params,
-              args, magicArg, changes);
-          if (instanceMutator == null) {
+          instance = canInlineExpression(info, instance, params, args, magicArg);
+          if (instance == null) {
             return null;
           }
-          changes.replaceExpression(newFieldRef.instance, instanceMutator);
         }
-        return new Holder(newFieldRef);
+        JFieldRef newFieldRef = new JFieldRef(program, info, instance, field,
+            currentMethod.getEnclosingType());
+        return newFieldRef;
       } else {
         /*
          * For now, only inline REALLY trivial stuff since we have no way of
@@ -191,42 +196,38 @@
       }
     }
 
-    private boolean inlineEmptyMethodCall(JMethodCall x, Mutator m,
-        JMethod method) {
-      ChangeList changes = new ChangeList("Inline a call to empty method '"
-          + method + "'");
-      JMultiExpression multi = new JMultiExpression(program);
+    /**
+     * Inlines a call to an empty method.
+     */
+    private void inlineEmptyMethodCall(JMethodCall x, Context ctx) {
+      JMultiExpression multi = new JMultiExpression(program, x.getSourceInfo());
       JExpression instance = x.getInstance();
       if (instance != null && instance.hasSideEffects()) {
-        changes.addExpression(x.instance, multi.exprs);
+        multi.exprs.add(x.getInstance());
       }
-      for (int i = 0, c = x.args.size(); i < c; ++i) {
-        if (x.args.getExpr(i).hasSideEffects()) {
-          changes.addExpression(x.args.getMutator(i), multi.exprs);
+      for (int i = 0, c = x.getArgs().size(); i < c; ++i) {
+        if (((JExpression) x.getArgs().get(i)).hasSideEffects()) {
+          multi.exprs.add(x.getArgs().get(i));
         }
       }
-
-      changes.replaceExpression(m, multi);
-      changeList.add(changes);
-      return true;
+      ctx.replaceMe(multi);
     }
 
-    private boolean tryInlineSimpleMethodCall(JMethodCall x, Mutator m,
-        JMethod method, JReturnStatement returnStmt) {
-      List/* <JParameter> */params = method.params;
-      HolderList args = x.args;
-
-      ChangeList changes = new ChangeList("Inline a call to simple method '"
-          + method + "'");
+    /**
+     * Inline a call to a method that contains only a return statement.
+     */
+    private boolean tryInlineExpression(JMethodCall x, Context ctx,
+        JExpression targetExpr) {
+      List/* <JParameter> */params = x.getTarget().params;
+      ArrayList args = x.getArgs();
 
       // the expression returned by the inlined method, if any
-      Mutator resultExpression;
+      JExpression resultExpression;
       // the argument that is returned by the inlined method, if any
       int magicArg[] = new int[1];
 
-      JExpression targetReturnExpr = returnStmt.getExpression();
-      resultExpression = canInlineResultExpression(targetReturnExpr, params,
-          args, magicArg, changes);
+      resultExpression = canInlineExpression(x.getSourceInfo(), targetExpr,
+          params, args, magicArg);
 
       if (resultExpression == null) {
         return false; // cannot inline
@@ -235,20 +236,20 @@
       // the argument that is returned by the inlined method
       int iMagicArg = magicArg[0];
 
-      JMultiExpression multi = new JMultiExpression(program);
+      JMultiExpression multi = new JMultiExpression(program, x.getSourceInfo());
 
       // Evaluate the instance argument (we can have one even with static calls)
       JExpression instance = x.getInstance();
       if (instance != null && instance.hasSideEffects()) {
-        changes.addExpression(x.instance, multi.exprs);
+        multi.exprs.add(x.getInstance());
       }
 
       // Now evaluate any side-effect args that aren't the magic arg.
       for (int i = 0; i < params.size(); ++i) {
-        if (args.getExpr(i).hasSideEffects()) {
+        if (((JExpression) args.get(i)).hasSideEffects()) {
           if (i < iMagicArg) {
             // evaluate this arg inside of the multi
-            changes.addExpression(args.getMutator(i), multi.exprs);
+            multi.exprs.add(args.get(i));
           } else if (i == iMagicArg) {
             // skip this arg, we'll do it below as the final one
           } else {
@@ -268,9 +269,8 @@
       }
 
       // add in the result expression as the last item in the multi
-      changes.addExpression(resultExpression, multi.exprs);
-      changes.replaceExpression(m, multi);
-      changeList.add(changes);
+      multi.exprs.add(resultExpression);
+      ctx.replaceMe(multi);
       return true;
     }
   }
@@ -278,13 +278,26 @@
   /**
    * Reduces <code>JMultiExpression</code> where possible.
    */
-  public class ReduceMultiVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Reduce multis where possible.");
+  public class ReduceMultiVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JMultiExpression x, Mutator m) {
-      HolderList exprs = x.exprs;
+    public void endVisit(JBlock x, Context ctx) {
+      for (Iterator it = x.statements.iterator(); it.hasNext();) {
+        JStatement stmt = (JStatement) it.next();
+        // If we're a JExprStmt with no side effects, just remove me
+        if (stmt instanceof JExpressionStatement) {
+          JExpression expr = ((JExpressionStatement) stmt).getExpr();
+          if (!expr.hasSideEffects()) {
+            it.remove();
+            didChange = true;
+          }
+        }
+      }
+    }
+
+    // @Override
+    public void endVisit(JMultiExpression x, Context ctx) {
+      ArrayList exprs = x.exprs;
 
       final int c = exprs.size();
       if (c == 0) {
@@ -293,42 +306,28 @@
 
       int countSideEffectsBeforeLast = 0;
       for (int i = 0; i < c - 1; ++i) {
-        JExpression expr = exprs.getExpr(i);
+        JExpression expr = (JExpression) exprs.get(i);
         if (expr.hasSideEffects()) {
           ++countSideEffectsBeforeLast;
         }
       }
 
       if (countSideEffectsBeforeLast == 0) {
-        changeList.replaceExpression(m, x.exprs.getMutator(c - 1));
+        ctx.replaceMe((JExpression) x.exprs.get(c - 1));
       } else {
+        JMultiExpression newMulti = new JMultiExpression(program,
+            x.getSourceInfo());
         for (int i = 0; i < c - 1; ++i) {
-          JExpression expr = exprs.getExpr(i);
-          if (!expr.hasSideEffects()) {
-            changeList.removeNode(x.exprs.getMutator(i), exprs);
+          JExpression expr = (JExpression) exprs.get(i);
+          if (expr.hasSideEffects()) {
+            newMulti.exprs.add(expr);
           }
         }
-      }
-    }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    // @Override
-    public boolean visit(JBlock x) {
-      for (int i = 0; i < x.statements.size(); ++i) {
-        JStatement stmt = (JStatement) x.statements.get(i);
-        stmt.traverse(this);
-        // If we're a JExprStmt with no side effects, just remove me
-        if (stmt instanceof JExpressionStatement) {
-          JExpression expr = ((JExpressionStatement) stmt).getExpression();
-          if (!expr.hasSideEffects()) {
-            changeList.removeNode(stmt, x.statements);
-          }
+        newMulti.exprs.add(x.exprs.get(c - 1));
+        if (newMulti.exprs.size() < x.exprs.size()) {
+          ctx.replaceMe(newMulti);
         }
       }
-      return false;
     }
   }
 
@@ -346,34 +345,18 @@
   private boolean execImpl() {
     boolean madeChanges = false;
     while (true) {
-      {
-        InliningVisitor inliner = new InliningVisitor();
-        program.traverse(inliner);
-        ChangeList changes = inliner.getChangeList();
-        if (changes.empty()) {
-          break;
-        }
-        changes.apply();
-        madeChanges = true;
+      InliningVisitor inliner = new InliningVisitor();
+      inliner.accept(program);
+      if (!inliner.didChange()) {
+        break;
       }
+      madeChanges = true;
 
-      {
-        FlattenMultiVisitor flattener = new FlattenMultiVisitor();
-        program.traverse(flattener);
-        ChangeList changes = flattener.getChangeList();
-        if (!changes.empty()) {
-          changes.apply();
-        }
-      }
+      FlattenMultiVisitor flattener = new FlattenMultiVisitor();
+      flattener.accept(program);
 
-      {
-        ReduceMultiVisitor reducer = new ReduceMultiVisitor();
-        program.traverse(reducer);
-        ChangeList changes = reducer.getChangeList();
-        if (!changes.empty()) {
-          changes.apply();
-        }
-      }
+      ReduceMultiVisitor reducer = new ReduceMultiVisitor();
+      reducer.accept(program);
     }
     return madeChanges;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index a9c22a4..4291e55 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
@@ -35,14 +36,13 @@
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -63,6 +63,8 @@
  * failures at compile time.
  * 
  * TODO(later): prune params and locals
+ * 
+ * TODO(later): make RescueVisitor use less stack?
  */
 public class Pruner {
 
@@ -72,31 +74,33 @@
    */
   private class PruneVisitor extends JVisitor {
 
-    private final ChangeList changeList = new ChangeList(
-        "Prune unreferenced methods, fields, and types.");
+    private boolean didChange = false;
 
-    public ChangeList getChangeList() {
-      return changeList;
+    public boolean didChange() {
+      return didChange;
     }
 
     // @Override
-    public boolean visit(JClassType type) {
+    public boolean visit(JClassType type, Context ctx) {
 
       assert (referencedTypes.contains(type));
       boolean isInstantiated = program.typeOracle.isInstantiatedType(type);
 
-      for (int i = 0; i < type.fields.size(); ++i) {
-        JField it = (JField) type.fields.get(i);
-        if (!referencedNonTypes.contains(it)
-            || pruneViaNoninstantiability(isInstantiated, it)) {
-          changeList.removeField(it);
+      for (Iterator it = type.fields.iterator(); it.hasNext();) {
+        JField field = (JField) it.next();
+        if (!referencedNonTypes.contains(field)
+            || pruneViaNoninstantiability(isInstantiated, field)) {
+          it.remove();
+          didChange = true;
         }
       }
-      for (int i = 0; i < type.methods.size(); ++i) {
-        JMethod it = (JMethod) type.methods.get(i);
-        if (!methodIsReferenced(it)
-            || pruneViaNoninstantiability(isInstantiated, it)) {
-          changeList.removeMethod(it);
+
+      for (Iterator it = type.methods.iterator(); it.hasNext();) {
+        JMethod method = (JMethod) it.next();
+        if (!methodIsReferenced(method)
+            || pruneViaNoninstantiability(isInstantiated, method)) {
+          it.remove();
+          didChange = true;
         }
       }
 
@@ -104,23 +108,30 @@
     }
 
     // @Override
-    public boolean visit(JInterfaceType type) {
+    public boolean visit(JInterfaceType type, Context ctx) {
       boolean isReferenced = referencedTypes.contains(type);
       boolean isInstantiated = program.typeOracle.isInstantiatedType(type);
 
-      for (int i = 0; i < type.fields.size(); ++i) {
-        JField it = (JField) type.fields.get(i);
+      for (Iterator it = type.fields.iterator(); it.hasNext();) {
+        JField field = (JField) it.next();
         // all interface fields are static and final
-        if (!isReferenced || !referencedNonTypes.contains(it)) {
-          changeList.removeField(it);
+        if (!isReferenced || !referencedNonTypes.contains(field)) {
+          it.remove();
+          didChange = true;
         }
       }
-      // start at index 1; never prune clinit directly out of the interface
-      for (int i = 1; i < type.methods.size(); ++i) {
-        JMethod it = (JMethod) type.methods.get(i);
+
+      Iterator it = type.methods.iterator();
+      if (it.hasNext()) {
+        // start at index 1; never prune clinit directly out of the interface
+        it.next();
+      }
+      while (it.hasNext()) {
+        JMethod method = (JMethod) it.next();
         // all other interface methods are instance and abstract
-        if (!isInstantiated || !methodIsReferenced(it)) {
-          changeList.removeMethod(it);
+        if (!isInstantiated || !methodIsReferenced(method)) {
+          it.remove();
+          didChange = true;
         }
       }
 
@@ -128,14 +139,15 @@
     }
 
     // @Override
-    public boolean visit(JProgram program) {
+    public boolean visit(JProgram program, Context ctx) {
       for (int i = 0; i < program.getDeclaredTypes().size(); ++i) {
         JReferenceType type = (JReferenceType) program.getDeclaredTypes().get(i);
         if (referencedTypes.contains(type)
             || program.typeOracle.isInstantiatedType(type)) {
-          type.traverse(this);
+          accept(type);
         } else {
-          changeList.removeType(type);
+          program.getDeclaredTypes().remove(type);
+          didChange = true;
         }
       }
       return false;
@@ -175,6 +187,7 @@
       return (!isInstantiated && (!it.isStatic() || program.isStaticImpl(it)));
     }
   }
+
   /**
    * Marks as "referenced" any types, methods, and fields that are reachable.
    * Also marks as "instantiable" any the classes and interfaces that can
@@ -189,9 +202,9 @@
     }
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       // special string concat handling
-      if (x.op == JBinaryOperator.ADD
+      if (x.getOp() == JBinaryOperator.ADD
           && x.getType() == program.getTypeJavaLangString()) {
         rescueByConcat(x.getLhs().getType());
         rescueByConcat(x.getRhs().getType());
@@ -199,7 +212,7 @@
     }
 
     // @Override
-    public boolean visit(JArrayType type) {
+    public boolean visit(JArrayType type, Context ctx) {
       assert (referencedTypes.contains(type));
       boolean isInstantiated = instantiatedTypes.contains(type);
 
@@ -225,7 +238,7 @@
     }
 
     // @Override
-    public boolean visit(JClassLiteral literal, Mutator mutator) {
+    public boolean visit(JClassLiteral literal, Context ctx) {
       // rescue and instantiate java.lang.Class
       // JLS 12.4.1: do not rescue the target type
       rescue(program.getTypeJavaLangClass(), true, true);
@@ -233,7 +246,7 @@
     }
 
     // @Override
-    public boolean visit(JClassType type) {
+    public boolean visit(JClassType type, Context ctx) {
       assert (referencedTypes.contains(type));
       boolean isInstantiated = instantiatedTypes.contains(type);
 
@@ -266,7 +279,7 @@
     }
 
     // @Override
-    public boolean visit(JFieldRef ref, Mutator mutator) {
+    public boolean visit(JFieldRef ref, Context ctx) {
       JField target = ref.getField();
 
       // JLS 12.4.1: references to static, non-final, or
@@ -281,7 +294,7 @@
     }
 
     // @Override
-    public boolean visit(JInterfaceType type) {
+    public boolean visit(JInterfaceType type, Context ctx) {
       boolean isReferenced = referencedTypes.contains(type);
       boolean isInstantiated = instantiatedTypes.contains(type);
       assert (isReferenced || isInstantiated);
@@ -301,14 +314,14 @@
       // visit any field initializers
       for (int i = 0; i < type.fields.size(); ++i) {
         JField it = (JField) type.fields.get(i);
-        it.traverse(this);
+        accept(it);
       }
 
       return false;
     }
 
     // @Override
-    public boolean visit(JMethodCall call, Mutator mutator) {
+    public boolean visit(JMethodCall call, Context ctx) {
       JMethod target = call.getTarget();
       // JLS 12.4.1: references to static methods rescue the enclosing class
       if (target.isStatic()) {
@@ -319,12 +332,12 @@
     }
 
     // @Override
-    public boolean visit(JNewArray newArray, Mutator mutator) {
+    public boolean visit(JNewArray newArray, Context ctx) {
       // rescue and instantiate the array type
       JArrayType arrayType = newArray.getArrayType();
       if (newArray.dims != null) {
         // rescue my type and all the implicitly nested types (with fewer dims)
-        int nDims = arrayType.dims;
+        int nDims = arrayType.getDims();
         JType leafType = arrayType.getLeafType();
         assert (newArray.dims.size() == nDims);
         for (int i = 0; i < nDims; ++i) {
@@ -344,28 +357,28 @@
     }
 
     // @Override
-    public boolean visit(JNewInstance newInstance, Mutator mutator) {
+    public boolean visit(JNewInstance newInstance, Context ctx) {
       // rescue and instantiate the target class!
       rescue(newInstance.getClassType(), true, true);
       return true;
     }
 
     // @Override
-    public boolean visit(JsniFieldRef x) {
+    public boolean visit(JsniFieldRef x, Context ctx) {
       /*
        * SPECIAL: this could be an assignment that passes a value from
        * JavaScript into Java.
        * 
-       * TODO: technically we only need to do this if the field is being
+       * TODO(later): technically we only need to do this if the field is being
        * assigned to.
        */
       maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
       // JsniFieldRef rescues as JFieldRef
-      return visit(/* (JFieldRef) */x, null);
+      return visit((JFieldRef) x, ctx);
     }
 
     // @Override
-    public boolean visit(JsniMethodRef x) {
+    public boolean visit(JsniMethodRef x, Context ctx) {
       /*
        * SPECIAL: each argument of the call passes a value from JavaScript into
        * Java.
@@ -376,11 +389,11 @@
         maybeRescueJavaScriptObjectPassingIntoJava(param.getType());
       }
       // JsniMethodRef rescues as JMethodCall
-      return visit(/* (JMethodCall) */x, null);
+      return visit((JMethodCall) x, ctx);
     }
 
     // @Override
-    public boolean visit(JStringLiteral literal, Mutator mutator) {
+    public boolean visit(JStringLiteral literal, Context ctx) {
       // rescue and instantiate java.lang.String
       rescue(program.getTypeJavaLangString(), true, true);
       return true;
@@ -406,60 +419,51 @@
       }
     }
 
-    private boolean rescue(JField field) {
-      if (field == null) {
-        return false;
+    private void rescue(JField field) {
+      if (field != null) {
+        if (!referencedNonTypes.contains(field)) {
+          referencedNonTypes.add(field);
+        }
       }
-
-      if (!referencedNonTypes.contains(field)) {
-        referencedNonTypes.add(field);
-        return true;
-      }
-      return true;
     }
 
     private boolean rescue(JMethod method) {
-      if (method == null) {
-        return false;
-      }
-
-      if (!referencedNonTypes.contains(method)) {
-        referencedNonTypes.add(method);
-        method.traverse(this);
-        if (method instanceof JsniMethod) {
-          /*
-           * SPECIAL: returning from this method passes a value from JavaScript
-           * into Java.
-           */
-          maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
+      if (method != null) {
+        if (!referencedNonTypes.contains(method)) {
+          referencedNonTypes.add(method);
+          accept(method);
+          if (method instanceof JsniMethod) {
+            /*
+             * SPECIAL: returning from this method passes a value from
+             * JavaScript into Java.
+             */
+            maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
+          }
+          return true;
         }
-        return true;
       }
       return false;
     }
 
-    private boolean rescue(JReferenceType type, boolean isReferenced,
+    private void rescue(JReferenceType type, boolean isReferenced,
         boolean isInstantiated) {
-      if (type == null) {
-        return false;
-      }
+      if (type != null) {
 
-      boolean doVisit = false;
-      if (isInstantiated && !instantiatedTypes.contains(type)) {
-        instantiatedTypes.add(type);
-        doVisit = true;
-      }
+        boolean doVisit = false;
+        if (isInstantiated && !instantiatedTypes.contains(type)) {
+          instantiatedTypes.add(type);
+          doVisit = true;
+        }
 
-      if (isReferenced && !referencedTypes.contains(type)) {
-        referencedTypes.add(type);
-        doVisit = true;
-      }
+        if (isReferenced && !referencedTypes.contains(type)) {
+          referencedTypes.add(type);
+          doVisit = true;
+        }
 
-      if (doVisit) {
-        type.traverse(this);
+        if (doVisit) {
+          accept(type);
+        }
       }
-
-      return doVisit;
     }
 
     /**
@@ -517,7 +521,7 @@
     }
 
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       if (referencedNonTypes.contains(x)) {
         return false;
       }
@@ -539,14 +543,14 @@
     }
 
     // @Override
-    public boolean visit(JProgram x) {
+    public boolean visit(JProgram x, Context ctx) {
       didRescue = false;
       return true;
     }
 
     // @Override
-    public boolean visit(JsniMethod x) {
-      return visit((JMethod) x);
+    public boolean visit(JsniMethod x, Context ctx) {
+      return visit((JMethod) x, ctx);
     }
   }
 
@@ -573,12 +577,10 @@
     boolean madeChanges = false;
     while (true) {
       RescueVisitor rescuer = new RescueVisitor();
-
       for (int i = 0; i < program.specialTypes.size(); ++i) {
         JReferenceType type = (JReferenceType) program.specialTypes.get(i);
         rescuer.rescue(type, true, noSpecialTypes);
       }
-
       for (int i = 0; i < program.entryMethods.size(); ++i) {
         JMethod method = (JMethod) program.entryMethods.get(i);
         rescuer.rescue(method);
@@ -587,17 +589,14 @@
       UpRefVisitor upRefer = new UpRefVisitor(rescuer);
       do {
         rescuer.commitInstantiatedTypes();
-        program.traverse(upRefer);
+        upRefer.accept(program);
       } while (upRefer.didRescue());
 
       PruneVisitor pruner = new PruneVisitor();
-      program.traverse(pruner);
-
-      ChangeList changes = pruner.getChangeList();
-      if (changes.empty()) {
+      pruner.accept(program);
+      if (!pruner.didChange()) {
         break;
       }
-      changes.apply();
 
       referencedTypes.clear();
       referencedNonTypes.clear();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
index e54a836..9c4b99d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
@@ -15,17 +15,15 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
-import com.google.gwt.dev.jjs.ast.Holder;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 
 /**
  * Replaces any "GWT.create()" calls with a new expression for the actual result
@@ -33,26 +31,22 @@
  */
 public class ReplaceRebinds {
 
-  private class RebindVisitor extends JVisitor {
-
-    private final ChangeList changeList = new ChangeList(
-        "Replace GWT.create() with new expressions.");
+  private class RebindVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator mutator) {
+    public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
       if (method == program.getRebindCreateMethod()) {
-        assert (x.args.size() == 1);
-        JExpression arg = x.args.getExpr(0);
+        assert (x.getArgs().size() == 1);
+        JExpression arg = (JExpression) x.getArgs().get(0);
         assert (arg instanceof JClassLiteral);
         JClassLiteral classLiteral = (JClassLiteral) arg;
-        JClassType classType = program.rebind(classLiteral.refType);
+        JClassType classType = program.rebind(classLiteral.getRefType());
 
         /*
          * Find the appropriate (noArg) constructor. In our AST, constructors
          * are instance methods that should be qualified with a new expression.
          */
-
         JMethod noArgCtor = null;
         for (int i = 0; i < classType.methods.size(); ++i) {
           JMethod ctor = (JMethod) classType.methods.get(i);
@@ -64,15 +58,13 @@
         }
         assert (noArgCtor != null);
         // Call it, using a new expression as a qualifier
-        JNewInstance newInstance = new JNewInstance(program, classType);
-        JMethodCall call = new JMethodCall(program, newInstance, noArgCtor);
-        changeList.replaceExpression(mutator, new Holder(call));
+        JNewInstance newInstance = new JNewInstance(program, x.getSourceInfo(),
+            classType);
+        JMethodCall call = new JMethodCall(program, x.getSourceInfo(),
+            newInstance, noArgCtor);
+        ctx.replaceMe(call);
       }
     }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
   }
 
   public static boolean exec(JProgram program) {
@@ -87,13 +79,8 @@
 
   private boolean execImpl() {
     RebindVisitor rebinder = new RebindVisitor();
-    program.traverse(rebinder);
-    ChangeList changes = rebinder.getChangeList();
-    if (changes.empty()) {
-      return false;
-    }
-    changes.apply();
-    return true;
+    rebinder.accept(program);
+    return rebinder.didChange();
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
index 4d07ac4..b61953a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs.impl;
 
 import com.google.gwt.dev.jjs.ast.CanBeFinal;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
@@ -50,25 +51,25 @@
   }
 
   // @Override
-  public boolean visit(JClassType x) {
+  public boolean visit(JClassType x, Context ctx) {
     // All classes are deemed "static" so the monolithic compile results can be
     // copy/pasted into a single enclosing class.
     print(CHARS_STATIC);
 
-    super.visit(x);
+    super.visit(x, ctx);
 
     openBlock();
 
     for (int i = 0; i < x.fields.size(); ++i) {
       JField it = (JField) x.fields.get(i);
-      it.traverse(this);
+      accept(it);
       newline();
       newline();
     }
     for (int i = 0; i < x.methods.size(); ++i) {
       JMethod it = (JMethod) x.methods.get(i);
       if (!isEmptyInitializer(it)) {
-        it.traverse(this);
+        accept(it);
         newline();
         newline();
       }
@@ -78,32 +79,32 @@
     return false;
   }
 
-  public boolean visit(JField x) {
-    super.visit(x);
+  public boolean visit(JField x, Context ctx) {
+    super.visit(x, ctx);
 
     if (x.constInitializer != null) {
       print(" = ");
-      x.constInitializer.traverse(this);
+      accept(x.constInitializer);
     }
     semi();
     return false;
   }
 
   // @Override
-  public boolean visit(JInterfaceType x) {
-    super.visit(x);
+  public boolean visit(JInterfaceType x, Context ctx) {
+    super.visit(x, ctx);
 
     openBlock();
 
     for (int i = 0; i < x.fields.size(); ++i) {
       JField field = (JField) x.fields.get(i);
-      field.traverse(this);
+      accept(field);
       newline();
       newline();
     }
     for (int i = 0; i < x.methods.size(); ++i) {
       JMethod method = (JMethod) x.methods.get(i);
-      method.traverse(this);
+      accept(method);
       newline();
       newline();
     }
@@ -113,21 +114,21 @@
   }
 
   // @Override
-  public boolean visit(JMethod x) {
+  public boolean visit(JMethod x, Context ctx) {
     // special: transcribe clinit and init as if they were initializer blocks
     if (isInitializer(x)) {
       if (x.isStatic()) {
         print(CHARS_STATIC);
       }
-      x.body.traverse(this);
+      accept(x.body);
     } else {
-      super.visit(x);
+      super.visit(x, ctx);
 
       if (x.isAbstract()) {
         semi();
       } else {
         space();
-        x.body.traverse(this);
+        accept(x.body);
       }
     }
 
@@ -135,16 +136,16 @@
   }
 
   // @Override
-  public boolean visit(JProgram x) {
+  public boolean visit(JProgram x, Context ctx) {
     for (int i = 0; i < x.entryMethods.size(); ++i) {
       JMethod method = (JMethod) x.entryMethods.get(i);
-      method.traverse(this);
+      accept(method);
       newline();
       newline();
     }
     for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
       JReferenceType type = (JReferenceType) x.getDeclaredTypes().get(i);
-      type.traverse(this);
+      accept(type);
       newline();
       newline();
     }
@@ -152,8 +153,8 @@
   }
 
   // @Override
-  public boolean visit(JsniMethod x) {
-    super.visit(x);
+  public boolean visit(JsniMethod x, Context ctx) {
+    super.visit(x, ctx);
     space();
     print(CHARS_SLASHSTAR);
     String jsniCode = x.getFunc().getBody().toString();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index dd3028d..ae9351e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -19,6 +19,7 @@
 import com.google.gwt.dev.jjs.ast.CanBeFinal;
 import com.google.gwt.dev.jjs.ast.CanBeNative;
 import com.google.gwt.dev.jjs.ast.CanBeStatic;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.HasType;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
@@ -77,7 +78,6 @@
 import com.google.gwt.dev.jjs.ast.JTryStatement;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JWhileStatement;
-import com.google.gwt.dev.jjs.ast.Mutator;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
@@ -147,70 +147,70 @@
   }
 
   // @Override
-  public boolean visit(JAbsentArrayDimension x, Mutator h) {
+  public boolean visit(JAbsentArrayDimension x, Context ctx) {
     // nothing to print, parent prints []
     return false;
   }
 
   // @Override
-  public boolean visit(JArrayRef x, Mutator h) {
+  public boolean visit(JArrayRef x, Context ctx) {
     JExpression instance = x.getInstance();
     parenPush(x, instance);
-    instance.traverse(this);
+    accept(instance);
     parenPop(x, instance);
     print('[');
-    x.getIndexExpr().traverse(this);
+    accept(x.getIndexExpr());
     print(']');
     return false;
   }
 
   // @Override
-  public boolean visit(JArrayType x) {
-    x.leafType.traverse(this);
-    for (int i = 0, c = x.dims; i < c; ++i) {
+  public boolean visit(JArrayType x, Context ctx) {
+    accept(x.getLeafType());
+    for (int i = 0, c = x.getDims(); i < c; ++i) {
       print("[]");
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JAssertStatement x) {
+  public boolean visit(JAssertStatement x, Context ctx) {
     print(CHARS_ASSERT);
-    x.getTestExpr().traverse(this);
+    accept(x.getTestExpr());
     if (x.getArg() != null) {
       print(" : ");
-      x.getArg().traverse(this);
+      accept(x.getArg());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JBinaryOperation x, Mutator h) {
+  public boolean visit(JBinaryOperation x, Context ctx) {
     // TODO(later): associativity
     JExpression arg1 = x.getLhs();
     parenPush(x, arg1);
-    arg1.traverse(this);
+    accept(arg1);
     parenPop(x, arg1);
 
     space();
-    print(x.op.getSymbol());
+    print(x.getOp().getSymbol());
     space();
 
     JExpression arg2 = x.getRhs();
     parenPush(x, arg2);
-    arg2.traverse(this);
+    accept(arg2);
     parenPop(x, arg2);
 
     return false;
   }
 
   // @Override
-  public boolean visit(JBlock x) {
+  public boolean visit(JBlock x, Context ctx) {
     openBlock();
     for (int i = 0; i < x.statements.size(); ++i) {
       JStatement statement = (JStatement) x.statements.get(i);
       needSemi = true;
-      statement.traverse(this);
+      accept(statement);
       if (needSemi) {
         semi();
       }
@@ -222,26 +222,26 @@
   }
 
   // @Override
-  public boolean visit(JBooleanLiteral x, Mutator h) {
-    printBooleanLiteral(x.value);
+  public boolean visit(JBooleanLiteral x, Context ctx) {
+    printBooleanLiteral(x.getValue());
     return false;
   }
 
   // @Override
-  public boolean visit(JBreakStatement x) {
+  public boolean visit(JBreakStatement x, Context ctx) {
     print(CHARS_BREAK);
-    if (x.label != null) {
+    if (x.getLabel() != null) {
       space();
-      x.label.traverse(this);
+      accept(x.getLabel());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JCaseStatement x) {
-    if (x.getExpression() != null) {
+  public boolean visit(JCaseStatement x, Context ctx) {
+    if (x.getExpr() != null) {
       print(CHARS_CASE);
-      x.getExpression().traverse(this);
+      accept(x.getExpr());
     } else {
       print(CHARS_DEFAULT);
     }
@@ -252,34 +252,34 @@
   }
 
   // @Override
-  public boolean visit(JCastOperation x, Mutator h) {
+  public boolean visit(JCastOperation x, Context ctx) {
     lparen();
     printType(x);
     rparen();
     space();
 
-    JExpression expr = x.getExpression();
+    JExpression expr = x.getExpr();
     parenPush(x, expr);
-    expr.traverse(this);
+    accept(expr);
     parenPop(x, expr);
     return false;
   }
 
   // @Override
-  public boolean visit(JCharLiteral x, Mutator h) {
-    printCharLiteral(x.value);
+  public boolean visit(JCharLiteral x, Context ctx) {
+    printCharLiteral(x.getValue());
     return false;
   }
 
   // @Override
-  public boolean visit(JClassLiteral x, Mutator h) {
-    printTypeName(x.refType);
+  public boolean visit(JClassLiteral x, Context ctx) {
+    printTypeName(x.getRefType());
     print(CHARS_DOTCLASS);
     return false;
   }
 
   // @Override
-  public boolean visit(JClassType x) {
+  public boolean visit(JClassType x, Context ctx) {
     printAbstractFlag(x);
     printFinalFlag(x);
     print(CHARS_CLASS);
@@ -306,47 +306,47 @@
   }
 
   // @Override
-  public boolean visit(JConditional x, Mutator h) {
+  public boolean visit(JConditional x, Context ctx) {
     // TODO(later): associativity
     JExpression ifTest = x.getIfTest();
     parenPush(x, ifTest);
-    ifTest.traverse(this);
+    accept(ifTest);
     parenPop(x, ifTest);
 
     print(" ? ");
 
     JExpression thenExpr = x.getThenExpr();
     parenPush(x, thenExpr);
-    thenExpr.traverse(this);
+    accept(thenExpr);
     parenPop(x, thenExpr);
 
     print(" : ");
 
     JExpression elseExpr = x.getElseExpr();
     parenPush(x, elseExpr);
-    elseExpr.traverse(this);
+    accept(elseExpr);
     parenPop(x, elseExpr);
 
     return false;
   }
 
   // @Override
-  public boolean visit(JContinueStatement x) {
+  public boolean visit(JContinueStatement x, Context ctx) {
     print(CHARS_CONTINUE);
-    if (x.label != null) {
+    if (x.getLabel() != null) {
       space();
-      x.label.traverse(this);
+      accept(x.getLabel());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JDoStatement x) {
+  public boolean visit(JDoStatement x, Context ctx) {
     print(CHARS_DO);
-    if (x.body != null) {
-      nestedStatementPush(x.body);
-      x.body.traverse(this);
-      nestedStatementPop(x.body);
+    if (x.getBody() != null) {
+      nestedStatementPush(x.getBody());
+      accept(x.getBody());
+      nestedStatementPop(x.getBody());
     }
     if (needSemi) {
       semi();
@@ -357,25 +357,25 @@
     }
     print(CHARS_WHILE);
     lparen();
-    x.getTestExpr().traverse(this);
+    accept(x.getTestExpr());
     rparen();
     return false;
   }
 
   // @Override
-  public boolean visit(JDoubleLiteral x, Mutator h) {
-    printDoubleLiteral(x.value);
+  public boolean visit(JDoubleLiteral x, Context ctx) {
+    printDoubleLiteral(x.getValue());
     return false;
   }
 
   // @Override
-  public boolean visit(JExpressionStatement x) {
-    x.getExpression().traverse(this);
+  public boolean visit(JExpressionStatement x, Context ctx) {
+    accept(x.getExpr());
     return false;
   }
 
   // @Override
-  public boolean visit(JField x) {
+  public boolean visit(JField x, Context ctx) {
     // Due to our wacky construction model, only constant fields may be final
     // when generating source
     if (x.constInitializer != null) {
@@ -392,48 +392,48 @@
   }
 
   // @Override
-  public boolean visit(JFieldRef x, Mutator h) {
+  public boolean visit(JFieldRef x, Context ctx) {
     JExpression instance = x.getInstance();
     if (instance != null) {
       parenPush(x, instance);
-      instance.traverse(this);
+      accept(instance);
       parenPop(x, instance);
     } else {
-      printTypeName(x.field.enclosingType);
+      printTypeName(x.getField().getEnclosingType());
     }
     print('.');
-    printUniqueName(x.field);
+    printUniqueName(x.getField());
     return false;
   }
 
   // @Override
-  public boolean visit(JFloatLiteral x, Mutator h) {
-    printFloatLiteral(x.value);
+  public boolean visit(JFloatLiteral x, Context ctx) {
+    printFloatLiteral(x.getValue());
     return false;
   }
 
   // @Override
-  public boolean visit(JForStatement x) {
+  public boolean visit(JForStatement x, Context ctx) {
     print(CHARS_FOR);
     lparen();
 
     Iterator/* <JStatement> */iter = x.getInitializers().iterator();
     if (iter.hasNext()) {
       JStatement stmt = (JStatement) iter.next();
-      stmt.traverse(this);
+      accept(stmt);
     }
     suppressType = true;
     while (iter.hasNext()) {
       print(CHARS_COMMA);
       JStatement stmt = (JStatement) iter.next();
-      stmt.traverse(this);
+      accept(stmt);
     }
     suppressType = false;
 
     semi();
     space();
     if (x.getTestExpr() != null) {
-      x.getTestExpr().traverse(this);
+      accept(x.getTestExpr());
     }
 
     semi();
@@ -441,28 +441,28 @@
     visitCollectionWithCommas(x.getIncrements().iterator());
     rparen();
 
-    if (x.body != null) {
-      nestedStatementPush(x.body);
-      x.body.traverse(this);
-      nestedStatementPop(x.body);
+    if (x.getBody() != null) {
+      nestedStatementPush(x.getBody());
+      accept(x.getBody());
+      nestedStatementPop(x.getBody());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JIfStatement x) {
+  public boolean visit(JIfStatement x, Context ctx) {
     print(CHARS_IF);
     lparen();
-    x.getIfExpr().traverse(this);
+    accept(x.getIfExpr());
     rparen();
 
-    if (x.thenStmt != null) {
-      nestedStatementPush(x.thenStmt);
-      x.thenStmt.traverse(this);
-      nestedStatementPop(x.thenStmt);
+    if (x.getThenStmt() != null) {
+      nestedStatementPush(x.getThenStmt());
+      accept(x.getThenStmt());
+      nestedStatementPop(x.getThenStmt());
     }
 
-    if (x.elseStmt != null) {
+    if (x.getElseStmt() != null) {
       if (needSemi) {
         semi();
         newline();
@@ -471,15 +471,15 @@
         needSemi = true;
       }
       print(CHARS_ELSE);
-      boolean elseIf = x.elseStmt instanceof JIfStatement;
+      boolean elseIf = x.getElseStmt() instanceof JIfStatement;
       if (!elseIf) {
-        nestedStatementPush(x.elseStmt);
+        nestedStatementPush(x.getElseStmt());
       } else {
         space();
       }
-      x.elseStmt.traverse(this);
+      accept(x.getElseStmt());
       if (!elseIf) {
-        nestedStatementPop(x.elseStmt);
+        nestedStatementPop(x.getElseStmt());
       }
     }
 
@@ -487,18 +487,18 @@
   }
 
   // @Override
-  public boolean visit(JInstanceOf x, Mutator h) {
-    JExpression expr = x.getExpression();
+  public boolean visit(JInstanceOf x, Context ctx) {
+    JExpression expr = x.getExpr();
     parenPush(x, expr);
-    expr.traverse(this);
+    accept(expr);
     parenPop(x, expr);
     print(CHARS_INSTANCEOF);
-    printTypeName(x.testType);
+    printTypeName(x.getTestType());
     return false;
   }
 
   // @Override
-  public boolean visit(JInterfaceType x) {
+  public boolean visit(JInterfaceType x, Context ctx) {
     print(CHARS_INTERFACE);
     printTypeName(x);
     space();
@@ -518,27 +518,27 @@
   }
 
   // @Override
-  public boolean visit(JIntLiteral x, Mutator h) {
-    print(Integer.toString(x.value).toCharArray());
+  public boolean visit(JIntLiteral x, Context ctx) {
+    print(Integer.toString(x.getValue()).toCharArray());
     return false;
   }
 
   // @Override
-  public boolean visit(JLabel x) {
+  public boolean visit(JLabel x, Context ctx) {
     printName(x);
     return false;
   }
 
   // @Override
-  public boolean visit(JLabeledStatement x) {
-    x.label.traverse(this);
+  public boolean visit(JLabeledStatement x, Context ctx) {
+    accept(x.getLabel());
     print(" : ");
-    x.body.traverse(this);
+    accept(x.getBody());
     return false;
   }
 
   // @Override
-  public boolean visit(JLocal x) {
+  public boolean visit(JLocal x, Context ctx) {
     printFinalFlag(x);
     printType(x);
     space();
@@ -547,44 +547,44 @@
   }
 
   // @Override
-  public boolean visit(JLocalDeclarationStatement x) {
+  public boolean visit(JLocalDeclarationStatement x, Context ctx) {
     if (!suppressType) {
-      x.getLocalRef().getTarget().traverse(this);
+      accept(x.getLocalRef().getTarget());
     } else {
-      x.getLocalRef().traverse(this);
+      accept(x.getLocalRef());
     }
     JExpression initializer = x.getInitializer();
     if (initializer != null) {
       print(" = ");
-      initializer.traverse(this);
+      accept(initializer);
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JLocalRef x, Mutator h) {
-    printName(x.local);
+  public boolean visit(JLocalRef x, Context ctx) {
+    printName(x.getLocal());
     return false;
   }
 
   // @Override
-  public boolean visit(JLongLiteral x, Mutator h) {
-    printLongLiteral(x.value);
+  public boolean visit(JLongLiteral x, Context ctx) {
+    printLongLiteral(x.getValue());
     return false;
   }
 
   // @Override
-  public boolean visit(JMethod x) {
+  public boolean visit(JMethod x, Context ctx) {
     return printMethodHeader(x);
   }
 
   // @Override
-  public boolean visit(JMethodCall x, Mutator h) {
+  public boolean visit(JMethodCall x, Context ctx) {
     JExpression instance = x.getInstance();
     JMethod target = x.getTarget();
     if (instance != null) {
       parenPush(x, instance);
-      instance.traverse(this);
+      accept(instance);
       parenPop(x, instance);
     } else {
       printTypeName(target.getEnclosingType());
@@ -596,13 +596,13 @@
       printName(target);
     }
     lparen();
-    visitCollectionWithCommas(x.args.iterator());
+    visitCollectionWithCommas(x.getArgs().iterator());
     rparen();
     return false;
   }
 
   // @Override
-  public boolean visit(JMultiExpression x, Mutator m) {
+  public boolean visit(JMultiExpression x, Context ctx) {
     lparen();
     visitCollectionWithCommas(x.exprs.iterator());
     rparen();
@@ -610,18 +610,18 @@
   }
 
   // @Override
-  public boolean visit(JNewArray x, Mutator h) {
+  public boolean visit(JNewArray x, Context ctx) {
     print(CHARS_NEW);
-    printTypeName(x.getArrayType().leafType);
+    printTypeName(x.getArrayType().getLeafType());
     if (x.initializers != null) {
       print('{');
       visitCollectionWithCommas(x.initializers.iterator());
       print('}');
     } else {
       for (int i = 0; i < x.dims.size(); ++i) {
-        JExpression expr = x.dims.getExpr(i);
+        JExpression expr = (JExpression) x.dims.get(i);
         print('[');
-        expr.traverse(this);
+        accept(expr);
         print(']');
       }
     }
@@ -629,7 +629,7 @@
   }
 
   // @Override
-  public boolean visit(JNewInstance x, Mutator h) {
+  public boolean visit(JNewInstance x, Context ctx) {
     print(CHARS_NEW);
     printType(x);
     lparen();
@@ -638,19 +638,19 @@
   }
 
   // @Override
-  public boolean visit(JNullLiteral x, Mutator h) {
+  public boolean visit(JNullLiteral x, Context ctx) {
     print(CHARS_NULL);
     return false;
   }
 
   // @Override
-  public boolean visit(JNullType x) {
+  public boolean visit(JNullType x, Context ctx) {
     printTypeName(x);
     return false;
   }
 
   // @Override
-  public boolean visit(JParameter x) {
+  public boolean visit(JParameter x, Context ctx) {
     printType(x);
     space();
     printName(x);
@@ -658,72 +658,72 @@
   }
 
   // @Override
-  public boolean visit(JParameterRef x, Mutator h) {
+  public boolean visit(JParameterRef x, Context ctx) {
     printName(x.getTarget());
     return false;
   }
 
   // @Override
-  public boolean visit(JPostfixOperation x, Mutator h) {
+  public boolean visit(JPostfixOperation x, Context ctx) {
     // TODO(later): associativity
     JExpression arg = x.getArg();
     parenPush(x, arg);
-    arg.traverse(this);
+    accept(arg);
     parenPop(x, arg);
-    print(x.op.getSymbol());
+    print(x.getOp().getSymbol());
     return false;
   }
 
   // @Override
-  public boolean visit(JPrefixOperation x, Mutator h) {
+  public boolean visit(JPrefixOperation x, Context ctx) {
     // TODO(later): associativity
-    print(x.op.getSymbol());
+    print(x.getOp().getSymbol());
     JExpression arg = x.getArg();
     parenPush(x, arg);
-    arg.traverse(this);
+    accept(arg);
     parenPop(x, arg);
     return false;
   }
 
   // @Override
-  public boolean visit(JPrimitiveType x) {
+  public boolean visit(JPrimitiveType x, Context ctx) {
     printTypeName(x);
     return false;
   }
 
   // @Override
-  public boolean visit(JProgram x) {
+  public boolean visit(JProgram x, Context ctx) {
     print("<JProgram>");
     return false;
   }
 
   // @Override
-  public boolean visit(JReturnStatement x) {
+  public boolean visit(JReturnStatement x, Context ctx) {
     print(CHARS_RETURN);
-    if (x.getExpression() != null) {
+    if (x.getExpr() != null) {
       space();
-      x.getExpression().traverse(this);
+      accept(x.getExpr());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JsniFieldRef x) {
-    return visit(x.getField());
+  public boolean visit(JsniFieldRef x, Context ctx) {
+    return visit(x.getField(), ctx);
   }
 
   // @Override
-  public boolean visit(JsniMethod x) {
+  public boolean visit(JsniMethod x, Context ctx) {
     return printMethodHeader(x);
   }
 
   // @Override
-  public boolean visit(JsniMethodRef x) {
+  public boolean visit(JsniMethodRef x, Context ctx) {
     return printMethodHeader(x.getTarget());
   }
 
   // @Override
-  public boolean visit(JsonArray x, Mutator m) {
+  public boolean visit(JsonArray x, Context ctx) {
     print('[');
     visitCollectionWithCommas(x.exprs.iterator());
     print(']');
@@ -731,7 +731,7 @@
   }
 
   // @Override
-  public boolean visit(JsonObject x, Mutator m) {
+  public boolean visit(JsonObject x, Context ctx) {
     print('{');
     visitCollectionWithCommas(x.propInits.iterator());
     print('}');
@@ -739,79 +739,79 @@
   }
 
   // @Override
-  public boolean visit(JsonPropInit x) {
-    x.labelExpr.traverse(this);
+  public boolean visit(JsonPropInit x, Context ctx) {
+    accept(x.labelExpr);
     print(':');
-    x.valueExpr.traverse(this);
+    accept(x.valueExpr);
     return false;
   }
 
   // @Override
-  public boolean visit(JStringLiteral x, Mutator h) {
-    printStringLiteral(x.value);
+  public boolean visit(JStringLiteral x, Context ctx) {
+    printStringLiteral(x.getValue());
     return false;
   }
 
   // @Override
-  public boolean visit(JSwitchStatement x) {
+  public boolean visit(JSwitchStatement x, Context ctx) {
     print(CHARS_SWITCH);
     lparen();
-    x.getExpression().traverse(this);
+    accept(x.getExpr());
     rparen();
     space();
-    nestedStatementPush(x.body);
-    x.body.traverse(this);
-    nestedStatementPop(x.body);
+    nestedStatementPush(x.getBody());
+    accept(x.getBody());
+    nestedStatementPop(x.getBody());
     return false;
   }
 
   // @Override
-  public boolean visit(JThisRef x, Mutator h) {
+  public boolean visit(JThisRef x, Context ctx) {
     print(CHARS_THIS);
     return false;
   }
 
   // @Override
-  public boolean visit(JThrowStatement x) {
+  public boolean visit(JThrowStatement x, Context ctx) {
     print(CHARS_THROW);
-    if (x.getExpression() != null) {
+    if (x.getExpr() != null) {
       space();
-      x.getExpression().traverse(this);
+      accept(x.getExpr());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JTryStatement x) {
+  public boolean visit(JTryStatement x, Context ctx) {
     print(CHARS_TRY);
-    x.tryBlock.traverse(this);
-    for (int i = 0, c = x.catchArgs.size(); i < c; ++i) {
+    accept(x.getTryBlock());
+    for (int i = 0, c = x.getCatchArgs().size(); i < c; ++i) {
       print(CHARS_CATCH);
       lparen();
-      JLocalRef localRef = (JLocalRef) x.catchArgs.get(i);
-      localRef.getTarget().traverse(this);
+      JLocalRef localRef = (JLocalRef) x.getCatchArgs().get(i);
+      accept(localRef.getTarget());
       rparen();
       space();
-      JBlock block = (JBlock) x.catchBlocks.get(i);
-      block.traverse(this);
+      JBlock block = (JBlock) x.getCatchBlocks().get(i);
+      accept(block);
     }
-    if (x.finallyBlock != null) {
+    if (x.getFinallyBlock() != null) {
       print(CHARS_FINALLY);
-      x.finallyBlock.traverse(this);
+      accept(x.getFinallyBlock());
     }
     return false;
   }
 
   // @Override
-  public boolean visit(JWhileStatement x) {
+  public boolean visit(JWhileStatement x, Context ctx) {
     print(CHARS_WHILE);
     lparen();
-    x.getTestExpr().traverse(this);
+    accept(x.getTestExpr());
     rparen();
-    if (x.body != null) {
-      nestedStatementPush(x.body);
-      x.body.traverse(this);
-      nestedStatementPop(x.body);
+    if (x.getBody() != null) {
+      nestedStatementPush(x.getBody());
+      accept(x.getBody());
+      nestedStatementPop(x.getBody());
     }
     return false;
   }
@@ -1061,12 +1061,12 @@
   protected void visitCollectionWithCommas(Iterator/* <? extends JNode> */iter) {
     if (iter.hasNext()) {
       JNode node = (JNode) iter.next();
-      node.traverse(this);
+      accept(node);
     }
     while (iter.hasNext()) {
       print(CHARS_COMMA);
       JNode node = (JNode) iter.next();
-      node.traverse(this);
+      accept(node);
     }
   }
 
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 8136f94..9ecd657 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
@@ -28,6 +29,7 @@
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNewArray;
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JParameter;
@@ -40,8 +42,6 @@
 import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVariableRef;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.Mutator;
-import com.google.gwt.dev.jjs.ast.change.ChangeList;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
@@ -89,45 +89,45 @@
   /**
    * Replaces dangling null references with dummy calls.
    */
-  public class FixDanglingRefsVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Replace dangling null references with dummy calls.");
+  public class FixDanglingRefsVisitor extends JModVisitor {
 
     // @Override
-    public void endVisit(JArrayRef x, Mutator m) {
+    public void endVisit(JArrayRef x, Context ctx) {
       JExpression instance = x.getInstance();
       if (instance.getType() == typeNull) {
         if (!instance.hasSideEffects()) {
           instance = program.getLiteralNull();
         }
-        JArrayRef arrayRef = new JArrayRef(program, instance,
-            program.getLiteralInt(0));
-        changeList.replaceExpression(m, arrayRef);
+        JArrayRef arrayRef = new JArrayRef(program, x.getSourceInfo(),
+            instance, program.getLiteralInt(0));
+        ctx.replaceMe(arrayRef);
       }
     }
 
     // @Override
-    public void endVisit(JFieldRef x, Mutator m) {
+    public void endVisit(JFieldRef x, Context ctx) {
       JExpression instance = x.getInstance();
       boolean isStatic = x.getField().isStatic();
       if (isStatic && instance != null) {
         // this doesn't really belong here, but while we're here let's remove
         // non-side-effect qualifiers to statics
         if (!instance.hasSideEffects()) {
-          changeList.replaceExpression(x.instance, (JExpression) null);
+          JFieldRef fieldRef = new JFieldRef(program, x.getSourceInfo(), null,
+              x.getField(), x.getEnclosingType());
+          ctx.replaceMe(fieldRef);
         }
       } else if (!isStatic && instance.getType() == typeNull) {
         if (!instance.hasSideEffects()) {
           instance = program.getLiteralNull();
         }
-        JFieldRef fieldRef = new JFieldRef(program, instance,
-            program.getNullField(), null);
-        changeList.replaceExpression(m, fieldRef);
+        JFieldRef fieldRef = new JFieldRef(program, x.getSourceInfo(),
+            instance, program.getNullField(), null);
+        ctx.replaceMe(fieldRef);
       }
     }
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
+    public void endVisit(JMethodCall x, Context ctx) {
       JExpression instance = x.getInstance();
       JMethod method = x.getTarget();
       boolean isStatic = method.isStatic();
@@ -136,30 +136,31 @@
         // this doesn't really belong here, but while we're here let's remove
         // non-side-effect qualifiers to statics
         if (!instance.hasSideEffects()) {
-          changeList.replaceExpression(x.instance, (JExpression) null);
+          JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
+              null, x.getTarget());
+          newCall.getArgs().addAll(x.getArgs());
+          ctx.replaceMe(newCall);
         }
       } else if (!isStatic && instance.getType() == typeNull) {
+        // bind null instance calls to the null method
         if (!instance.hasSideEffects()) {
           instance = program.getLiteralNull();
         }
-        JMethodCall call = new JMethodCall(program, instance,
-            program.getNullMethod());
-        changeList.replaceExpression(m, call);
-      } else if (isStaticImpl && x.args.size() > 0
-          && x.args.getExpr(0).getType() == typeNull) {
-        instance = x.args.getExpr(0);
+        JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
+            instance, program.getNullMethod());
+        ctx.replaceMe(newCall);
+      } else if (isStaticImpl && x.getArgs().size() > 0
+          && ((JExpression) x.getArgs().get(0)).getType() == typeNull) {
+        // bind null instance calls to the null method for static impls
+        instance = (JExpression) x.getArgs().get(0);
         if (!instance.hasSideEffects()) {
           instance = program.getLiteralNull();
         }
-        JMethodCall call = new JMethodCall(program, instance,
-            program.getNullMethod());
-        changeList.replaceExpression(m, call);
+        JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
+            instance, program.getNullMethod());
+        ctx.replaceMe(newCall);
       }
     }
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
   }
   /**
    * Record "type flow" information. Variables receive type flow via assignment.
@@ -180,7 +181,7 @@
     private JMethod currentMethod;
 
     // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
+    public void endVisit(JBinaryOperation x, Context ctx) {
       if (x.isAssignment() && (x.getType() instanceof JReferenceType)) {
         JExpression lhs = x.getLhs();
         if (lhs instanceof JVariableRef) {
@@ -190,7 +191,7 @@
     }
 
     // @Override
-    public void endVisit(JClassType x) {
+    public void endVisit(JClassType x, Context ctx) {
       for (JClassType cur = x; cur != null; cur = cur.extnds) {
         addImplementor(cur, x);
         for (Iterator it = cur.implments.iterator(); it.hasNext();) {
@@ -201,7 +202,7 @@
     }
 
     // @Override
-    public void endVisit(JField x) {
+    public void endVisit(JField x, Context ctx) {
       if (x.constInitializer != null) {
         addAssignment(x, x.constInitializer);
       }
@@ -209,7 +210,7 @@
     }
 
     // @Override
-    public void endVisit(JLocalDeclarationStatement x) {
+    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
       JExpression initializer = x.getInitializer();
       if (initializer != null) {
         addAssignment(x.getLocalRef().getTarget(), initializer);
@@ -217,7 +218,7 @@
     }
 
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
       for (int i = 0; i < x.overrides.size(); ++i) {
         JMethod method = (JMethod) x.overrides.get(i);
         addOverrider(method, x);
@@ -231,10 +232,10 @@
     }
 
     // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
+    public void endVisit(JMethodCall x, Context ctx) {
       // All of the params in the target method are considered to be assigned by
       // the arguments from the caller
-      Iterator/* <JExpression> */argIt = x.args.iterator();
+      Iterator/* <JExpression> */argIt = x.getArgs().iterator();
       ArrayList params = x.getTarget().params;
       for (int i = 0; i < params.size(); ++i) {
         JParameter param = (JParameter) params.get(i);
@@ -246,26 +247,26 @@
     }
 
     // @Override
-    public void endVisit(JReturnStatement x) {
+    public void endVisit(JReturnStatement x, Context ctx) {
       if (currentMethod.getType() instanceof JReferenceType) {
-        addReturn(currentMethod, x.getExpression());
+        addReturn(currentMethod, x.getExpr());
       }
     }
 
     // @Override
-    public void endVisit(JsniFieldRef x) {
+    public void endVisit(JsniFieldRef x, Context ctx) {
       // If this happens in JSNI, we can't make any type-tightening assumptions
       // Fake an assignment-to-self to prevent tightening
       addAssignment(x.getTarget(), x);
     }
 
     // @Override
-    public void endVisit(JsniMethod x) {
-      endVisit((JMethod) x);
+    public void endVisit(JsniMethod x, Context ctx) {
+      endVisit((JMethod) x, ctx);
     }
 
     // @Override
-    public void endVisit(JsniMethodRef x) {
+    public void endVisit(JsniMethodRef x, Context ctx) {
       // If this happens in JSNI, we can't make any type-tightening assumptions
       // Fake an assignment-to-self on all args to prevent tightening
 
@@ -273,16 +274,16 @@
 
       for (int i = 0; i < method.params.size(); ++i) {
         JParameter param = (JParameter) method.params.get(i);
-        addAssignment(param, new JParameterRef(program, param));
+        addAssignment(param, new JParameterRef(program, null, param));
       }
     }
 
     // @Override
-    public void endVisit(JTryStatement x) {
+    public void endVisit(JTryStatement x, Context ctx) {
       // Never tighten args to catch blocks
       // Fake an assignment-to-self to prevent tightening
-      for (int i = 0; i < x.catchArgs.size(); ++i) {
-        JLocalRef arg = (JLocalRef) x.catchArgs.get(i);
+      for (int i = 0; i < x.getCatchArgs().size(); ++i) {
+        JLocalRef arg = (JLocalRef) x.getCatchArgs().get(i);
         addAssignment(arg.getTarget(), arg);
       }
     }
@@ -292,7 +293,7 @@
      * param type in an overriding method if the declaring method is looser.
      */
     // @Override
-    public boolean visit(JMethod x) {
+    public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
 
       List/* <JMethod> */overrides = x.overrides;
@@ -374,16 +375,20 @@
    * on it.
    */
   public class TightenTypesVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-        "Tighten types on fields, locals, params, methods.");
+
+    private boolean didChange = false;
+
+    public boolean didChange() {
+      return didChange;
+    }
 
     // @Override
-    public void endVisit(JField x) {
+    public void endVisit(JField x, Context ctx) {
       tighten(x);
     }
 
     // @Override
-    public void endVisit(JLocal x) {
+    public void endVisit(JLocal x, Context ctx) {
       tighten(x);
     }
 
@@ -391,7 +396,7 @@
      * Tighten based on return types and overrides.
      */
     // @Override
-    public void endVisit(JMethod x) {
+    public void endVisit(JMethod x, Context ctx) {
 
       if (!(x.getType() instanceof JReferenceType)) {
         return;
@@ -404,7 +409,8 @@
 
       // tighten based on non-instantiability
       if (!program.typeOracle.isInstantiatedType(refType)) {
-        changeList.changeType(x, typeNull);
+        x.setType(typeNull);
+        didChange = true;
         return;
       }
 
@@ -436,34 +442,32 @@
       JReferenceType resultType = program.generalizeTypes(typeList);
       resultType = program.strongerType(refType, resultType);
       if (refType != resultType) {
-        changeList.changeType(x, resultType);
+        x.setType(resultType);
+        didChange = true;
       }
     }
 
     // @Override
-    public void endVisit(JNewArray x, Mutator m) {
+    public void endVisit(JNewArray x, Context ctx) {
       // tighten leaf type based on non-instantiability
       JArrayType arrayType = x.getArrayType();
       JType leafType = arrayType.getLeafType();
       if (leafType instanceof JReferenceType) {
         if (!program.typeOracle.isInstantiatedType((JReferenceType) leafType)) {
           arrayType = program.getTypeArray(typeNull, arrayType.getDims());
-          changeList.changeType(x, arrayType);
+          x.setType(arrayType);
+          didChange = true;
         }
       }
     }
 
     // @Override
-    public void endVisit(JParameter x) {
+    public void endVisit(JParameter x, Context ctx) {
       tighten(x);
     }
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
     // @Override
-    public boolean visit(JClassType x) {
+    public boolean visit(JClassType x, Context ctx) {
       // don't mess with classes used in code gen
       if (program.specialTypes.contains(x)) {
         return false;
@@ -471,7 +475,7 @@
       return true;
     }
 
-    public boolean visit(JsniMethod x) {
+    public boolean visit(JsniMethod x, Context ctx) {
       /*
        * Explicitly NOT visiting native methods since we can't infer type
        * information.
@@ -496,7 +500,8 @@
 
       // tighten based on non-instantiability
       if (!program.typeOracle.isInstantiatedType(refType)) {
-        changeList.changeType(x, typeNull);
+        x.setType(typeNull);
+        didChange = true;
         return;
       }
 
@@ -545,7 +550,8 @@
       JReferenceType resultType = program.generalizeTypes(typeList);
       resultType = program.strongerType(refType, resultType);
       if (refType != resultType) {
-        changeList.changeType(x, resultType);
+        x.setType(resultType);
+        didChange = true;
       }
     }
   }
@@ -578,26 +584,21 @@
   }
 
   private boolean execImpl() {
-    boolean madeChanges = false;
     RecordVisitor recorder = new RecordVisitor();
-    program.traverse(recorder);
+    recorder.accept(program);
+    boolean madeChanges = false;
     while (true) {
       TightenTypesVisitor tightener = new TightenTypesVisitor();
-      program.traverse(tightener);
-      ChangeList changes = tightener.getChangeList();
-      if (changes.empty()) {
-        return madeChanges;
+      tightener.accept(program);
+      if (!tightener.didChange()) {
+        break;
       }
-      changes.apply();
       madeChanges = true;
 
       FixDanglingRefsVisitor fixer = new FixDanglingRefsVisitor();
-      program.traverse(fixer);
-      changes = fixer.getChangeList();
-      if (!changes.empty()) {
-        changes.apply();
-      }
+      fixer.accept(program);
     }
+    return madeChanges;
   }
 
 }
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 8f20d8a..49580f9 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
@@ -25,6 +25,11 @@
  */
 public abstract class JsNode implements JsVisitable {
 
+  public JsSourceInfo getInfo() {
+    // TODO: make this real
+    return null;
+  }
+  
   // @Override
   public String toString() {
     // no obfuscation
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/change/Change.java b/dev/core/src/com/google/gwt/dev/js/ast/JsSourceInfo.java
similarity index 66%
rename from dev/core/src/com/google/gwt/dev/jjs/ast/change/Change.java
rename to dev/core/src/com/google/gwt/dev/js/ast/JsSourceInfo.java
index ac79b3c..f159495 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/change/Change.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSourceInfo.java
@@ -13,18 +13,10 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.dev.jjs.ast.change;
-
-import com.google.gwt.core.ext.TreeLogger;
+package com.google.gwt.dev.js.ast;
 
 /**
- * Interface implemented by classes that apply changes to a set of statements.
+ * Tracks file and line information for AST nodes.
  */
-public interface Change {
-
-  void describe(TreeLogger logger, TreeLogger.Type type);
-
-  void apply();
-  // void undo();
-  // void verify();
+public class JsSourceInfo {
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index d4659e4..08a923b 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -15,47 +15,47 @@
   private static interface Fruit {
   }
 
-  private static class Granny extends Apple {
-  }
-
   private static class Fuji extends Apple {
   }
 
-  private static class SideEffectCauser {
-    private static Object instance = new Object();
-
-    public static Object causeClinitSideEffect() {
-      return instance;
-    }
-
-    static {
-      CompilerTest.sideEffectChecker++;
-    }
-  }
-
-  private static class SideEffectCauser2 {
-    public static Object causeClinitSideEffect() {
-      return null;
-    }
-
-    static {
-      CompilerTest.sideEffectChecker++;
-    }
+  private static class Granny extends Apple {
   }
 
   private static class NonSideEffectCauser {
     public static final String NOT_A_COMPILE_TIME_CONSTANT = null;
   }
 
+  private static class SideEffectCauser {
+    private static Object instance = new Object();
+
+    static {
+      CompilerTest.sideEffectChecker++;
+    }
+
+    public static Object causeClinitSideEffect() {
+      return instance;
+    }
+  }
+
+  private static class SideEffectCauser2 {
+    static {
+      CompilerTest.sideEffectChecker++;
+    }
+
+    public static Object causeClinitSideEffect() {
+      return null;
+    }
+  }
+
   private static final class UninstantiableType {
+    public Object field;
+
     private UninstantiableType() {
     }
 
     public Object returnNull() {
       return null;
     }
-
-    public Object field;
   }
 
   private static int sideEffectChecker;
@@ -137,6 +137,39 @@
     assertEquals(null, checkRescued);
   }
 
+  public void testDeadCode() {
+    while (returnFalse()) {
+      break;
+    }
+
+    do {
+      break;
+    } while (false);
+
+    do {
+      break;
+    } while (returnFalse());
+
+    for (; returnFalse();) {
+    }
+
+    boolean check = false;
+    for (check = true; returnFalse(); fail()) {
+      fail();
+    }
+    assertTrue(check);
+
+    if (returnFalse()) {
+      fail();
+    } else {
+    }
+
+    if (!returnFalse()) {
+    } else {
+      fail();
+    }
+  }
+
   public void testDeadTypes() {
     if (false) {
       new Object() {
@@ -339,11 +372,23 @@
     final String bar = cannotOptimize() ? "bar" : "foo";
     String result = new Object() {
 
+      private String a = foo;
+
+      {
+        a = foo;
+      }
+
       public String toString() {
         return new Object() {
 
           private static final String constantString = "wallawalla";
 
+          private String ai = foo;
+
+          {
+            ai = foo;
+          }
+
           public String toString() {
             // this line used to cause ICE due to no synthetic path to bar
             bar.valueOf(false);
@@ -352,21 +397,9 @@
             return foo + a + ai;
           }
 
-          {
-            ai = foo;
-          }
-
-          private String ai = foo;
-
         }.toString() + a;
       }
 
-      {
-        a = foo;
-      }
-
-      private String a = foo;
-
     }.toString();
     assertEquals(result, "foofoofoofoo");
   }
@@ -429,20 +462,10 @@
     new B();
   }
 
-  public void testSwitchStatement() {
-    switch (0) {
-      case 0:
-        int test; // used to cause an ICE
-        break;
-    }
-  }
-
-  public void testSubclassStaticInnerAndClinitOrdering() {
-    new CheckSubclassStaticInnerAndClinitOrdering();
-  }
-
   public void testReturnStatementInCtor() {
     class Foo {
+      int i;
+
       Foo(int i) {
         this.i = i;
         if (i == 0)
@@ -451,20 +474,34 @@
           return;
         return;
       }
-
-      int i;
     }
     assertEquals(new Foo(0).i, 0);
     assertEquals(new Foo(1).i, 1);
     assertEquals(new Foo(2).i, 2);
   }
 
+  public void testSubclassStaticInnerAndClinitOrdering() {
+    new CheckSubclassStaticInnerAndClinitOrdering();
+  }
+
+  public void testSwitchStatement() {
+    switch (0) {
+      case 0:
+        int test; // used to cause an ICE
+        break;
+    }
+  }
+
   public void testUnaryPlus() {
     int x, y = -7;
     x = +y;
     assertEquals(-7, x);
   }
 
+  private boolean returnFalse() {
+    return false;
+  }
+
 }
 
 class A {
@@ -479,13 +516,11 @@
   }
 }
 
-class Outer {
-  public static class StaticInner {
-  }
-}
-
 // This construct used to cause an ICE
 class CheckSubclassStaticInnerAndClinitOrdering extends Outer.StaticInner {
+  private static class Foo {
+  }
+
   private static final Foo FOO = new Foo();
 
   public CheckSubclassStaticInnerAndClinitOrdering() {
@@ -496,7 +531,9 @@
     // This used to be null due to clinit ordering issues
     Assert.assertNotNull(foo);
   }
+}
 
-  private static class Foo {
+class Outer {
+  public static class StaticInner {
   }
 }